UP主小助手 使用说明
2020-05-12 本文已影响0人
请叫我雯子小姐的小爷
最新版软件已经发布
示例图如下,有兴趣的小伙伴可以查看这个视频:
软件介绍视频:https://www.bilibili.com/video/BV1Jp4y1C73Y/
软件介绍视频(快闪版): https://www.bilibili.com/video/BV11A41187Ye/
软件下载
V1.21版紧急发布:4月28日晚,B站视频接口做了调整,v1.2版本仅能显示最新10个视频的数据,v1.21版本现在已经发布,此次更新为增量更新,只需下载补丁复制到程序所在的文件夹中进行替换即可
正式版v1.2:
- 1、优化了评论管理模块的展示效果
- 2、增加了弹幕管理模块
- 3、增加了版本管理文件
正式版v1.1:
修复了v1.0版本中的一些bug
- 1、修复了自2020-04-10晚开始的评论接口数据异常而导致的软件频繁提示重启问题;
- 2、更改了软件重启机制,非主进程停止工作时不>会影响软件的运行;
半成品预览版:点击进入
程序源码
Github:https://github.com/MR5356/bilibili-up-helper
[图片上传失败...(image-9fc112-1589275555223)]
<span style="color:red">注意:以下为旧版应用信息,不适用上面的新版软件</span>
介绍视频:https://www.bilibili.com/video/BV1m7411o7k5/
软件下载
此软件为百度网盘分享
链接:https://pan.baidu.com/s/1ICc1D6mV0vELQrNANrnlqA
提取码:jssa
使用
首先,下载后将软件解压,并修改软件目录中的config.toml(可使用记事本打开修改):
[global]
refresh = 60 #刷新间隔(s)
[user]
cookies = true # 自动保存cookies,方便以后登录获取信息
account = """
username=你的账号;password=你的密码;
"""
然后双击运行软件即可,第一次登录会获取登录cookies并保存,可能较慢(3-5秒),此后运行将会优先使用cookies,打开就比较快了。
帮助与Bug 反馈
请扫描页面下方二维码,关注微信公众号后留言即可,我会在看到后第一时间给予回复。
支持此项目
由于本人实为理科生,艺术细胞不明显,导致界面并不符合各位的审美,如有好的UI方案或建议,可关注微信公众号后给我留言。万分感谢。
下面是源码(不要问我为什么不用GitHub~~~)
程序入口:main.py
import sys
import function
from PyQt5 import QtWidgets
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
ui = function.fun_main()
ui.show()
sys.exit(app.exec_())
软件UI:window.py
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'window.ui'
#
# Created by: PyQt5 UI code generator 5.13.2
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(400, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.widget_main = QtWidgets.QWidget(self.centralwidget)
self.widget_main.setAutoFillBackground(False)
self.widget_main.setStyleSheet("QWidget#widget_main{border-image:url(:/images/2233.png);border:1px solid blank;border-radius:10px;}")
self.widget_main.setObjectName("widget_main")
self.level = QtWidgets.QLabel(self.widget_main)
self.level.setGeometry(QtCore.QRect(170, 100, 81, 21))
self.level.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.level.setText("")
self.level.setObjectName("level")
self.name_9 = QtWidgets.QLabel(self.widget_main)
self.name_9.setGeometry(QtCore.QRect(260, 120, 61, 21))
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.name_9.setFont(font)
self.name_9.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.name_9.setObjectName("name_9")
self.pushButton_min = QtWidgets.QPushButton(self.widget_main)
self.pushButton_min.setGeometry(QtCore.QRect(330, 10, 21, 21))
self.pushButton_min.setStyleSheet("QPushButton{background:Transparent;border-radius:5px;border:0px solid grey;}QPushButton:hover{background:CornflowerBlue;}")
self.pushButton_min.setText("")
self.pushButton_min.setObjectName("pushButton_min")
self.name_4 = QtWidgets.QLabel(self.widget_main)
self.name_4.setGeometry(QtCore.QRect(110, 120, 51, 21))
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.name_4.setFont(font)
self.name_4.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.name_4.setObjectName("name_4")
self.pushButton_top = QtWidgets.QPushButton(self.widget_main)
self.pushButton_top.setGeometry(QtCore.QRect(310, 10, 21, 21))
self.pushButton_top.setStyleSheet("QPushButton{background:Transparent;border-radius:5px;border:0px solid grey;}QPushButton:hover{background:CornflowerBlue;}")
self.pushButton_top.setText("")
self.pushButton_top.setObjectName("pushButton_top")
self.name_2 = QtWidgets.QLabel(self.widget_main)
self.name_2.setGeometry(QtCore.QRect(260, 80, 41, 21))
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.name_2.setFont(font)
self.name_2.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.name_2.setObjectName("name_2")
self.nameaaaa = QtWidgets.QLabel(self.widget_main)
self.nameaaaa.setGeometry(QtCore.QRect(110, 40, 41, 21))
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.nameaaaa.setFont(font)
self.nameaaaa.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.nameaaaa.setObjectName("nameaaaa")
self.video_info = QtWidgets.QTableView(self.widget_main)
self.video_info.setGeometry(QtCore.QRect(10, 160, 361, 391))
self.video_info.setStyleSheet("QTableView{background:Transparent;border-radius:8px;border:0px solid grey;padding:2px 4px}QTbaleView:hover{background:GhostWhite;}QHeaderView::section{background:Transparent;}")
self.video_info.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.video_info.setShowGrid(False)
self.video_info.setObjectName("video_info")
self.sex = QtWidgets.QLabel(self.widget_main)
self.sex.setGeometry(QtCore.QRect(300, 80, 71, 21))
self.sex.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.sex.setText("")
self.sex.setObjectName("sex")
self.name = QtWidgets.QLabel(self.widget_main)
self.name.setGeometry(QtCore.QRect(150, 40, 221, 21))
self.name.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.name.setText("")
self.name.setObjectName("name")
self.likes = QtWidgets.QLabel(self.widget_main)
self.likes.setGeometry(QtCore.QRect(320, 120, 51, 21))
self.likes.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.likes.setText("")
self.likes.setObjectName("likes")
self.name_8 = QtWidgets.QLabel(self.widget_main)
self.name_8.setGeometry(QtCore.QRect(260, 140, 61, 21))
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.name_8.setFont(font)
self.name_8.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.name_8.setObjectName("name_8")
self.name_7 = QtWidgets.QLabel(self.widget_main)
self.name_7.setGeometry(QtCore.QRect(110, 100, 61, 21))
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.name_7.setFont(font)
self.name_7.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.name_7.setObjectName("name_7")
self.name_6 = QtWidgets.QLabel(self.widget_main)
self.name_6.setGeometry(QtCore.QRect(110, 140, 61, 21))
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.name_6.setFont(font)
self.name_6.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.name_6.setObjectName("name_6")
self.face = QtWidgets.QLabel(self.widget_main)
self.face.setGeometry(QtCore.QRect(10, 40, 91, 91))
self.face.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.face.setText("")
self.face.setAlignment(QtCore.Qt.AlignCenter)
self.face.setObjectName("face")
self.sign = QtWidgets.QLabel(self.widget_main)
self.sign.setGeometry(QtCore.QRect(150, 60, 221, 21))
self.sign.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.sign.setText("")
self.sign.setObjectName("sign")
self.name_3 = QtWidgets.QLabel(self.widget_main)
self.name_3.setGeometry(QtCore.QRect(110, 60, 41, 21))
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.name_3.setFont(font)
self.name_3.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.name_3.setObjectName("name_3")
self.follower = QtWidgets.QLabel(self.widget_main)
self.follower.setGeometry(QtCore.QRect(160, 120, 71, 21))
self.follower.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.follower.setText("")
self.follower.setObjectName("follower")
self.pushButton_close = QtWidgets.QPushButton(self.widget_main)
self.pushButton_close.setGeometry(QtCore.QRect(350, 10, 21, 21))
self.pushButton_close.setStyleSheet("QPushButton{background:Transparent;border-radius:5px;border:0px solid grey;}QPushButton:hover{background:CornflowerBlue;}")
self.pushButton_close.setText("")
self.pushButton_close.setObjectName("pushButton_close")
self.arc_v = QtWidgets.QLabel(self.widget_main)
self.arc_v.setGeometry(QtCore.QRect(170, 140, 81, 21))
self.arc_v.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.arc_v.setText("")
self.arc_v.setObjectName("arc_v")
self.art_v = QtWidgets.QLabel(self.widget_main)
self.art_v.setGeometry(QtCore.QRect(320, 140, 51, 21))
self.art_v.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.art_v.setText("")
self.art_v.setObjectName("art_v")
self.pushButton_help = QtWidgets.QPushButton(self.widget_main)
self.pushButton_help.setGeometry(QtCore.QRect(250, 10, 41, 23))
self.pushButton_help.setStyleSheet("QPushButton{background:Transparent;border-radius:5px;border:0px solid grey;}QPushButton:hover{background:CornflowerBlue;}")
self.pushButton_help.setObjectName("pushButton_help")
self.count = QtWidgets.QLabel(self.widget_main)
self.count.setGeometry(QtCore.QRect(10, 10, 181, 21))
font = QtGui.QFont()
font.setBold(False)
font.setWeight(50)
self.count.setFont(font)
self.count.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.count.setText("")
self.count.setObjectName("count")
self.nameaaaa_2 = QtWidgets.QLabel(self.widget_main)
self.nameaaaa_2.setGeometry(QtCore.QRect(260, 100, 61, 21))
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.nameaaaa_2.setFont(font)
self.nameaaaa_2.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.nameaaaa_2.setObjectName("nameaaaa_2")
self.ban = QtWidgets.QLabel(self.widget_main)
self.ban.setGeometry(QtCore.QRect(320, 100, 51, 21))
self.ban.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.ban.setText("")
self.ban.setObjectName("ban")
self.name_10 = QtWidgets.QLabel(self.widget_main)
self.name_10.setGeometry(QtCore.QRect(10, 140, 41, 21))
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.name_10.setFont(font)
self.name_10.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.name_10.setObjectName("name_10")
self.name_11 = QtWidgets.QLabel(self.widget_main)
self.name_11.setGeometry(QtCore.QRect(110, 80, 31, 21))
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
self.name_11.setFont(font)
self.name_11.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.name_11.setObjectName("name_11")
self.coins = QtWidgets.QLabel(self.widget_main)
self.coins.setGeometry(QtCore.QRect(50, 140, 61, 21))
self.coins.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.coins.setText("")
self.coins.setObjectName("coins")
self.birthday = QtWidgets.QLabel(self.widget_main)
self.birthday.setGeometry(QtCore.QRect(150, 80, 91, 21))
self.birthday.setStyleSheet("QLabel{background:Transparent;border-radius:5px;border:0px solid grey;}")
self.birthday.setText("")
self.birthday.setObjectName("birthday")
self.pushButton_home = QtWidgets.QPushButton(self.widget_main)
self.pushButton_home.setGeometry(QtCore.QRect(200, 10, 51, 23))
self.pushButton_home.setStyleSheet("QPushButton{background:Transparent;border-radius:5px;border:0px solid grey;}QPushButton:hover{background:CornflowerBlue;}")
self.pushButton_home.setObjectName("pushButton_home")
self.gridLayout.addWidget(self.widget_main, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 400, 23))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "UP主小助手"))
self.name_9.setText(_translate("MainWindow", "收到的赞:"))
self.name_4.setText(_translate("MainWindow", "粉丝数:"))
self.name_2.setText(_translate("MainWindow", "性别:"))
self.nameaaaa.setText(_translate("MainWindow", "昵称:"))
self.name_8.setText(_translate("MainWindow", "文章查看:"))
self.name_7.setText(_translate("MainWindow", "会员等级:"))
self.name_6.setText(_translate("MainWindow", "视频播放:"))
self.name_3.setText(_translate("MainWindow", "签名:"))
self.pushButton_help.setText(_translate("MainWindow", "帮助"))
self.nameaaaa_2.setText(_translate("MainWindow", "账号状态:"))
self.name_10.setText(_translate("MainWindow", "硬币:"))
self.name_11.setText(_translate("MainWindow", "生日:"))
self.pushButton_home.setText(_translate("MainWindow", "个人主页"))
import resources_rc
程序逻辑:function.py
import requests
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtGui import QImage, QPixmap, QStandardItem, QStandardItemModel, QIcon, QPalette, QBrush
from PyQt5.QtWidgets import QMessageBox, QTableView, QHeaderView, QAbstractItemView
from window import Ui_MainWindow
import qtawesome
import time
import resources_rc
import api_v2
class fun_main(Ui_MainWindow, QtWidgets.QMainWindow):
def __init__(self):
super(fun_main, self).__init__()
self.main_thread()
self.isTop = 0
self.setupUi(self)
self.set_icon()
self.set_location()
self.signal_on_button()
self.setWindowOpacity(0.9) # 设置窗口透明度
self.setAttribute(QtCore.Qt.WA_TranslucentBackground) ##主窗口透明
self.setWindowFlag(QtCore.Qt.FramelessWindowHint) ##隐藏边框
self.setWindowFlag(QtCore.Qt.WindowStaysOnTopHint) ##窗口始终置顶
self.setWindowIcon(QIcon(':/images/logo.ico'))
def set_icon(self):
self.pushButton_close.setIcon(qtawesome.icon('fa.close', color='blank'))
self.pushButton_min.setIcon(qtawesome.icon('fa.window-minimize', color='blank'))
self.pushButton_top.setIcon(qtawesome.icon('fa.thumb-tack', color='blank'))
def set_location(self): # 屏幕右上角
desktop = QtWidgets.QApplication.desktop()
x = (desktop.width() - self.width())
y = (desktop.height() - self.height())-10
self.move(x, y)
def signal_on_button(self):
# 主界面转换按钮
self.pushButton_close.clicked.connect(self.close_win)
self.pushButton_min.clicked.connect(self.showMinimized)
self.pushButton_top.clicked.connect(self.window_Top)
self.pushButton_help.clicked.connect(lambda: self.open_browser("https://www.toodo.fun/funs/learn/files/article.php?id=58&title=UP%E4%B8%BB%E5%B0%8F%E5%8A%A9%E6%89%8B%20%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E"))
self.pushButton_home.clicked.connect(lambda: self.open_browser(f"https://space.bilibili.com/{self.msm['mid']}"))
self.video_info.clicked.connect(self.video_clicked)
def close_win(self):
res = QMessageBox.question(self, '是否关闭小助手', '是否确认退出小助手', QMessageBox.Yes | QMessageBox.No,
QMessageBox.No)
if res == QMessageBox.Yes:
self.close()
else:
pass
def window_Top(self):
QMessageBox.information(self, '小助手提示', '当前UP主小助手仅支持置顶模式')
def open_browser(self, url):
QtGui.QDesktopServices.openUrl(QtCore.QUrl(url))
def main_thread(self):
try:
self.main_Thread.stop()
print("已经停止")
except:
pass
try:
# mid = self.mid.text()
self.main_Thread = Main_Thread()
self.main_Thread.display_signal.connect(self.change_UI)
self.main_Thread.start()
except:
QMessageBox.information(self, '小助手提示', '程序运行异常,请确定网络连接是否正常,然后尝试重启客户端,如问题还未解决,请点击帮助按钮留言')
def change_UI(self, msm):
self.msm = msm
if msm.get('error') == 1:
res = QMessageBox.question(self, '小助手提示', '配置文件"config.toml"不存在或者配置不正确,如有疑问请查看帮助', QMessageBox.Yes | QMessageBox.No,
QMessageBox.No)
if res == QMessageBox.Yes:
self.open_browser(
"https://www.toodo.fun/funs/learn/files/article.php?id=58&title=UP%E4%B8%BB%E5%B0%8F%E5%8A%A9%E6%89%8B%20%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E")
self.close()
else:
self.open_browser(
"https://www.toodo.fun/funs/learn/files/article.php?id=58&title=UP%E4%B8%BB%E5%B0%8F%E5%8A%A9%E6%89%8B%20%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E")
self.close()
return
self.name.setText(msm['nickname'])
self.sex.setText(msm['sex'])
try:
img = QImage.fromData(requests.get(msm['face']).content)
except:
img = QImage.fromData(requests.get("http://bpic.588ku.com/element_pic/01/47/72/305743feac8f672.jpg").content)
self.face.setPixmap(QPixmap.fromImage(img))
self.face.setScaledContents(True)
self.sign.setText(str(msm['sign']))
self.level.setText(f"{msm['level']}({msm['experience']['current']}/{msm['experience']['next']})")
self.arc_v.setText(str(msm['arc_view']))
self.art_v.setText(str(msm['art_view']))
self.likes.setText(str(msm['likes']))
self.follower.setText(str(msm['follower']))
self.count.setText(time.strftime("%Y-%m-%d", time.localtime(time.time())) + " 已刷新"+str(msm['count'])+"次,等待" + str(msm['refresh']) + "秒" )
self.coins.setText(str(msm['coins']))
if msm['ban'] == 0:
self.ban.setText("正常")
else:
self.ban.setText("封禁")
self.birthday.setText(time.strftime("%Y-%m-%d", time.localtime(msm['birthday'])))
self.model = QStandardItemModel(len(msm['videos']), 4)
self.model.clear()
self.model.setHorizontalHeaderLabels(['发布时间', '视频标题', '评论', '播放次数'])
for row in range(len(msm['videos'])):
for column in range(4):
item = QStandardItem(str(msm['videos'][row][column]))
self.model.setItem(row, column, item)
self.video_info.setModel(self.model)
self.video_info.setEditTriggers(QTableView.NoEditTriggers)
self.video_info.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.video_info.setSelectionMode(QAbstractItemView.SingleSelection)
self.video_info.horizontalHeader().setVisible(False)
self.video_info.verticalHeader().setVisible(False)
def video_clicked(self):
num = self.video_info.currentIndex().row()
self.open_browser(f"https://www.bilibili.com/video/av{self.msm['videos'][num][4]}/")
class Main_Thread(QThread):
display_signal = pyqtSignal(dict)
def __init__(self):
super().__init__()
self.flag = 1
def run(self):
num = 0
while self.flag == 1:
try:
msms = api_v2.main()
num += 1
msms['count'] = num
self.display_signal.emit(msms)
st = int(msms['refresh'])
time.sleep(st)
except:
msms = {"error": 1, "mid": 0}
self.display_signal.emit(msms)
time.sleep(1e5)
def stop(self):
self.flag = 0
api接口:api_v2.py(登录逻辑借鉴Bilibili-Tookit)
# -*- coding: utf-8 -*-
import base64
import chardet
import hashlib
import requests
import rsa
import sys
import time
import toml
import random
from urllib import parse
class Bilibili:
app_key = "1d8b6e7d45233436"
def __init__(self, https=True):
self._session = requests.Session()
self._session.headers.update({'User-Agent': "Mozilla/5.0 BiliDroid/5.51.1 (bbcallen@gmail.com)"})
self.get_cookies = lambda: self._session.cookies.get_dict(domain=".bilibili.com")
self.get_csrf = lambda: self.get_cookies().get("bili_jct", "")
self.get_sid = lambda: self.get_cookies().get("sid", "")
self.get_uid = lambda: self.get_cookies().get("DedeUserID", "")
self.access_token = ""
self.refresh_token = ""
self.username = ""
self.password = ""
self.info = {
'ban': False,
'coins': 0,
'experience': {
'current': 0,
'next': 0,
},
'face': "",
'level': 0,
'nickname': "",
'mid': "",
'sign': "",
'follower': 0,
'following': 0,
'birthday': 0,
'sex': "",
'arc_view': 0,
'art_view': 0,
'likes': 0,
'videos': [],
}
self.protocol = "https" if https else "http"
def _log(self, message):
log = f"[{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))}][{self.username if self.username else '#' + self.get_uid() if self.get_uid() else ''}] {message}"
print(log)
def _requests(self, method, url, decode_level=2, retry=10, timeout=15, **kwargs):
if method in ["get", "post"]:
for _ in range(retry + 1):
try:
response = getattr(self._session, method)(url, timeout=timeout, **kwargs)
return response.json() if decode_level == 2 else response.content if decode_level == 1 else response
except:
pass
return None
# 验证码识别
def _solve_captcha(self, image):
url = "https://bili.dev:2233/captcha"
payload = {'image': base64.b64encode(image).decode("utf-8")}
response = self._requests("post", url, json=payload)
return response['message'] if response and response.get("code") == 0 else None
@staticmethod
def calc_sign(param):
salt = "560c52ccd288fed045859ed18bffd973"
sign_hash = hashlib.md5()
sign_hash.update(f"{param}{salt}".encode())
return sign_hash.hexdigest()
# 登录
def login(self, **kwargs):
def by_cookie():
url = f"{self.protocol}://api.bilibili.com/x/space/myinfo"
headers = {'Host': "api.bilibili.com"}
response = self._requests("get", url, headers=headers)
if response and response.get("code") != -101:
return True
else:
return False
def by_token(force_refresh=False):
if not force_refresh:
param = f"access_key={self.access_token}&appkey={Bilibili.app_key}&ts={int(time.time())}"
print(param)
url = f"{self.protocol}://passport.bilibili.com/api/v2/oauth2/info?{param}&sign={self.calc_sign(param)}"
response = self._requests("get", url)
if response and response.get("code") == 0:
self._session.cookies.set('DedeUserID', str(response['data']['mid']), domain=".bilibili.com")
param = f"access_key={self.access_token}&appkey={Bilibili.app_key}&gourl={self.protocol}%3A%2F%2Faccount.bilibili.com%2Faccount%2Fhome&ts={int(time.time())}"
url = f"{self.protocol}://passport.bilibili.com/api/login/sso?{param}&sign={self.calc_sign(param)}"
self._requests("get", url, decode_level=0)
if all(key in self.get_cookies() for key in
["bili_jct", "DedeUserID", "DedeUserID__ckMd5", "sid", "SESSDATA"]):
return True
else:
pass
url = f"{self.protocol}://passport.bilibili.com/api/v2/oauth2/refresh_token"
param = f"access_key={self.access_token}&appkey={Bilibili.app_key}&refresh_token={self.refresh_token}&ts={int(time.time())}"
payload = f"{param}&sign={self.calc_sign(param)}"
headers = {'Content-type': "application/x-www-form-urlencoded"}
response = self._requests("post", url, data=payload, headers=headers)
if response and response.get("code") == 0:
for cookie in response['data']['cookie_info']['cookies']:
self._session.cookies.set(cookie['name'], cookie['value'], domain=".bilibili.com")
self.access_token = response['data']['token_info']['access_token']
self.refresh_token = response['data']['token_info']['refresh_token']
return True
else:
self.access_token = ""
self.refresh_token = ""
return False
def by_password():
def get_key():
url = f"{self.protocol}://passport.bilibili.com/api/oauth2/getKey"
payload = {
'appkey': Bilibili.app_key,
'sign': self.calc_sign(f"appkey={Bilibili.app_key}"),
}
while True:
response = self._requests("post", url, data=payload)
if response and response.get("code") == 0:
return {
'key_hash': response['data']['hash'],
'pub_key': rsa.PublicKey.load_pkcs1_openssl_pem(response['data']['key'].encode()),
}
else:
time.sleep(1)
while True:
key = get_key()
key_hash, pub_key = key['key_hash'], key['pub_key']
url = f"{self.protocol}://passport.bilibili.com/api/v2/oauth2/login"
param = f"appkey={Bilibili.app_key}&password={parse.quote_plus(base64.b64encode(rsa.encrypt(f'{key_hash}{self.password}'.encode(), pub_key)))}&username={parse.quote_plus(self.username)}"
payload = f"{param}&sign={self.calc_sign(param)}"
headers = {'Content-type': "application/x-www-form-urlencoded"}
response = self._requests("post", url, data=payload, headers=headers)
while True:
if response and response.get("code") is not None:
if response['code'] == -105:
url = f"{self.protocol}://passport.bilibili.com/captcha"
headers = {'Host': "passport.bilibili.com"}
response = self._requests("get", url, headers=headers, decode_level=1)
captcha = self._solve_captcha(response)
if captcha:
key = get_key()
key_hash, pub_key = key['key_hash'], key['pub_key']
url = f"{self.protocol}://passport.bilibili.com/api/v2/oauth2/login"
param = f"appkey={Bilibili.app_key}&captcha={captcha}&password={parse.quote_plus(base64.b64encode(rsa.encrypt(f'{key_hash}{self.password}'.encode(), pub_key)))}&username={parse.quote_plus(self.username)}"
payload = f"{param}&sign={self.calc_sign(param)}"
headers = {'Content-type': "application/x-www-form-urlencoded"}
response = self._requests("post", url, data=payload, headers=headers)
else:
time.sleep(10)
elif response['code'] == -449:
time.sleep(1)
response = self._requests("post", url, data=payload, headers=headers)
elif response['code'] == 0 and response['data']['status'] == 0:
for cookie in response['data']['cookie_info']['cookies']:
self._session.cookies.set(cookie['name'], cookie['value'], domain=".bilibili.com")
self.access_token = response['data']['token_info']['access_token']
self.refresh_token = response['data']['token_info']['refresh_token']
return True
else:
return False
else:
time.sleep(60)
self._session.cookies.clear()
for name in ["bili_jct", "DedeUserID", "DedeUserID__ckMd5", "sid", "SESSDATA"]:
value = kwargs.get(name)
if value:
self._session.cookies.set(name, value, domain=".bilibili.com")
self.access_token = kwargs.get("access_token", "")
self.refresh_token = kwargs.get("refresh_token", "")
self.username = kwargs.get("username", "")
self.password = kwargs.get("password", "")
force_refresh_token = kwargs.get("force_refresh_token", False)
if (not force_refresh_token or not self.access_token or not self.refresh_token) and all(
key in self.get_cookies() for key in
["bili_jct", "DedeUserID", "DedeUserID__ckMd5", "sid", "SESSDATA"]) and by_cookie():
return True
elif self.access_token and self.refresh_token and by_token(force_refresh_token):
return True
elif self.username and self.password and by_password():
return True
else:
self._session.cookies.clear()
return False
# 获取用户信息
def get_user_info(self):
url = f"{self.protocol}://api.bilibili.com/x/space/myinfo?jsonp=jsonp"
headers = {
'Host': "api.bilibili.com",
'Referer': f"https://space.bilibili.com/{self.get_uid()}/",
}
response = self._requests("get", url, headers=headers)
if response and response.get("code") == 0:
self.info['ban'] = bool(response['data']['silence'])
self.info['coins'] = response['data']['coins']
self.info['experience']['current'] = response['data']['level_exp']['current_exp']
self.info['experience']['next'] = response['data']['level_exp']['next_exp']
self.info['face'] = response['data']['face']
self.info['level'] = response['data']['level']
self.info['nickname'] = response['data']['name']
self.info['sign'] = response['data']['sign']
self.info['mid'] = self.get_uid()
self.info['follower'] = response['data']['follower']
self.info['following'] = response['data']['following']
self.info['birthday'] = response['data']['birthday']
self.info['sex'] = response['data']["sex"]
else:
return False
up_views = self._requests("get",f"https://api.bilibili.com/x/space/upstat?mid={self.get_uid()}",headers=headers)
if up_views and up_views.get("code") == 0:
arc_view = up_views["data"]["archive"]["view"]
art_view = up_views["data"]["article"]["view"]
likes = up_views["data"]["likes"]
else:
arc_view, art_view, likes = "unknow", "unknow", "unknow"
self.info['arc_view'] = arc_view
self.info['art_view'] = art_view
self.info['likes'] = likes
video_info = self._requests("get", f"https://api.bilibili.com/x/space/arc/search?mid={self.get_uid()}&ps=30&pn=1", headers=headers)
# print(video_info)
if video_info["code"] == 0:
count = video_info["data"]["page"]["count"]
videos = []
for i in video_info["data"]["list"]["vlist"]:
aid = i["aid"]
title = i["title"]
created_time = time.strftime("%Y-%m-%d", time.localtime(i["created"]))
comment = i["comment"]
play = i["play"]
videos.append([created_time, title, comment, play, aid])
if count > 30:
if count % 30 == 0:
pn = int(count / 30)
else:
pn = int(count / 30) + 1
for i in range(2, pn + 1):
video_info = self._requests("get",
f"https://api.bilibili.com/x/space/arc/search?mid={self.get_uid()}&ps=30&pn={i}", headers=headers)
if video_info["code"] == 0:
count = video_info["data"]["page"]["count"]
for i in video_info["data"]["list"]["vlist"]:
aid = i["aid"]
title = i["title"]
created_time = time.strftime("%Y-%m-%d", time.localtime(i["created"]))
comment = i["comment"]
play = i["play"]
videos.append([created_time, title, comment, play, aid])
else:
videos = []
self.info['videos'] = videos
return True
def detect_charset(file, fallback="utf-8"):
with open(file, "rb") as f:
detector = chardet.UniversalDetector()
for line in f.readlines():
detector.feed(line)
if detector.done:
return detector.result['encoding']
return fallback
def run_app(arg):
app = Bilibili()
config, account = arg['config'], arg['account']
if app.login(force_refresh_token=True, **account):
if app.get_user_info():
return [{
'username': app.username,
'password': app.password,
'access_token': app.access_token,
'refresh_token': app.refresh_token,
'cookie': app.get_cookies(),
}], app.info
def main():
config_file = sys.argv[1] if len(sys.argv) > 1 else "config.toml"
try:
with open(config_file, "r", encoding=detect_charset(config_file)) as f:
config = toml.load(f)
except:
print(f"无法加载{config_file}")
return
accounts = []
for line in config['user']['account'].splitlines():
try:
if line[0] == "#":
continue
pairs = {}
for pair in line.strip(";").split(";"):
if len(pair.split("=")) == 2:
key, value = pair.split("=")
pairs[key] = value
password = all(key in pairs for key in ["username", "password"])
token = all(key in pairs for key in ["access_token", "refresh_token"])
cookie = all(key in pairs for key in ["bili_jct", "DedeUserID", "DedeUserID__ckMd5", "sid", "SESSDATA"])
if password or token or cookie:
accounts.append(pairs)
except:
pass
config['user'].pop("account")
if not accounts:
return
# 主程序
result, user_info = run_app({"config": config, "account": accounts[0]})
if config['user']['cookies']:
with open(config_file, "r+", encoding=detect_charset(config_file)) as f:
content = f.read()
before = content.split("account")[0]
after = content.split("account")[-1].split("\"\"\"")[-1]
f.seek(0)
f.truncate()
f.write(before)
f.write("account = \"\"\"\n")
for credential in result:
new_line = False
for key, value in credential.items():
if value:
if key == "cookie":
f.write(f"{';'.join(f'{key}={value}' for key, value in value.items())};")
else:
f.write(f"{key}={value};")
new_line = True
if new_line:
f.write("\n")
f.write("\"\"\"")
f.write(after)
refresh = config['global']['refresh']
refresh = int(refresh) + random.randint(-10, 10)
user_info['refresh'] = refresh
return user_info
if __name__ == "__main__":
a = main()
print(a)
程序图片资源:resources.qrc logo.ico 2233.png
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/images">
<file alias="logo.ico">images/logo.ico</file>
<file alias="2233.png">images/2233.png</file>
</qresource>
</RCC>
程序配置文件:config.toml
[global]
refresh = 60 #刷新间隔(s)
[user]
cookies = true # 自动保存cookies,方便以后登录获取信息
account = """
username=你的账号;password=你的密码;
"""