1. 项目概述与核心思路拆解最近在折腾一块性能不错的RISC-V开发板总琢磨着怎么把它用起来而不是让它吃灰。正好我每天都会刷B站但有时候忙起来或者摸鱼摸得不够专业会错过一些重要的私信、评论或者系统通知。虽然B站有App推送但一来手机通知容易被淹没二来总感觉缺了点极客的仪式感。于是一个想法冒了出来能不能用这块开发板配合一个自己写的Python脚本来实时监测B站的未读消息并用一种更直观、更物理化的方式提醒我呢比如让板载的LED灯闪烁或者通过一个小屏幕显示未读数。这个项目的核心其实就是实现一个轻量级的、可定制的B站消息监测终端。它不依赖手机App而是直接与B站的服务端API进行交互定时轮询未读消息状态再通过开发板的硬件接口给出反馈。听起来好像就是个“网络请求硬件控制”的小程序但里面涉及到几个关键的技术点如何安全地模拟登录B站、如何解析其API返回的复杂数据、如何在资源相对有限的嵌入式环境下稳定运行Python网络程序以及如何设计一个合理的轮询策略以避免对B站服务器造成不必要的压力或被风控。选择RISC-V开发板一方面是出于对开源架构的兴趣和手头有现成设备另一方面也是想验证在非x86/ARM的主流生态下用Python做这种网络硬件交互项目的可行性。实测下来只要选对工具链和库体验相当流畅。2. 技术选型与环境搭建要点工欲善其事必先利其器。在动手写代码之前我们需要明确技术栈并搭建好开发环境。2.1 硬件平台选择为何是RISC-V我使用的是一款基于阿里平头哥C906核心的RISC-V开发板主频在1GHz以上内存有512MB或1GB的版本。选择它主要基于几点考虑性能足够监测脚本本身计算量不大但网络请求和JSON解析需要一定的CPU资源。这款板子的性能足以流畅运行一个完整的Linux发行版如Debian和Python环境。GPIO与外围设备板子自带用户可编程的LED灯并且通过排针引出了标准的GPIO、I2C、SPI等接口方便后续扩展例如连接OLED屏幕。社区与生态虽然相比树莓派RISC-V的生态还在成长但这款板子的Linux系统镜像、基础驱动支持都比较完善降低了底层折腾的成本。当然如果你手头有树莓派、香橙派或者其他ARM开发板这个项目完全可以无缝迁移原理和代码几乎通用。2.2 软件栈与依赖库解析脚本的核心逻辑在Python 3上实现。主要依赖以下几个库requests用于发送HTTP请求到B站API。这是最核心的网络库比标准库的urllib更简洁易用。json用于解析B站API返回的JSON格式数据。time/datetime用于实现定时任务和日志时间戳。logging用于记录脚本运行日志便于排查问题。硬件控制库根据你的开发板型号和要驱动的设备而定。如果只是控制板载LEDLinux下通常可以通过读写/sys/class/leds/目录下的文件来实现无需额外库。如果需要驱动I2C的OLED屏幕可能需要smbus2或Adafruit_SSD1306等库。安装这些库非常简单在开发板的Linux终端里执行sudo apt update sudo apt install python3-pip pip3 install requests # 如果需要OLED屏幕 pip3 install smbus2 pillow Adafruit-SSD1306注意在嵌入式设备上建议优先使用系统包管理器apt安装库如果版本不满足再用pip。同时注意pip安装时可能涉及本地编译确保设备上有基本的编译工具链build-essential。2.3 B站API接口分析与逆向这是项目最具挑战性的部分。B站没有公开的、稳定的官方消息API文档供第三方使用。因此我们需要通过“抓包”的方式分析B站客户端或网页端的网络请求找到获取未读消息的那个关键请求。常用方法使用浏览器开发者工具在电脑浏览器上登录B站网页版打开“网络(Network)”选项卡筛选XHR/Fetch请求。然后点击页面上的“消息”图标观察此时产生了哪些网络请求。寻找包含“unread”、“message”、“count”等关键词的请求。使用抓包工具如Charles、Fiddler或mitmproxy对手机App的流量进行抓包分析。这种方法能获取到更接近原生App的API。经过分析我找到了核心接口请注意B站的接口路径和参数可能随时变更以下为示例接口地址https://api.bilibili.com/x/msgfeed/unread请求方法GET必要认证需要在请求头Headers中携带用户的登录凭证Cookie。Cookie的获取与安全获取在浏览器登录B站后从开发者工具的“应用(Application)” - “Cookies”中可以找到SESSDATA、bili_jct、DedeUserID等关键字段。将这些字段组合成Cookie字符串。安全警告Cookie等同于你的账号密码绝对不要直接硬编码在脚本里更不要上传到公开的代码仓库如GitHub。正确做法将Cookie存储在开发板本地的一个配置文件如config.json中并设置该文件权限为仅当前用户可读chmod 600 config.json。进阶思路可以考虑实现一个简单的OAuth2授权流程但这对于个人脚本来说过于复杂。更务实的做法是定期手动更新Cookie。3. Python脚本核心实现详解有了前面的铺垫我们来一步步构建脚本。我将脚本命名为bilibili_monitor.py。3.1 配置文件与常量定义首先创建一个config.json文件来存放敏感信息和配置{ cookies: { SESSDATA: 你的SESSDATA值, bili_jct: 你的bili_jct值, DedeUserID: 你的DedeUserID }, check_interval_seconds: 300, led_path: /sys/class/leds/board:green:user/brightness }check_interval_seconds是轮询间隔设为300秒5分钟是一个比较友好的频率既不会错过重要消息也不会对B站服务器造成骚扰。在Python脚本开头我们读取配置并定义常量import json import requests import time import logging from pathlib import Path # 读取配置 CONFIG_PATH Path.home() / .config / bilibili_monitor / config.json with open(CONFIG_PATH, r) as f: config json.load(f) COOKIES config[cookies] CHECK_INTERVAL config[check_interval_seconds] LED_PATH config.get(led_path) # 硬件相关可能为空 # API地址 (示例需根据实际情况调整) API_UNREAD https://api.bilibili.com/x/msgfeed/unread HEADERS { User-Agent: Mozilla/5.0 (X11; Linux riscv64) AppleWebKit/537.36, Referer: https://www.bilibili.com/ } # 设置日志 logging.basicConfig( levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s, handlers[ logging.FileHandler(/var/log/bilibili_monitor.log), logging.StreamHandler() ] ) logger logging.getLogger(__name__)3.2 消息获取与解析函数这是脚本的核心函数负责请求API并解析未读数。def get_unread_count(): 获取B站未读消息总数 返回: 成功返回未读数(int)失败返回-1 try: # 构造Cookie字符串 cookie_str ; .join([f{k}{v} for k, v in COOKIES.items()]) headers HEADERS.copy() headers[Cookie] cookie_str response requests.get(API_UNREAD, headersheaders, timeout10) response.raise_for_status() # 检查HTTP错误 data response.json() # B站API返回格式通常为 {code:0, data:{...}, message:0} if data.get(code) 0: # 需要根据实际API返回结构解析这里是一个示例 # 假设返回的data结构为: {at: 2, reply: 5, like: 1, ...} unread_data data.get(data, {}) # 计算所有类型的未读消息总和 total_unread sum([v for v in unread_data.values() if isinstance(v, int)]) logger.info(f未读消息总数: {total_unread}) return total_unread else: logger.error(fAPI返回错误: {data.get(message)}) return -1 except requests.exceptions.RequestException as e: logger.error(f网络请求失败: {e}) return -1 except json.JSONDecodeError as e: logger.error(fJSON解析失败: {e}) return -1 except KeyError as e: logger.error(f解析API数据结构时键错误: {e}) return -1实操心得B站的API返回结构可能非常复杂且嵌套很深。务必使用logger详细记录下完整的返回数据data变量然后仔细分析其结构找到真正的未读数字段。我在这里就踩过坑最初只解析了第一层漏掉了嵌套在更深层的未读数。3.3 硬件反馈控制函数根据未读数控制开发板上的硬件给出反馈。这里以实现最简单的LED闪烁为例。def hardware_feedback(unread_count): 根据未读数控制硬件反馈 :param unread_count: 未读消息数 if not LED_PATH or unread_count 0: return led_file Path(LED_PATH) if not led_file.exists(): logger.warning(fLED路径不存在: {LED_PATH}) return try: if unread_count 0: # 没有未读熄灭LED led_file.write_text(0) logger.debug(LED已熄灭) else: # 有未读让LED闪烁 # 闪烁模式快速闪烁几次后长亮循环 for _ in range(min(unread_count, 5)): # 最多闪烁5次 led_file.write_text(1) time.sleep(0.2) led_file.write_text(0) time.sleep(0.2) # 闪烁结束后如果还有未读则长亮作为持续提醒 if unread_count 0: led_file.write_text(1) logger.debug(fLED已根据未读数 {unread_count} 进行反馈) except IOError as e: logger.error(f控制LED时发生IO错误: {e})扩展思路如果你连接了OLED屏幕可以在这个函数里添加显示逻辑比如调用Adafruit_SSD1306库在屏幕上绘制一个简单的界面显示“B站未读X”。3.4 主循环与异常处理最后将上述函数组合起来形成一个持续运行的主循环。def main(): logger.info(B站消息监测脚本启动) last_unread 0 while True: try: current_unread get_unread_count() if current_unread 0: # 获取成功 if current_unread ! last_unread: logger.info(f未读消息数发生变化: {last_unread} - {current_unread}) hardware_feedback(current_unread) last_unread current_unread else: logger.debug(f未读消息数未变: {current_unread}) else: logger.warning(获取未读数失败本次跳过硬件反馈) # 等待下一个检查周期 time.sleep(CHECK_INTERVAL) except KeyboardInterrupt: logger.info(检测到键盘中断脚本退出) # 退出前确保LED熄灭 if LED_PATH: try: Path(LED_PATH).write_text(0) except: pass break except Exception as e: logger.critical(f主循环发生未预期错误: {e}, exc_infoTrue) # 发生严重错误等待更长时间后重试避免疯狂刷日志 time.sleep(CHECK_INTERVAL * 3) if __name__ __main__: main()注意事项time.sleep(CHECK_INTERVAL)是简单的定时方式。对于更精确的定时任务可以考虑使用schedule库或者系统的cron来定时执行脚本。但使用主循环sleep的方式可以保持硬件反馈状态的持续性比如LED长亮。4. 部署、优化与问题排查脚本写好了接下来就是把它放到开发板上跑起来并让它稳定可靠地运行。4.1 系统服务化部署我们不希望一直开着一个终端运行python3 bilibili_monitor.py。最好的方式是将其注册为一个系统服务如systemd服务实现开机自启和后台运行。创建服务文件在开发板上创建/etc/systemd/system/bilibili-monitor.service[Unit] DescriptionBilibili Unread Message Monitor Afternetwork.target [Service] Typesimple Userpi # 替换为你的用户名如debian WorkingDirectory/home/pi/bilibili_monitor # 替换为你的脚本所在目录 ExecStart/usr/bin/python3 /home/pi/bilibili_monitor/bilibili_monitor.py Restarton-failure RestartSec10 StandardOutputjournal StandardErrorjournal [Install] WantedBymulti-user.target启用并启动服务sudo systemctl daemon-reload sudo systemctl enable bilibili-monitor.service sudo systemctl start bilibili-monitor.service查看服务状态和日志sudo systemctl status bilibili-monitor.service sudo journalctl -u bilibili-monitor.service -f4.2 性能优化与资源考量在资源受限的开发板上运行长期服务需要注意资源使用。内存requests库会占用一些内存。如果内存非常紧张如只有256MB可以考虑使用更轻量的urequestsMicroPython版或http.client标准库但会牺牲易用性。CPU与网络轮询间隔CHECK_INTERVAL是关键。太短如10秒会增加CPU和网络负担也可能触发B站的风控机制。5-10分钟是一个比较合理的区间。日志管理日志文件会不断增长。需要配置日志轮转logrotate。可以编辑/etc/logrotate.d/bilibili-monitor/var/log/bilibili_monitor.log { daily rotate 7 compress delaycompress missingok notifempty create 644 root root }4.3 常见问题与排查实录在实际运行中你可能会遇到以下问题问题1脚本运行后日志显示“API返回错误-401”或“未登录”。原因Cookie失效了。B站的登录状态Cookie通常有一定有效期也可能因为异地登录等安全原因失效。解决重新在浏览器登录B站获取新的Cookie值更新config.json文件然后重启服务sudo systemctl restart bilibili-monitor.service。问题2LED灯没有任何反应。排查步骤检查路径首先确认LED_PATH配置是否正确。可以通过ls /sys/class/leds/命令查看可用的LED设备。手动测试在终端执行echo 1 | sudo tee /sys/class/leds/你的led路径/brightness看LED是否能亮起。检查权限运行服务的用户如pi是否有权限写入LED文件通常需要root权限或用户属于gpio组。可以在服务文件中将Userroot或者将LED设备文件权限改为可写sudo chmod 666 /sys/class/leds/.../brightness不推荐安全性低。查看脚本日志sudo journalctl -u bilibili-monitor.service查看是否有权限错误。问题3脚本运行一段时间后CPU或内存占用异常高。可能原因内存泄漏或某个异常导致循环卡死。排查使用top或htop命令查看进程资源占用。检查日志中是否有大量重复的错误信息。尝试在脚本的main循环中在每次循环开始和结束时打印简单日志观察循环是否正常。考虑在hardware_feedback或get_unread_count函数中增加超时设置防止某个操作阻塞整个进程。问题4网络不稳定导致请求频繁失败。优化在requests.get中已经设置了timeout10。可以进一步增加重试机制。使用requests的适配器或者urllib3的重试功能from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry session requests.Session() retries Retry(total3, backoff_factor1, status_forcelist[500, 502, 503, 504]) session.mount(https://, HTTPAdapter(max_retriesretries)) # 然后用session.get代替requests.get5. 项目扩展与进阶玩法这个基础框架搭建好后你可以根据自己的想法进行无限扩展多平台聚合不止B站可以同时监测邮箱、GitHub通知、其他社交平台消息等让开发板成为一个统一的消息中心。更丰富的硬件反馈OLED屏幕显示显示未读消息的详细分类我的、回复、赞。RGB LED用不同颜色代表不同消息类型如蓝色代表回复红色代表。蜂鸣器有重要消息如时发出短促提示音。连接智能家居通过MQTT协议让家里的智能灯变色比如收到UP主更新时灯光变成B站蓝。消息过滤与优先级在脚本中增加逻辑只对特定UP主的、特定关键词的评论进行提醒忽略普通的点赞通知。云端状态同步将未读状态上传到私有云如Home Assistant方便在手机或电脑上远程查看。降低功耗模式如果使用电池供电可以设计成平时深度睡眠每隔一段时间唤醒检查检查完继续睡眠。这个项目麻雀虽小五脏俱全。它串联起了网络爬虫、API逆向、嵌入式Linux编程、硬件交互、系统服务部署等多个环节。最重要的是它解决了一个真实的小痛点并且整个过程充满了动手的乐趣。当你第一次看到开发板上的LED灯因为B站的一条新消息而闪烁起来时那种“自己造轮子”的成就感是直接用现成App无法比拟的。