Star 历史趋势
数据来源: GitHub API · 生成自 Stargazers.cn
README.md

douyinLive

一个基于 WebSocket 的抖音直播弹幕抓取工具。

项目边界说明,请先阅读

本项目仅用于研究和记录抖音直播 WebSocket 链接的逆向获取、连接方式及基础数据接收流程。

本项目不承诺、也不负责保证任何具体业务消息一定能够收到或完整解析。包括但不限于:礼物消息收不到、某类消息缺失、字段无法解析、消息结构变化、个别直播间数据不完整等问题,均不在本项目维护范围内。

请不要提交“没有礼物消息”“某类消息解析不了”“为什么收不到某条消息”等相关 Issue。此类问题不会作为 Bug 处理,也不作为本项目后续适配目标。

它做的事很简单:

  1. 连接抖音直播间消息流
  2. 解析弹幕 / 礼物 / 点赞 / 进场等消息
  3. 再通过你本地启动的 WebSocket 服务把消息转发给你的客户端

适合两种用法:

  • 直接当成一个本地 WebSocket 服务用
  • 当成 Go 库集成到你自己的项目里

GitHub Release License Go Version

功能

  • 实时接收直播间消息
  • 支持单进程监听多个直播间
  • 支持弹幕、礼物、点赞、进场、关注等常见消息
  • 支持可选 Cookie,适配部分需要登录态的场景
  • 可作为独立服务运行,也可作为 Go 库使用
  • 内置断线重连和基础保活逻辑

它不是做什么的

这个项目主要是直播间消息抓取 / 转发,不是录播工具。

它不负责:

  • 下载 flv / m3u8 视频流
  • 录制直播画面
  • 保存回放

如果你要的是录播,应该看录制类项目;如果你要的是实时弹幕、礼物、互动消息,这个项目更合适。


快速开始

方式一:直接下载可执行文件

  1. 打开 Releases
  2. 下载对应平台的程序
  3. 运行程序

发布包名称会带上版本号和构建 commit,格式类似:

douyinLive-v2.0.3-abcdef123456-linux-amd64.tar.gz douyinLive-v2.0.3-abcdef123456-windows-amd64.zip

压缩包里的可执行文件名仍然固定为 douyinLive,所以脚本和 Docker 启动命令不需要因为 hash 变化而每次修改。

./douyinLive

程序启动后会在本地启动一个 WebSocket 服务,默认端口是 1088

然后你的客户端连接:

ws://127.0.0.1:1088/ws/直播间标识

例如:

ws://127.0.0.1:1088/ws/516466932480

方式二:源码编译

git clone https://github.com/jwwsjlm/douyinLive.git cd douyinLive go build -o douyinLive ./cmd/main ./douyinLive

查看当前二进制的构建信息:

./douyinLive --version

输出示例:

tag=v2.0.3 commit=abcdef123456 buildDate=2026-05-24T00:00:00Z source=github-actions/release#123.1

方式三:Docker 运行

1. 直接启动最新版镜像

docker run --rm -p 1088:1088 ghcr.io/jwwsjlm/douyinlive:latest

程序启动后,对外提供的 WebSocket 地址仍然是:

ws://127.0.0.1:1088/ws/直播间标识

如果你需要固定版本,也可以直接拉指定 tag:

docker run --rm -p 1088:1088 ghcr.io/jwwsjlm/douyinlive:v2.0.3

Docker 镜像也支持查看构建信息:

docker run --rm ghcr.io/jwwsjlm/douyinlive:v2.0.3 --version

2. 通过 Docker 挂载 config.yaml

如果你希望加载自定义配置,先在宿主机准备一个 config.yaml,再把它挂载到容器中的 /app/config.yaml,并通过 --config 显式传入:

docker run --rm -p 1088:1088 \ -v $(pwd)/config.yaml:/app/config.yaml:ro \ ghcr.io/jwwsjlm/douyinlive:latest --config /app/config.yaml

说明:

  • -v $(pwd)/config.yaml:/app/config.yaml:ro:把宿主机当前目录下的 config.yaml 挂载到容器内
  • :ro:只读挂载,避免容器误改宿主机配置
  • --config /app/config.yaml:显式指定程序读取这个配置文件

3. 持久化运行(推荐长期使用)

如果你希望容器长期后台运行,不要使用 --rm,建议改成:

docker run -d \ --name douyinlive \ --restart unless-stopped \ -p 1088:1088 \ -v $(pwd)/config.yaml:/app/config.yaml:ro \ ghcr.io/jwwsjlm/douyinlive:latest --config /app/config.yaml

这样即使容器被删除或重建,宿主机上的 config.yaml 仍然保留,达到配置持久化的效果。

4. 挂载整个目录(适合后续扩展)

如果你后续不只想挂一个配置文件,也可以直接挂整个目录:

mkdir -p ./data cp config.example.yaml ./data/config.yaml docker run -d \ --name douyinlive \ --restart unless-stopped \ -p 1088:1088 \ -v $(pwd)/data:/app/data \ ghcr.io/jwwsjlm/douyinlive:latest --config /app/data/config.yaml

这种方式更适合统一管理容器运行时使用到的文件。

5. 使用 Docker Compose(推荐)

项目已自带两个 compose 示例文件:

  • compose.yaml:挂载单个 config.yaml
  • compose.data.yaml:挂载整个 data 目录
方案 A:使用 compose.yaml

先准备配置文件:

cp config.example.yaml config.yaml

然后直接启动:

docker compose up -d docker compose logs -f docker compose down

compose.yaml 内容如下:

services: douyinlive: image: ghcr.io/jwwsjlm/douyinlive:latest container_name: douyinlive restart: unless-stopped ports: - "1088:1088" volumes: - ./config.yaml:/app/config.yaml:ro command: ["--config", "/app/config.yaml"]
方案 B:使用 compose.data.yaml

如果你想把配置统一收纳到目录里,先执行:

mkdir -p data cp config.example.yaml data/config.yaml

然后用下面命令启动:

docker compose -f compose.data.yaml up -d docker compose -f compose.data.yaml logs -f docker compose -f compose.data.yaml down

compose.data.yaml 内容如下:

services: douyinlive: image: ghcr.io/jwwsjlm/douyinlive:latest container_name: douyinlive restart: unless-stopped ports: - "1088:1088" volumes: - ./data:/app/data command: ["--config", "/app/data/config.yaml"]

此时你只需要保证宿主机存在:

./data/config.yaml

6. 常用查看命令

docker logs -f douyinlive docker ps docker stop douyinlive docker rm -f douyinlive

最重要的一点:房间参数怎么传

很多人第一次用会卡在这里。

这个程序启动时不需要在命令行传直播间号。

直播间标识是通过 WebSocket 路径传进去的:

ws://127.0.0.1:1088/ws/直播间标识

也就是说:

  • 程序只负责启动本地服务
  • 你连接哪个房间,是由 /ws/后面的内容 决定的

什么叫“直播间标识”

一般就是你访问下面这个地址时,后面的那段:

https://live.douyin.com/xxxxx

这里的 xxxxx 就是你应该传给 /ws/ 的内容。

例如:

  • https://live.douyin.com/516466932480
    • 则连接:ws://127.0.0.1:1088/ws/516466932480

如果你传的是无效标识,服务端会关闭这个连接。

如果直播间暂时未开播:

  • 本地 WebSocket 连接会保留
  • 服务端会先返回一条“直播间未开播”的状态通知
  • 然后按配置的时间间隔持续推送未开播状态
  • 一旦检测到开播,就自动切回正常消息流

运行方式

CLI 完整示例(推荐先看这里)

douyinLive 启动后是一个本地 WebSocket 服务。直播间标识不是 CLI 启动参数,而是客户端连接 WebSocket 时写在 URL 里。

Linux / macOS

cp config.example.yaml config.yaml ./douyinLive --config ./config.yaml --port 1088 --log-level info

然后让你的客户端连接:

ws://127.0.0.1:1088/ws/516466932480

Windows PowerShell

Copy-Item .\config.example.yaml .\config.yaml .\douyinLive.exe --config .\config.yaml --port 1088 --log-level info

然后让你的客户端连接:

ws://127.0.0.1:1088/ws/516466932480

默认启动

如果不需要配置文件,也可以直接启动:

./douyinLive

Windows:

.\douyinLive.exe

默认行为:

  • 读取同目录下的 config.yaml(如果存在)
  • 如果没有配置文件,就使用默认值
  • 默认端口:1088
  • 默认日志级别:info

指定端口

./douyinLive --port 1088

Windows:

.\douyinLive.exe --port 1088

指定配置文件

./douyinLive --config ./config.yaml

Windows:

.\douyinLive.exe --config .\config.yaml

输出未知消息类型(调试用)

./douyinLive --unknown

Windows:

.\douyinLive.exe --unknown

设置日志级别

./douyinLive --log-level debug

Windows:

.\douyinLive.exe --log-level debug

支持 debuginfowarnerror,默认是 info。也可以写进配置文件:

log: level: "debug"

日志使用 Go slog 文本格式,会带上 leveltime 以及 room_idlive_iderr 等字段,方便长时间挂机时排查连接和重连状态。

查看版本和构建来源

./douyinLive --version

Windows:

.\douyinLive.exe --version

输出会包含:

  • tag:本次构建对应的 tag,本地手动构建默认为 dev
  • commit:构建时注入的短 commit hash
  • buildDate:构建时间
  • source:构建来源,例如 GitHub Actions 或本地构建

CLI 参数速查

--config string 指定配置文件路径,例如 ./config.yaml --port string 本地 WebSocket 服务端口,默认 1088 --unknown 输出未知 protobuf 消息类型,调试用 --log-level string 日志级别:debug、info、warn、error --version 输出版本和构建来源

配置文件

你可以创建一个 config.yaml 放在程序同目录下。

示例:

port: "1088" unknown: false log: level: "info" monitor: poll_interval: "15s" notify_interval: "30s" cookie: douyin: "" rooms: # "516466932480": "ttwid=...; sessionid=..."

项目里也自带了一个示例文件:

  • config.example.yaml

配置项说明

port

本地 WebSocket 服务端口。

默认值:

port: "1088"

unknown

是否打印未知消息类型。

默认值:

unknown: false

log.level

日志级别。默认输出 info 及以上级别,排查连接、心跳、重连问题时可以临时调整为 debug

默认值:

log: level: "info"

monitor.poll_interval

未开播时,服务端检查“是否已经开播”的时间间隔。

默认值:

monitor: poll_interval: "15s"

monitor.notify_interval

未开播时,服务端向本地 WebSocket 客户端重复推送状态通知的时间间隔。

默认值:

monitor: notify_interval: "30s"

客户端会收到类似:

{"type":"system","event":"live_status","live":false,"room_id":"516466932480","message":"直播间未开播","retry_interval_seconds":30}

cookie.douyin

抖音默认 Cookie,可选。

没有单独配置某个直播间的 Cookie 时,会优先回退到这里。再往后才是自动获取的逻辑。

cookie: douyin: "ttwid=...; sessionid=..."

cookie.rooms

按直播间 ID 单独配置 Cookie,可选。

如果你要同时监听多个直播间,而且它们对应不同账号、不同登录态,就可以在这里分别配置。没有配置到的直播间,会自动回退使用 cookie.douyin

cookie: douyin: "默认 Cookie" rooms: "516466932480": "直播间 516466932480 专用 Cookie" "123456789": "直播间 123456789 专用 Cookie" "888888888": "直播间 888888888 专用 Cookie"

一个更完整的例子:

port: "1088" unknown: false log: level: "info" monitor: poll_interval: "15s" notify_interval: "30s" cookie: douyin: "默认 Cookie" rooms: "516466932480": "room A 的 Cookie" "123456789": "room B 的 Cookie"

Cookie 优先级:

WebSocket 临时 Cookie > 直播间 Cookie(cookie.rooms) > 默认 Cookie(cookie.douyin) > 自动获取

WebSocket 临时 Cookie 仅建议临时调试使用:

ws://127.0.0.1:1088/ws/直播间ID?cookie_b64=BASE64URL_COOKIE

也支持直接传 URL 编码后的 Cookie:

ws://127.0.0.1:1088/ws/直播间ID?cookie=URL_ENCODED_COOKIE

什么时候需要 Cookie

不是所有场景都必须填 Cookie。

你可以先不填,直接跑。

如果出现下面这些情况,再考虑补 Cookie:

  • 某些直播间拿不到消息
  • 请求被限制
  • 页面返回结果异常
  • 需要更稳定的登录态

Cookie 怎么拿

  1. 浏览器打开:https://live.douyin.com
  2. 登录抖音
  3. F12
  4. 打开 Network
  5. 随便点一个请求
  6. 复制请求头里的 Cookie

然后填到:

cookie: douyin: "你的完整 Cookie"

作为 Go 库集成使用

你也可以直接把 douyinLive 作为 Go 库集成到你自己的项目中。

安装

go get github.com/jwwsjlm/douyinLive/v2

订阅接口怎么选

新版本推荐使用 LiveMessage 相关订阅接口:

  • SubscribeMessage(handler):订阅所有抖音消息
  • SubscribeMethod(method, handler):只订阅一个消息类型
  • SubscribeMethods(methods, handler):订阅多个消息类型

消息类型由抖音 WebSocket 下发的 method 字段决定,例如 WebcastChatMessageWebcastGiftMessageWebcastLikeMessage。也就是说,订阅分发不是靠结构体类型猜测,而是先看 method 字符串,再把匹配到的消息交给对应 handler。

LiveMessage 会同时带上原始消息、已解析消息和直播间元信息:

type LiveMessage struct { LiveID string RoomID string LiveName string Title string AvatarThumb string Raw *new_douyin.Webcast_Im_Message Parsed proto.Message ReceivedAt time.Time }

常用方法:

  • msg.GetMethod():获取消息类型
  • msg.GetPayload():获取 protobuf 原始 payload

如果你的项目使用 log/slog,可以直接用 NewDouyinLiveWithSlog 创建实例,日志会保留结构化级别和字段:

dl, err := douyinlive.NewDouyinLiveWithSlog(roomID, slog.Default(), cookie)

最简使用示例

package main import ( "log" douyinlive "github.com/jwwsjlm/douyinLive/v2" ) func main() { // 直播间ID,从 https://live.douyin.com/xxxx 获取 roomID := "516466932480" // 可选 Cookie,如果需要登录态可以传入,留空表示不使用 cookie := "" // 创建实例 dl, err := douyinlive.NewDouyinLive(roomID, log.Default(), cookie) if err != nil { log.Fatalf("创建失败: %v", err) return } // 订阅所有抖音消息 dl.SubscribeMessage(func(msg *douyinlive.LiveMessage) { log.Printf("收到消息 method=%s payload_len=%d live=%s\n", msg.GetMethod(), len(msg.GetPayload()), msg.LiveName, ) }) // 启动监听,会阻塞直到连接关闭 dl.Start() }

处理具体消息类型示例

package main import ( "log" douyinlive "github.com/jwwsjlm/douyinLive/v2" "github.com/jwwsjlm/douyinLive/v2/generated/new_douyin" "google.golang.org/protobuf/proto" ) func main() { roomID := "516466932480" dl, err := douyinlive.NewDouyinLive(roomID, log.Default(), "") if err != nil { log.Fatal(err) } dl.SubscribeMethod(douyinlive.WebcastChatMessage, func(msg *douyinlive.LiveMessage) { chat := &new_douyin.Webcast_Im_ChatMessage{} if err := proto.Unmarshal(msg.GetPayload(), chat); err != nil { log.Println(err) return } if chat.GetContent() != "" && chat.GetUser() != nil { log.Printf("弹幕 [%s]: %s\n", chat.GetUser().GetNickname(), chat.GetContent()) } }) dl.SubscribeMethods([]string{ douyinlive.WebcastGiftMessage, douyinlive.WebcastLikeMessage, }, func(msg *douyinlive.LiveMessage) { switch msg.GetMethod() { case douyinlive.WebcastGiftMessage: gift := &new_douyin.Webcast_Im_GiftMessage{} if err := proto.Unmarshal(msg.GetPayload(), gift); err != nil { log.Println(err) return } if gift.GetUser() != nil && gift.GetGift() != nil { log.Printf("礼物: %s 赠送了 %s x%d\n", gift.GetUser().GetNickname(), gift.GetGift().GetName(), gift.GetCount(), ) } case douyinlive.WebcastLikeMessage: like := &new_douyin.Webcast_Im_LikeMessage{} if err := proto.Unmarshal(msg.GetPayload(), like); err != nil { log.Println(err) return } if like.GetUser() != nil { log.Printf("%s 点赞了直播间\n", like.GetUser().GetNickname()) } } }) dl.Start() }

更多消息类型可以参考 generated/new_douyin 包下的 protobuf 生成代码。

旧的 Subscribe(func(raw, parsed)) 接口仍然保留,方便已有代码兼容;新代码建议优先使用 SubscribeMessage / SubscribeMethod / SubscribeMethods


客户端怎么接(独立服务模式)

如果你直接运行独立服务,你的客户端只需要连本地 WebSocket 服务即可。

JavaScript 示例

const ws = new WebSocket('ws://127.0.0.1:1088/ws/516466932480'); ws.onopen = () => { console.log('已连接'); }; ws.onmessage = (event) => { const data = JSON.parse(event.data); console.log('收到消息:', data); if (data.event === 'live_status') { if (data.live) { console.log('状态通知: 直播间已开播'); } else if (data.ended) { console.log(`状态通知: ${data.message},后续会继续按 ${data.retry_interval_seconds} 秒轮询`); } else { console.log(`状态通知: ${data.message}${data.retry_interval_seconds} 秒后重试`); } return; } switch (data.method) { case 'WebcastChatMessage': console.log(`弹幕: ${data.user.nickname} - ${data.content}`); break; case 'WebcastGiftMessage': console.log(`礼物: ${data.user.nickname} 赠送了 ${data.gift.name}`); break; case 'WebcastLikeMessage': console.log(`${data.user.nickname} 点赞了直播间`); break; default: break; } }; ws.onclose = () => { console.log('连接关闭'); }; ws.onerror = (err) => { console.error('WebSocket 错误:', err); }; // 可选:给本地服务发 ping,服务会回 pong setInterval(() => { if (ws.readyState === WebSocket.OPEN) { ws.send('ping'); } }, 30000);

服务端返回什么格式

服务端会把解析后的 protobuf 消息转成 JSON 文本发给你。

不同消息类型字段不完全一样,但都会包含对应消息内容。

另外会额外补一个字段:

  • livename:直播间名称
  • method:抖音消息类型,例如 WebcastChatMessage
  • title:直播间标题
  • avatarThumb:主播头像缩略图地址

如果直播间还没开播,则会返回系统状态消息,例如:

{"type":"system","event":"live_status","live":false,"room_id":"516466932480","message":"直播间未开播","retry_interval_seconds":30}

检测到开播时,也会先返回一条状态消息:

{"type":"system","event":"live_status","live":true,"room_id":"516466932480","message":"直播间已开播"}

如果直播过程中下播,也会先返回一条状态消息:

{"type":"system","event":"live_status","live":false,"room_id":"516466932480","message":"直播间已下播","ended":true,"retry_interval_seconds":30}

项目结构

douyinLive/ ├── cmd/main/ # 可执行程序入口 │ ├── main.go # 主程序 │ ├── app.go # HTTP / WebSocket 服务 │ ├── room.go # 房间与客户端管理 │ ├── config.go # 配置读取 │ └── WsHandler.go # WebSocket 事件处理 ├── douyin.go # 核心抓取逻辑,对外库接口 ├── sign/ # 签名与 Cookie 相关逻辑 ├── jsScript/ # 签名脚本 ├── protobuf/ # protobuf 定义 ├── generated/ # 生成后的 protobuf 代码 ├── utils/ # 工具函数 ├── config.example.yaml # 配置示例 └── README.md

适合谁用

如果你需要:

  • 获取抖音直播间实时弹幕
  • 做自己的弹幕大屏
  • 做直播互动统计
  • 做礼物 / 点赞 / 关注监听
  • 把抖音消息接进自己的系统

这个项目就比较合适。


致谢

本项目参考过这些项目和资料:

感谢原作者们的公开分享。


许可证

MIT


支持

如果这个项目对你有帮助,欢迎点个 Star。

关于 About

抖音弹幕抓取

语言 Languages

Go98.4%
Makefile1.1%
Dockerfile0.5%
Batchfile0.0%

提交活跃度 Commit Activity

代码提交热力图
过去 52 周的开发活跃度
59
Total Commits
峰值: 16次/周
Less
More

核心贡献者 Contributors