1. 从浏览器 Network 面板看懂 dy web 端直播弹幕请求
dy web 端直播间里,弹幕是一条条实时刷出来的,但你在 Network 面板里点开某条请求,Response 里看到的往往是一堆乱码或者二进制。这不是接口坏了,而是它用了 protobuf 作为载荷格式。protobuf 是什么?你可以把它理解成一种「压缩过的结构化数据」——字段名不写在数据里,只写字段编号和值,接收方靠一份 .proto 描述文件把编号翻译回字段名。它能做什么?让弹幕这种高频、小体积的消息传输更省带宽。适合谁看?想搞懂 web 端实时消息链路、想学 protobuf 逆向分析流程的开发者。
我试过直接盯着那串二进制看,完全没头绪,后来才意识到得先找到「谁在解码」。浏览器能显示弹幕,说明前端 JS 里一定有一段代码把二进制还原成了对象。所以逆向的第一步不是硬啃字节,而是顺着请求找到解码入口,再反推字段结构。这篇就按这个思路走:先定位弹幕请求,再抓 protobuf 载荷,然后用脚本还原字段,最后跑一次完整的消息验证。整个过程只做学习分析,不涉及任何绕过平台限制的操作。
需要提前说明一点:web 端直播间的可用性和你本地网络环境有关,如果页面本身加载异常,先确认基础网络能正常访问页面,再谈抓包。下面所有操作都在浏览器开发者工具和本地 Python 环境里完成。
2. TaoToken 前置准备:给逆向脚本配一个稳定的模型调用入口
逆向过程中经常需要让脚本或助手帮你解释一段 JS、补全 .proto 字段、或者把还原出来的 JSON 做二次分析。这时候如果每次都要手动切模型、管密钥,节奏很容易断。我的做法是先把调用入口统一好,后面写解码脚本时直接复用。
TaoToken 在这里的角色就是一个统一的 API 入口,兼容常见的模型调用格式。你可以在官网了解它的能力范围:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。真正写代码时用的是 API 地址:https://taotoken.net/api ,注意这个地址不带跟踪参数,直接填进配置即可。
如果你只是偶尔问几个问题,用模型对话页面就够了:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 。但如果你打算长期做逆向分析、写脚本、跑 Agent 辅助,建议直接上 Coding Plan,额度更稳:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。密钥在控制台里建:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,建完去 API Keys 页面复制:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。接入细节看文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。
这里要提醒一句:TaoToken 是模型调用入口,不是抓包工具,也不参与你和目标站点之间的任何网络请求。抓包、解码这些事仍然在你自己浏览器和本地脚本里完成。
3. 可复制配置:抓包过滤条件与 protobuf 解码脚本骨架
3.1 浏览器侧定位弹幕请求
打开直播间,按 F12 进 Network 面板,勾选 Fetch/XHR,然后在过滤框里输入关键词。弹幕相关的请求路径通常带有im、fetch、message这类字样,你可以先用下面的过滤条件缩小范围:
im/fetch如果列表还是太多,就按请求类型再筛一层,只看 POST,并且按 Size 排序,弹幕长连接拉取的响应体通常体积不大但会周期性出现。找到候选请求后,点开看 Response,如果显示的是乱码或提示二进制,基本可以确认是 protobuf。
请求参数里几个关键字段的作用可以对照下表理解:
| 字段 | 作用 | 备注 |
|---|---|---|
| resp_content_type | 声明响应格式 | 值为 protobuf 时说明返回二进制 |
| room_id | 直播间房间号 | 需先通过另一个接口获取 |
| cursor | 翻页游标 | 从上一包 protobuf 响应里取 |
| internal_ext | 内部扩展字段 | 同样来自响应体 |
| msToken | 会话令牌 | 短时间内可复用 |
| X-Bogus | 请求签名 | 由前面若干字段参与计算 |
| _signature | 签名参数 | 实测不校验,可忽略 |
3.2 用 Python 写一个 protobuf 解码骨架
确认是 protobuf 后,本地装好依赖。如果运行时报AttributeError: 'NoneType' object has no attribute 'message_types_by_name',执行下面这条命令升级即可:
pip install --upgrade protobuf解码脚本的骨架长这样,核心思路是把抓到的二进制载荷喂给解析器,再按字段编号逐层展开:
import requests from google.protobuf.descriptor_pb2 import FileDescriptorProto # 这里放你从 JS 里还原出来的 .proto 描述 # 实际使用时用 protoc 编译成 _pb2.py 后 import # from dy_im_pb2 import Response def decode_payload(raw_bytes): resp = Response() resp.ParseFromString(raw_bytes) return resp def walk_message(msg, depth=0): indent = " " * depth for field, value in msg.ListFields(): if hasattr(value, "ListFields"): print(f"{indent}{field.name}:") walk_message(value, depth + 1) else: print(f"{indent}{field.name}: {value}") if __name__ == "__main__": # raw 为从 Network 面板复制出来的二进制响应 with open("danmu.bin", "rb") as f: raw = f.read() result = decode_payload(raw) walk_message(result)3.3 还原 .proto 字段结构
在浏览器 Sources 面板全局搜索protobuf或decode,找到解码函数后打断点,单步跟进去就能看到它读取了哪些字段编号。把这些编号和你在响应里看到的值对应起来,写成 .proto 文件。下面是一段简化后的结构示例:
syntax = "proto3"; message Response { repeated Message messages = 1; string cursor = 2; string internal_ext = 3; } message Message { string method = 1; bytes payload = 2; int64 msg_id = 3; } message ChatMessage { string content = 1; User user = 2; } message User { int64 id = 1; string nickname = 2; }字段编号不是猜的,是跟着断点里实际读取的 tag 值填的。填错编号会导致解析出来的字段名对不上,但值还在,这时候回头核对断点即可。
4. 验证请求:跑一次完整的弹幕消息还原
配置写好后,先做一次最小验证。把 Network 里抓到的那条弹幕响应体保存成danmu.bin,然后运行第 3 节的脚本。如果 .proto 结构正确,你会看到类似下面的输出:
messages: method: WebcastChatMessage payload: content: 主播好厉害 user: id: 123456789 nickname: 某用户 cursor: CjRiYW... internal_ext: CnRi...看到content和nickname被正确还原,说明字段映射是对的。接下来做一次动态验证:保持脚本运行,在直播间发一条测试弹幕,重新抓一包,确认新消息也能被解析出来。这一步能验证你的字段结构不是只对某一条数据生效。
如果你想把还原结果接到模型做进一步分析,比如统计弹幕情绪或提取关键词,可以在脚本里把解析出的 JSON 通过 TaoToken 的 API 发出去。Base URL 填https://taotoken.net/api,Key 用你在 API Keys 页面建的那个,Model ID 按文档里支持的模型名填。三件套齐了就能跑通。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth
做这类逆向分析,报错基本集中在几个地方,对照着排会快很多。
401 Unauthorized:如果你是在调用模型接口时遇到,先检查 API Key 是否复制完整、有没有多余空格,以及 Base URL 是不是写成了带路径的地址。TaoToken 的 API 地址就是https://taotoken.net/api,不要自己拼/v1之类的后缀,具体以文档为准。
local proxy failed:这个通常出现在你本地开了某些网络工具的情况下。web 端直播页面本身对网络环境敏感,如果页面加载异常,先关掉本地代理类工具再刷新页面。注意这里说的是本地网络配置问题,不是让你去用什么特殊手段。
reading choices或解析时报字段缺失:多半是 .proto 字段编号填错了,或者响应体保存时被截断。重新抓一次完整响应,核对断点里的 tag 值。另外确认pip install --upgrade protobuf已经执行过,版本过低会导致解析器初始化失败。
OAuth相关报错:如果你用的是 Claude Code 这类工具接入,认证方式要选对。走 API Key 模式时,Base URL、Key、Model ID 三件套必须齐全,缺一个都会在握手阶段失败。Claude Code 的接入配置可以参考文档里的说明:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。
还有一个容易忽略的点:cursor和internal_ext必须从上一包响应里取,不能写死。写死的话第一包能过,第二包就会返回空或者报错。
6. 语义 CTA:把逆向流程沉淀成可复用的分析链路
整套流程跑通后,你会发现真正花时间的不是解码本身,而是定位解码入口和还原字段结构。这两步一旦完成,后面就是重复劳动。我的建议是把 .proto 文件和解析脚本放进同一个仓库,每次遇到新消息类型时只增量补字段,不要每次从头抓。
如果你后续想让脚本自动解释 JS 片段、补全 .proto、或者对还原出的弹幕数据做批量分析,可以用 TaoToken 的模型对话快速验证想法:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 。要长期跑自动化分析任务,Coding Plan 更合适:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。密钥统一在 API Keys 页面管理:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。
最后留一个实用技巧:抓包时把cursor的变化过程打印出来,能帮你判断长连接是否正常续包。如果 cursor 一直不变,说明请求参数有问题,先回去检查internal_ext有没有正确带上。