零依赖Python实现B站自动签到:Cookie驱动与API调用实战
1. 项目概述与核心价值
如果你是一个B站深度用户,每天手动完成登录、看视频、分享、投币这些日常任务来获取经验值,或者为了支持喜欢的主播,需要去他的直播间发送弹幕签到,时间一长,这绝对会变成一件枯燥又容易忘记的“负担”。我自己就经历过这个阶段,直到我决定用代码把这件事自动化。今天要分享的,就是基于XiaoYiWeio/bili-checkin这个项目思路,我构建的一套零依赖、纯Python标准库实现的B站全自动签到与任务系统。它完美解决了两个核心痛点:一是每日65点经验值的自动获取,二是直播间弹幕签到的无人值守执行。
这个工具的核心设计哲学是“极简与高效”。它不依赖任何第三方HTTP请求库(如requests),也不使用笨重的浏览器自动化工具(如Selenium),仅仅通过Python内置的urllib模块,配合你从浏览器获取的一次性登录Cookie(SESSDATA和bili_jct),就能模拟真人操作,调用B站官方的内部API。这意味着它的运行速度极快,资源占用极低,并且因为去除了复杂的依赖,部署和迁移变得异常简单。无论是放在自己的树莓派、NAS上,还是利用云服务器的定时任务,都能稳定可靠地运行。
对于想要快速提升B站等级的用户、需要维护多个账号的UP主助手、或是单纯想用技术解放双手的开发者来说,这个方案提供了一个清晰、可复现的路径。接下来,我会从设计思路、核心原理、到每一步的实操细节,包括如何安全地获取Cookie、如何解析API、以及如何避开那些我踩过的坑,为你完整拆解这个项目。你会发现,用不到200行Python代码,就能构建一个属于自己的自动化小助手。
2. 核心设计思路与架构解析
2.1 为什么选择“零依赖”与“Cookie驱动”方案?
在开始动手之前,方案选型至关重要。市面上常见的自动化方案主要有两类:一是基于Selenium或Playwright的浏览器模拟,二是基于requests或aiohttp的直接API调用。我最终选择了后者,并且更进一步,连requests这样的第三方库都舍弃了,原因如下。
首先,浏览器模拟方案虽然直观,能执行几乎所有用户操作,但其缺点非常明显:资源消耗大(需要启动一个完整的浏览器进程)、运行速度慢、对环境依赖强(需要匹配的浏览器驱动),并且极其不稳定——B站前端页面结构稍有变动,脚本就可能失效。这对于需要7x24小时稳定运行的后台任务来说,是不可接受的。
其次,基于第三方库的API调用是更优的选择,但引入requests库仍然增加了一个外部依赖。对于追求极致简洁和可移植性的项目,我希望它能在任何有Python标准环境的地方“开箱即用”,无需pip install。Python 3.4+ 内置的urllib.request和json库已经完全足够处理HTTP请求和JSON解析。
那么,如何实现登录认证呢?这就是“Cookie驱动”的精髓。我们不需要模拟登录过程(那需要处理验证码、加密密码等复杂问题)。我们只需要让用户在浏览器中正常登录一次B站,然后从浏览器的开发者工具中提取出关键的登录态Cookie——主要是SESSDATA和bili_jct。这两个Cookie值包含了服务器认可的会话信息和CSRF令牌。只要在后续的API请求中携带它们,B站服务器就会认为这是“已登录用户”发出的合法请求。这种方法将复杂的登录逻辑交给了用户和浏览器,脚本只负责最纯粹的“任务执行”,大大降低了复杂度和维护成本。
注意:Cookie是有有效期的(通常为1个月左右)。这意味着你需要定期(约每月一次)重新登录并更新Cookie文件。这是此方案的一个小小代价,但相比其带来的稳定性和简便性,是完全值得的。
2.2 项目架构与模块职责
基于以上思路,项目的目录结构非常清晰,每个脚本各司其职:
bili-checkin/ ├── scripts/ │ ├── daily.py # 核心:执行每日经验任务(登录、观看、分享、投币、直播签到) │ ├── checkin.py # 核心:执行直播间弹幕签到,并包含Cookie管理功能 │ └── lookup.py # 工具:将UP主名称、UID、空间链接等解析为直播间房间号(room_id) ├── SKILL.md # 用于集成到OpenClaw AI助手的技能定义文件 └── persona.md # 定义AI助手与用户交互的对话人格checkin.py是这个项目的基础。它承担了两个核心功能:
- Cookie管理:提供了
--save-cookie参数,允许用户将提取的SESSDATA和bili_jct加密后保存到本地文件(~/.bili_cookie),并设置严格的文件权限(600),确保只有用户自己可读。 - 直播间签到:接收房间号(
room_id)和自定义弹幕内容,通过调用B站直播间的发送弹幕API,完成签到,并返回亲密度和经验值奖励信息。
daily.py是主要的任务执行器。它按顺序调用四个B站经验任务API:
- 登录任务:模拟一次客户端登录,触发每日登录经验。
- 观看任务:通过API“报告”观看了一个视频(通常是一个固定的、无需真实播放的视频ID)。
- 分享任务:通过API“报告”分享了一个视频。
- 投币任务:这是经验值的大头,每天最多投5枚硬币,可获得50经验。脚本会自动从账号余额中投出5枚硬币。考虑到硬币的珍贵性,脚本贴心地提供了
--skip-coin参数,允许用户跳过此步骤。 - 直播签到:实际上,它会内部调用
checkin.py的逻辑,向一个默认的或指定的直播间发送签到弹幕。
lookup.py是一个实用的辅助工具。B站的直播间签到需要的是数字房间号(room_id),但用户通常只知道UP主的名字、个人空间链接或直播链接。这个脚本通过一系列网络请求,将这些信息解析为最终的room_id,极大提升了工具的易用性。
这种模块化设计使得代码逻辑清晰,易于维护和扩展。例如,未来如果想增加“银瓜子兑换”或“漫画签到”等功能,只需在daily.py中添加相应的函数模块即可。
3. 环境准备与一次性配置详解
3.1 获取核心身份凭证:SESSDATA 与 bili_jct
这是整个流程中唯一需要手动操作的步骤,也是最重要的一步。请严格按照以下步骤在Chrome或Edge浏览器(内核相同)中操作:
- 正常登录:打开 bilibili.com ,输入账号密码,完成登录。确保登录成功,能正常浏览个人动态。
- 打开开发者工具:在B站任意页面,按下键盘上的
F12键,或右键点击页面选择“检查”。 - 定位到Cookies:在开发者工具顶部,切换到“Application”标签页(中文可能是“应用程序”)。在左侧侧边栏,找到“Storage” -> “Cookies”,并点击其下展开的
https://www.bilibili.com。 - 查找目标Cookie:在右侧会列出该域名下所有的Cookie。你需要找到名为
SESSDATA和bili_jct的两行。SESSDATA:一串很长的、包含百分号%的字符串,这是你的会话数据。bili_jct:一串较短的、由字母和数字组成的字符串,这是用于防止跨站请求伪造(CSRF)的令牌。
- 复制值:分别双击这两个Cookie的“Value”字段,完整地复制它们的值。请务必小心,不要复制到多余的空格或换行符。
实操心得:
SESSDATA的值通常以%开头和结尾,中间包含很多%2A之类的编码字符,复制时要确保完整。一个验证方法是,复制出来的字符串长度通常在200字符以上。bili_jct则短得多,像一个普通的令牌字符串。
3.2 安全保存Cookie到本地
获取到Cookie后,绝不能以明文形式写在脚本里或到处粘贴。我们使用项目提供的checkin.py脚本,将其安全地保存到你的用户主目录下的一个隐藏文件中。
打开终端(Linux/macOS)或命令提示符/PowerShell(Windows),进入你克隆的项目目录bili-checkin/scripts/。
运行以下命令,将YOUR_SESSDATA和YOUR_BILI_JCT替换为你刚才复制的实际值:
python3 checkin.py --save-cookie --sessdata "你的SESSDATA长字符串" --bili-jct "你的bili_jct字符串"这个命令背后做了什么?
- 脚本会在你的家目录(
~)下创建一个名为.bili_cookie的隐藏文件。 - 它将
SESSDATA和bili_jct以key=value的格式写入文件。 - 最关键的一步,它会调用系统命令,将文件权限设置为
600(即,仅文件所有者可读可写,其他用户无任何权限)。这是保护你账号安全的重要措施,防止其他用户或进程窃取你的登录态。
执行成功后,通常会看到类似Cookie saved to /home/your_username/.bili_cookie的提示。至此,一次性配置完成。后续所有脚本运行时,都会自动从这个文件中读取Cookie,无需再次输入。
4. 核心功能实操与代码逐行解析
4.1 直播间弹幕签到 (checkin.py)
这是最基础的功能模块。我们来看其核心函数send_danmaku是如何工作的。
# 示例代码结构,非完整代码 import urllib.request import urllib.parse import json import os def load_cookie(): """从 ~/.bili_cookie 文件加载Cookie""" cookie_file = os.path.expanduser('~/.bili_cookie') if not os.path.exists(cookie_file): raise FileNotFoundError("Cookie文件未找到,请先运行 --save-cookie 命令") cookies = {} with open(cookie_file, 'r') as f: for line in f: if '=' in line: key, value = line.strip().split('=', 1) cookies[key] = value return cookies.get('SESSDATA'), cookies.get('bili_jct') def send_danmaku(room_id, msg="签到"): """向指定直播间发送弹幕""" sessdata, csrf = load_cookie() # csrf 即 bili_jct if not all([sessdata, csrf]): print("错误:Cookie信息不完整") return # 1. 构建API请求URL和参数 url = "https://api.live.bilibili.com/msg/send" # 弹幕发送API需要表单格式的数据 data = urllib.parse.urlencode({ 'color': '16777215', # 默认白色 'fontsize': '25', # 标准字体大小 'mode': '1', # 滚动弹幕 'msg': msg, # 弹幕内容 'rnd': int(time.time()), # 随机数,防重复 'roomid': room_id, # 直播间ID 'bubble': '0', 'csrf': csrf, # 关键的CSRF令牌 'csrf_token': csrf, }).encode('utf-8') # 2. 构建请求头,携带Cookie headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Cookie': f'SESSDATA={sessdata}', 'User-Agent': 'Mozilla/5.0 ...', # 模拟浏览器UA 'Referer': f'https://live.bilibili.com/{room_id}', # 重要,表明请求来源 'Origin': 'https://live.bilibili.com', } # 3. 使用urllib发送POST请求 req = urllib.request.Request(url, data=data, headers=headers) try: with urllib.request.urlopen(req) as response: result = json.loads(response.read().decode('utf-8')) # 4. 解析响应 if result.get('code') == 0: print(f"弹幕发送成功:{msg}") # 响应中通常包含亲密度变化信息 data = result.get('data', {}) print(f"亲密度+{data.get(' intimacy', 0)}, 经验值+{data.get('exp', 0)}") else: print(f"弹幕发送失败:{result.get('message')}") except urllib.error.URLError as e: print(f"网络请求失败:{e.reason}")关键点解析:
- CSRF防护:B站的所有写操作(发送弹幕、投币、分享)都需要验证
csrf(即bili_jct)参数,它必须与Cookie中的bili_jct值一致,这是防止跨站请求伪造的重要机制。我们的脚本从同一个源获取这两个值,保证了其一致性。 - 请求头(Headers):
User-Agent模拟了真实浏览器,Referer和Origin头对于通过B站服务器的反爬虫检查至关重要,缺少它们可能导致403错误。 - 错误处理:代码包含了基本的网络错误(
URLError)和API错误(code != 0)处理。在实际使用中,你可能需要根据不同的错误码(如“弹幕发送过于频繁”)添加重试逻辑或延迟。
4.2 每日经验任务全自动执行 (daily.py)
daily.py脚本是任务编排的中心。它按顺序调用各个任务函数。我们以“投币任务”为例,看看如何安全地消耗硬币。
def coin_task(csrf, sessdata): """执行投币任务,默认投5枚""" url = "https://api.bilibili.com/x/web-interface/coin/add" # 需要投币的视频ID,这里通常选择一个固定的、允许投币的视频 target_aid = "170001" # 示例视频,实际可能需要一个长期有效的视频 headers = { /* 类似上述的headers,包含Cookie和csrf */ } coins_to_add = 5 success_count = 0 for i in range(coins_to_add): data = urllib.parse.urlencode({ 'aid': target_aid, 'multiply': '1', # 每次投1枚 'select_like': '1', # 同时点赞 'cross_domain': 'true', 'csrf': csrf }).encode() req = urllib.request.Request(url, data=data, headers=headers, method='POST') try: with urllib.request.urlopen(req) as resp: result = json.load(resp) if result['code'] == 0: success_count += 1 print(f"投币成功 ({i+1}/5)") else: # 处理错误:硬币不足、重复投币等 print(f"投币失败:{result['message']}") break except Exception as e: print(f"投币请求异常:{e}") break time.sleep(1) # 关键:每次投币间隔1秒,避免触发频率限制 print(f"投币任务完成,成功投出 {success_count} 枚硬币,预计获得 {success_count * 10} 经验") return success_count注意事项与心得:
- 目标视频选择:脚本需要指定一个视频ID(
aid)进行投币。选择一个官方、长期存在且允许投币的视频至关重要。如果视频被删除或UP主关闭了投币功能,任务就会失败。在实际项目中,这个target_aid可能需要定期维护或提供一个可配置的选项。 - 频率限制:B站对投币操作有频率限制。代码中的
time.sleep(1)是必须的,快速连续请求会导致操作被拒绝。甚至1秒间隔有时也可能触发限制,在实际运行中,我建议增加到2-3秒更为稳妥。 - 硬币余额检查:当前脚本没有在投币前检查账户硬币余额。如果硬币不足,API会返回错误。一个更健壮的实现应该先调用账户信息API查询硬币余额,再决定投币数量。
--skip-coin参数:这个参数非常实用。对于硬币稀缺的用户,或者只想完成登录、观看、分享等“零成本”任务时,可以使用此参数跳过投币步骤。
4.3 UP主信息查找与房间号解析 (lookup.py)
这个工具脚本极大地提升了易用性。其原理是通过B站开放的搜索API和网页解析,将多种输入统一为直播间房间号room_id。
def resolve_to_room_id(identifier): """ 解析输入,返回room_id。 输入可以是:房间号、UP主名、UID、空间链接、直播链接。 """ # 情况1:输入本身就是纯数字,假设为room_id if identifier.isdigit(): # 可以额外请求API验证该room_id是否存在 return identifier # 情况2:输入是直播链接,如 https://live.bilibili.com/22605245 if 'live.bilibili.com' in identifier: # 使用正则表达式从URL中提取数字 import re match = re.search(r'live\.bilibili\.com/(\d+)', identifier) if match: return match.group(1) # 情况3:输入是空间链接或UID,如 https://space.bilibili.com/123456 或 “123456” # 情况4:输入是UP主名称,如 “老番茄” # 对于情况3和4,流程更复杂: # a. 如果是UID或空间链接,提取出数字UID。 # b. 如果是名称,使用搜索API https://api.bilibili.com/x/web-interface/search/type 搜索用户,获取第一个结果的UID。 # c. 拿到UID后,请求 https://api.bilibili.com/x/space/acc/info?mid={UID} 获取用户信息。 # d. 从用户信息JSON中,找到 `live_room` 对象下的 `roomid` 字段。 # e. 如果 `live_room` 为空,说明该UP主未开通直播间。 # 这里省略具体代码,展示逻辑链条 uid = extract_uid(identifier) # 提取或搜索得到UID user_info = get_user_info_by_uid(uid) # 请求用户信息API room_id = user_info.get('data', {}).get('live_room', {}).get('roomid') if room_id: return str(room_id) else: raise ValueError(f"未找到用户 {identifier} 的直播间信息")通过这个解析器,用户就可以使用非常自然的方式指定签到目标:
python3 lookup.py “老番茄” python3 lookup.py 22605245 python3 lookup.py https://live.bilibili.com/22605245脚本会自动输出对应的room_id,这个room_id可以直接用于checkin.py的--room参数。
5. 部署方案与自动化运行指南
让脚本每天自动运行,才能真正解放双手。这里提供几种常见的部署方案。
5.1 方案一:Linux/macOS 系统的 Crontab(最经典)
Crontab是类Unix系统上最常用的定时任务工具。假设你的脚本位于/home/user/bili-checkin/scripts。
打开当前用户的crontab编辑界面:
crontab -e添加定时任务:在文件末尾添加一行。以下例子设定每天上午10点15分执行每日任务,并在晚上8点30分为指定主播进行直播间签到。
# 分 时 日 月 周 命令 15 10 * * * cd /home/user/bili-checkin/scripts && /usr/bin/python3 daily.py >> /tmp/bili_daily.log 2>&1 30 20 * * * cd /home/user/bili-checkin/scripts && /usr/bin/python3 checkin.py --room 22605245 --msg “晚安打卡” >> /tmp/bili_live.log 2>&1cd /path/to/scripts:确保在正确的目录下执行,以便脚本能找到相对路径的模块或配置文件。/usr/bin/python3:使用绝对路径指定Python解释器,避免因环境变量问题找不到命令。>> /tmp/bili_daily.log 2>&1:将脚本的标准输出和标准错误都重定向追加到日志文件中,方便后续查看执行结果和排查问题。/tmp目录下的文件重启可能会消失,如需持久化,请改为~/bili.log等路径。
保存并退出(在vim中按
Esc后输入:wq)。Cron服务会自动加载新配置。
实操心得:Cron的时间语法容易出错。一个在线工具 crontab.guru 可以帮助你快速验证和生成cron表达式。另外,Cron的环境变量非常精简,可能不包含你终端里的
PATH,这就是为什么强调要使用绝对路径的原因。
5.2 方案二:Windows 系统的任务计划程序
对于Windows用户,可以通过图形化的“任务计划程序”来实现。
- 搜索并打开“任务计划程序”。
- 点击右侧“创建基本任务”。
- 填写名称和描述,例如“B站每日签到”。
- 触发器选择“每天”,并设置具体时间。
- 操作选择“启动程序”。
- “程序或脚本”填写:
C:\Python39\python.exe(你的Python安装路径)。 - “添加参数”填写:
D:\path\to\bili-checkin\scripts\daily.py(你的脚本绝对路径)。 - “起始于”填写:
D:\path\to\bili-checkin\scripts(脚本所在目录)。
- “程序或脚本”填写:
- 完成创建后,可以在右侧手动“运行”一次进行测试。
5.3 方案三:使用云服务器或容器
如果你有常开的云服务器(如腾讯云、阿里云的轻量应用服务器),或者在家里的NAS(如群晖DSM)上运行Docker,部署起来更为方便。
- 云服务器:通过SSH连接到服务器,按照方案一的步骤配置Crontab即可。优势是网络稳定,24小时在线。
- Docker容器:你可以编写一个简单的Dockerfile,将脚本和Cookie文件(通过卷挂载或构建时复制)打包进镜像,然后使用
docker run配合--restart=always和cron服务在容器内运行定时任务。这种方式隔离性好,易于迁移。
通用建议:无论采用哪种部署方式,务必先手动运行几次脚本,确保一切正常,再加入到定时任务中。同时,配置日志记录(如上文中的>> logfile)是排查问题的生命线。
6. 常见问题排查与进阶技巧
即使脚本设计得再完善,在实际运行中也可能遇到各种问题。这里记录了我遇到的一些典型情况及其解决方法。
6.1 Cookie失效问题
现象:脚本突然开始报错,提示“未登录”或“权限错误”,错误码可能是-101、-111等。
原因与解决:
- 自然过期:
SESSDATA的默认有效期大约为30天。到期后需要重新登录B站网页版,并按照3.1节的步骤获取新的Cookie值,然后重新运行python3 checkin.py --save-cookie ...命令更新本地文件。 - 异地登录或异常行为:如果你的账号在别处登录,或者B站检测到异常活动,可能会使当前Cookie失效。同样需要重新获取。
- 文件权限或损坏:检查
~/.bili_cookie文件是否存在,权限是否为600,内容格式是否正确(SESSDATA=xxx和bili_jct=yyy各占一行)。
自动化提醒思路:可以写一个简单的检查脚本,定期(比如每周一次)调用一个需要登录态的API(如查询账户信息),如果返回未登录错误,则通过邮件、Server酱或Telegram Bot发送提醒通知你更新Cookie。
6.2 网络请求失败或返回403错误
现象:脚本执行失败,提示URLError或HTTP Error 403: Forbidden。
原因与解决:
- User-Agent被识别:B站可能会屏蔽一些简单的Python UA。确保你的请求头中
User-Agent是一个常见的浏览器UA字符串,可以从你自己的浏览器开发者工具中复制一个。 - 缺少Referer或Origin头:如4.1节所述,对于发送弹幕、投币等POST请求,
Referer和Origin头是强校验的,必须正确设置。 - IP频率限制:如果你在短时间内发送了大量请求(比如快速循环投币),可能会触发B站的临时IP限制。解决方案是在关键操作(如投币、发送弹幕)之间增加足够的延迟(
time.sleep(2)或更长)。 - 代理问题:如果你身处需要特殊网络环境的地区,确保运行脚本的机器网络通畅。脚本本身不支持配置代理,如果需要,可以设置全局代理或修改代码使用
urllib.request.ProxyHandler。
6.3 任务执行不完整或经验未增加
现象:脚本运行显示成功,但B站APP或网页上经验值没有变化,或者某些任务(如分享)没完成。
原因与解决:
- API变更:B站的后端API可能会升级或改动。虽然核心API相对稳定,但仍有小概率发生变化。关注脚本运行日志,如果某个任务持续失败,可以手动在浏览器中抓包(F12 -> Network),查看完成任务时调用的真实API地址和参数,与脚本中的进行对比更新。
- 视频ID失效:每日任务中的“观看”和“分享”通常针对一个固定的视频ID。如果这个视频被删除,任务就会失败。需要更新脚本中的
aid参数。你可以手动找一个B站官方的、长期存在的视频(比如“每日动画”),用其aid替换。 - 投币任务硬币不足或重复:投币前最好先查询余额。另外,B站规定每个视频最多投2枚硬币。如果脚本一直对同一个视频投币,投到第3枚时会失败。更健壮的逻辑是准备一个视频ID列表,轮流投币。
6.4 进阶技巧:多账号管理与安全增强
- 多账号支持:你可以创建多个Cookie文件,例如
.bili_cookie_account1和.bili_cookie_account2。然后修改load_cookie()函数,使其接受一个账号标识参数,读取对应的文件。在定时任务中,为每个账号分别设置一个Cron任务,使用不同的Cookie文件路径。 - 环境变量配置:将Cookie值或文件路径通过环境变量传入,而不是硬编码在脚本或命令行中,安全性更高。例如:
然后在脚本中使用export BILI_SESSDATA="your_sessdata" export BILI_JCT="your_jct" python3 daily.pyos.environ.get('BILI_SESSDATA')读取。 - 加入随机延迟与随机话术:让自动化行为更接近真人。在定时任务触发时间上加入随机分钟数偏移,在发送的弹幕内容中从一个预定义的列表(如“打卡”、“来了”、“签到”、“报道”)中随机选择,可以进一步降低被系统识别为机器人的风险。
这个项目麻雀虽小,五脏俱全。它不仅仅是一个签到脚本,更是一个理解Web自动化、API调用、Cookie机制和定时任务部署的绝佳实践。通过亲手配置和解决运行中遇到的问题,你能对网络编程和系统运维有更深的体会。最重要的是,它真的能帮你省下每天几分钟的重复操作,让科技服务于生活。
