当前位置: 首页 > news >正文

基于RISC-V开发板的B站消息监测终端:Python脚本与硬件交互实践

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的版本。选择它主要基于几点考虑:

  1. 性能足够:监测脚本本身计算量不大,但网络请求和JSON解析需要一定的CPU资源。这款板子的性能足以流畅运行一个完整的Linux发行版(如Debian)和Python环境。
  2. GPIO与外围设备:板子自带用户可编程的LED灯,并且通过排针引出了标准的GPIO、I2C、SPI等接口,方便后续扩展(例如连接OLED屏幕)。
  3. 社区与生态:虽然相比树莓派,RISC-V的生态还在成长,但这款板子的Linux系统镜像、基础驱动支持都比较完善,降低了底层折腾的成本。

当然,如果你手头有树莓派、香橙派或者其他ARM开发板,这个项目完全可以无缝迁移,原理和代码几乎通用。

2.2 软件栈与依赖库解析

脚本的核心逻辑在Python 3上实现。主要依赖以下几个库:

  • requests:用于发送HTTP请求到B站API。这是最核心的网络库,比标准库的urllib更简洁易用。
  • json:用于解析B站API返回的JSON格式数据。
  • time/datetime:用于实现定时任务和日志时间戳。
  • logging:用于记录脚本运行日志,便于排查问题。
  • 硬件控制库:根据你的开发板型号和要驱动的设备而定。
    • 如果只是控制板载LED,Linux下通常可以通过读写/sys/class/leds/目录下的文件来实现,无需额外库。
    • 如果需要驱动I2C的OLED屏幕,可能需要smbus2Adafruit_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站客户端或网页端的网络请求,找到获取未读消息的那个关键请求。

常用方法

  1. 使用浏览器开发者工具:在电脑浏览器上登录B站网页版,打开“网络(Network)”选项卡,筛选XHR/Fetch请求。然后点击页面上的“消息”图标,观察此时产生了哪些网络请求。寻找包含“unread”、“message”、“count”等关键词的请求。
  2. 使用抓包工具:如Charles、Fiddler或mitmproxy,对手机App的流量进行抓包分析。这种方法能获取到更接近原生App的API。

经过分析,我找到了核心接口(请注意,B站的接口路径和参数可能随时变更,以下为示例):

  • 接口地址https://api.bilibili.com/x/msgfeed/unread
  • 请求方法:GET
  • 必要认证:需要在请求头(Headers)中携带用户的登录凭证(Cookie)。

Cookie的获取与安全

  • 获取:在浏览器登录B站后,从开发者工具的“应用(Application)” -> “Cookies”中,可以找到SESSDATAbili_jctDedeUserID等关键字段。将这些字段组合成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( level=logging.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, headers=headers, timeout=10) 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(f"API返回错误: {data.get('message')}") return -1 except requests.exceptions.RequestException as e: logger.error(f"网络请求失败: {e}") return -1 except json.JSONDecodeError as e: logger.error(f"JSON解析失败: {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(f"LED路径不存在: {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(f"LED已根据未读数 {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_info=True) # 发生严重错误,等待更长时间后重试,避免疯狂刷日志 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服务),实现开机自启和后台运行。

  1. 创建服务文件:在开发板上创建/etc/systemd/system/bilibili-monitor.service
    [Unit] Description=Bilibili Unread Message Monitor After=network.target [Service] Type=simple User=pi # 替换为你的用户名,如debian WorkingDirectory=/home/pi/bilibili_monitor # 替换为你的脚本所在目录 ExecStart=/usr/bin/python3 /home/pi/bilibili_monitor/bilibili_monitor.py Restart=on-failure RestartSec=10 StandardOutput=journal StandardError=journal [Install] WantedBy=multi-user.target
  2. 启用并启动服务
    sudo systemctl daemon-reload sudo systemctl enable bilibili-monitor.service sudo systemctl start bilibili-monitor.service
  3. 查看服务状态和日志
    sudo systemctl status bilibili-monitor.service sudo journalctl -u bilibili-monitor.service -f

4.2 性能优化与资源考量

在资源受限的开发板上运行长期服务,需要注意资源使用。

  • 内存requests库会占用一些内存。如果内存非常紧张(如只有256MB),可以考虑使用更轻量的urequests(MicroPython版)或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

问题2:LED灯没有任何反应。

  • 排查步骤
    1. 检查路径:首先确认LED_PATH配置是否正确。可以通过ls /sys/class/leds/命令查看可用的LED设备。
    2. 手动测试:在终端执行echo 1 | sudo tee /sys/class/leds/你的led路径/brightness看LED是否能亮起。
    3. 检查权限:运行服务的用户(如pi)是否有权限写入LED文件?通常需要root权限或用户属于gpio组。可以在服务文件中将User=root,或者将LED设备文件权限改为可写:sudo chmod 666 /sys/class/leds/.../brightness(不推荐,安全性低)。
    4. 查看脚本日志sudo journalctl -u bilibili-monitor.service查看是否有权限错误。

问题3:脚本运行一段时间后,CPU或内存占用异常高。

  • 可能原因:内存泄漏或某个异常导致循环卡死。
  • 排查
    1. 使用tophtop命令查看进程资源占用。
    2. 检查日志中是否有大量重复的错误信息。
    3. 尝试在脚本的main循环中,在每次循环开始和结束时打印简单日志,观察循环是否正常。
    4. 考虑在hardware_feedbackget_unread_count函数中增加超时设置,防止某个操作阻塞整个进程。

问题4:网络不稳定导致请求频繁失败。

  • 优化:在requests.get中已经设置了timeout=10。可以进一步增加重试机制。使用requests的适配器或者urllib3的重试功能:
    from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry session = requests.Session() retries = Retry(total=3, backoff_factor=1, status_forcelist=[500, 502, 503, 504]) session.mount('https://', HTTPAdapter(max_retries=retries)) # 然后用session.get代替requests.get

5. 项目扩展与进阶玩法

这个基础框架搭建好后,你可以根据自己的想法进行无限扩展:

  1. 多平台聚合:不止B站,可以同时监测邮箱、GitHub通知、其他社交平台消息等,让开发板成为一个统一的消息中心。
  2. 更丰富的硬件反馈
    • OLED屏幕显示:显示未读消息的详细分类(@我的、回复、赞)。
    • RGB LED:用不同颜色代表不同消息类型(如蓝色代表回复,红色代表@)。
    • 蜂鸣器:有重要消息(如@)时发出短促提示音。
    • 连接智能家居:通过MQTT协议,让家里的智能灯变色(比如收到UP主更新时,灯光变成B站蓝)。
  3. 消息过滤与优先级:在脚本中增加逻辑,只对特定UP主的@、特定关键词的评论进行提醒,忽略普通的点赞通知。
  4. 云端状态同步:将未读状态上传到私有云(如Home Assistant),方便在手机或电脑上远程查看。
  5. 降低功耗模式:如果使用电池供电,可以设计成平时深度睡眠,每隔一段时间唤醒检查,检查完继续睡眠。

这个项目麻雀虽小,五脏俱全。它串联起了网络爬虫、API逆向、嵌入式Linux编程、硬件交互、系统服务部署等多个环节。最重要的是,它解决了一个真实的小痛点,并且整个过程充满了动手的乐趣。当你第一次看到开发板上的LED灯因为B站的一条新消息而闪烁起来时,那种“自己造轮子”的成就感,是直接用现成App无法比拟的。

http://www.jsqmd.com/news/847958/

相关文章:

  • 基于Arduino与CC3000的便携式WiFi探测器:硬件选型、低功耗设计与实践
  • PNPM依赖管理实战:从`outdated`发现漏洞到`update`精准修复的安全升级指南
  • Codex CLI 与 Cursor 双工具联动:3 步实现项目迁移、配置互通与能力互补
  • 微软与东南大学联手:让AI助手真正学会“拖拽“和“画图“
  • 从Wi-Fi信号变弱到高速PCB设计:S参数S21插入损耗到底在说什么?
  • 微信小程序自定义TabBar实战:从配置到隐藏,手把手教你打造个性化底部导航(附完整代码)
  • 大型工程重构×细节调试:OpenAI Codex CLI 与 Cursor 联动的 4 步落地流程
  • 2026北京旅游定制旅行社推荐:口碑性价比综合测评解析 - 品牌企业推荐师(官方)
  • 【Perplexity认证考试终极指南】:2024最新考纲解析、通过率数据与3天冲刺计划
  • 避坑指南:在Ubuntu 22.04上用Anaconda配置Vision-Mamba环境,解决‘bimamba_type‘报错
  • 别再死记命令了!用H3C模拟器搞定ACL配置,我踩过的坑你别踩(附实验拓扑)
  • 2026 Finder继电器总代理权威选型指南:官方授权正品供应商推荐 - 品牌企业推荐师(官方)
  • 别再手写WebSocket适配器了!用Websockify 5分钟搞定TCP服务Web化(附Python/JS客户端踩坑实录)
  • ECCV2020 ParSeNet论文精读与复现:手把手搭建你的3D点云参数化表面拟合环境
  • 如何彻底卸载Windows 10中的OneDrive:一键解决方案指南
  • 训练大模型太烧钱?Nous Research找到让AI“一目十行“的学习秘诀
  • 基于Raspberry Pi Pico与步进开关打造多功能桌面控制器
  • kindle 5.18.6 越狱经验贴
  • CircuitJS1电路仿真器:3天从零到精通的完整入门指南
  • Windows IoT Core远程配置开机自启动应用:PowerShell与IoTStartup实战指南
  • 终极指南:如何快速免费解决GBK到UTF-8编码转换难题
  • 告别C语言:利用CH9329与Lua脚本轻松打造USB自动化控制工具
  • 别再只盯着串口了!STM32F103C8T6的SWD下载电路,手把手教你画(附BOOT引脚配置详解)
  • 玩转OpenWrt旁路由:用LuCI界面+Shell命令双重监控局域网所有设备状态
  • 虚拟机共享文件挂载
  • CircuitPython实战:NeoPixel、I2C传感器与电容触摸的嵌入式交互开发
  • 嵌入式Linux无线AP搭建实战:hostapd与udhcpd配置详解
  • 别再手动改了!用Endnote X9/20一键搞定参考文献期刊缩写(附最新期刊缩写库下载)
  • 对比直接使用官方API体验Taotoken在容灾与路由上的优势
  • 2026年AOC有源线缆源头工厂测评推荐:全速率高速AOC选型指南 - 品牌企业推荐师(官方)