
抖音直播弹幕抓取DouyinLiveWebFetcher 从原理到生产的实战笔记【免费下载链接】DouyinLiveWebFetcher抖音直播间网页版的弹幕数据抓取2025最新版本项目地址: https://gitcode.com/gh_mirrors/do/DouyinLiveWebFetcher 为什么竞品直播间的弹幕总是晚你 5 秒运营说我想实时盯住一个竞品直播间的弹幕出现指定关键词就报警HTTP 轮询在这件事上就是不行间隔 5 秒已经算激进等数据落到你手里主播都换完话题了。抖音直播弹幕抓取想做到实时就得听服务器主动推送。DouyinLiveWebFetcher 干的就是这件事它接上抖音直播的 WebSocket 通道每条弹幕到达时立刻输出。 一条数据是怎么跑到你手里的完整链路拿到 live_id直播页地址栏里的那串数字之后程序依次做了这些事核心逻辑都在 liveMan.py 里换身份先请求抖音直播首页从响应 cookie 里拿到 ttwid。这是网页版的设备标识后面所有请求都带着它。换 IDURL 里的 live_id 不是系统内部用的 room_id。程序把直播间页面 HTML 抓下来用正则提取roomId\:\后面的数字。拼推送地址固定网关地址加上几十个浏览器、设备参数和 room_id。地址里有个compressgzip说明后面的数据流是压缩的。签名URL 末尾要挂一个签名参数不对的话服务器直接拒掉握手怎么生成的第 4 章细讲。握手与心跳用 websocket-client 维持连接连上后另起一个线程每 5 秒发一个 protobuf 心跳帧。原因是链路上的中间设备会掐掉空闲 TCP 连接心跳就是那句别关我。收帧服务器推过来的是二进制帧外层是 PushFrame带 log_id解压 payload 后得到 Response里面装着本次这一批 Message。路由Message.method 是个WebcastChatMessage这样的字符串程序用一张 dict 把它分发给对应的解析函数。说白了这套链路值得学的地方有两个。一是用推代替拉直播弹幕是高频低价值数据轮询又费带宽又总是慢半拍长连接一次握手服务端有事就发。二是用二进制代替 JSON压缩加编码之后带宽小很多字段结构则被 protobuf/douyin.py 里的 schema 锁死由 douyin.proto 经 protoc 生成双方按编号对齐字段不靠字符串猜。 5 分钟跑通你的第一个抖音直播 WebSocket 采集器一共 5 步。备环境Python 3.7 起步。建议顺手装好 Node.js断线回调里的房间状态检查会执行 a_bogus.js它靠 execjs 调 Node不装的话弹幕采集本身不受影响只是断线时的状态检查会报错。拿代码git clone https://gitcode.com/gh_mirrors/do/DouyinLiveWebFetcher装依赖pip install -r requirements.txt主要是 requests、websocket-client、betterproto、mini_racer 这几个。找 live_id打开一个正在直播的直播间地址栏里域名后面的数字就是。运行from liveMan import DouyinLiveWebFetcher live_id 261378947940 # 地址栏里的数字 room DouyinLiveWebFetcher(live_id) room.start()示例 live_id 可能已经下播换成你手头正在播的房间即可。实测下来连接建立后几秒终端就开始滚动【进场msg】[3548874980203464][男]姚先生 进入了直播间 【礼物msg】X L 送出了 为你点亮x1 【统计msg】当前观看人数: 22164, 累计观看人数: 43.6万 【聊天msg】[67197561586]说谎: 去拿 去拿去哪 【粉丝团msg】恭喜 安好 成为粉丝团第289687名成员 两个核心机制签名校验与 Protobuf 帧的读法签名不破解直接跑wss 地址带几十个参数服务端会校验其中的签名不对就拒掉握手。这里有个坑参数不是按 URL 原顺序参与计算的而是按一套固定顺序取 13 个字段拼成kv,kv再取 MD5然后交给 JS 算出最终签名。项目的思路不是把混淆 JS 手工翻成 Python而是原样跑params (live_id,aid,version_code,...identity).split(,) ... md5_param hashlib.md5(param.encode()).hexdigest() ctx MiniRacer() ctx.eval(script) signature ctx.call(get_sign, md5_param)JS 文件是 sign.js。取舍在于算法本体在 JS 里抖音哪天更新了页面脚本你就得抓新版本替换——这是长期对抗不是一劳永逸。MiniRacer 把 V8 引擎塞进 Python 进程里跑好处是不依赖 Node 环境仓库里另一个 ac_signature.py 是纯 Python 实现的老接口签名属于失效的 HTTP 链路start() 主流程用不到读源码时别被它带偏。Protobuf一帧二进制怎么拆开收帧逻辑是典型的信封套信封package PushFrame().parse(message) response Response().parse(gzip.decompress(package.payload)) ... for msg in response.messages_list: handlers.get(msg.method)(msg.payload)外层 PushFrame 带 log_id解压 payload 得到 Response。有个容易漏的细节Response 里有need_ack字段为真时必须把同一个 log_id 原样回发一帧否则服务端会停推——断流排查时先查这里。另一个取舍在容错上解析层外面包着 try/except未知或结构对不上的消息会被静默丢弃不会让整条连接崩掉。代价是新增消息类型时你不会有报错提示。想支持一个新的WebcastXxxMessage动作是三步在 douyin.proto 里补 message 定义、重新生成 douyin.py、在路由 dict 里加一个解析函数proto 文件都在 protobuf/ 目录下。️ 7×24 生产你实际会撞上什么先说直白点断线之后库的 on_close 只打一行日志run_forever 随之返回。想让它过夜运行外壳得自己搭。问题症状解法断线后静默停摆进程活着终端不再输出外层 while 循环重新调 start()5 秒起步指数退避设上限超限后再取一次房间状态确认是否下播下播后连接空挂没数据了心跳还在发_parseControlMsg 里 status 为 3 时主动 stop()外层循环看到就别再重连内存缓慢上涨解析结果存进了 list 越攒越多边收边落盘不缓冲历史缓冲区设长度上限日志文件膨胀跑一天几十 MB 纯文本RotatingFileHandler 轮转10MB 滚 5 份库目前用 print 直接输出做 7×24 日志治理时把 print 换成 logger 即可import logging import logging.handlers logger logging.getLogger(fetcher) handler logging.handlers.RotatingFileHandler( fetcher.log, maxBytes10 * 1024 * 1024, backupCount5, encodingutf-8) logger.addHandler(handler) logger.setLevel(logging.INFO) 边界与扩展方向先说不适合谁。每个房间就是一条独立 WebSocket 连接资源开销随房间数线性涨几十个房间可以扛上百个就该拆多进程或多台机器别硬塞。它也不是历史数据回补工具推送通道只给你连接建立之后的数据URL 里的 cursor 参数虽然能拉历史但项目没封装想用得自己实现。还有一点README 里写得很清楚代码只用于学习研究交流拿它做商业采集或骚扰平台风险自己扛。思路是可迁移的只要目标平台是网页 推送通道 二进制协议同一套动作都能复用——先抓包找到 wss 地址再定位参与签名的参数最后拿一帧数据反推 proto 结构。两个改动量都不大的扩展方向。一是接消息队列把解析函数里的 print 换成queue.put(event)让消费进程写 Kafka 或 Elasticsearch采集主循环和存储解耦下游卡死不会拖垮连接。二是多房间聚合每个房间一个 fetcher 加一个线程所有房间汇入同一个 sink下游只需要认识一种统一的事件结构。✍️ 写在最后开头那个 5 秒延迟的痛点本质是协议选型问题上了推送通道之后弹幕到达的时间就只剩服务端推送那一份延迟。你剩下的工作是在链路上别掉帧——签名别过期、连接断了要拉回来、内存别涨上去。把 main.py 里的 live_id 换成你要盯的房间跑起来这件事就算成了。【免费下载链接】DouyinLiveWebFetcher抖音直播间网页版的弹幕数据抓取2025最新版本项目地址: https://gitcode.com/gh_mirrors/do/DouyinLiveWebFetcher创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考