B 站直播弹幕的 WebSocket 获取尝试与抽奖程序实现
网上对于 B 站的直播 ws 协议研究已经很多了,但是一是相互 copy 未形成完整的解决方案,二是 B 站收紧了未登录用户查看的弹幕信息。正巧学校团委部门直播使用,于是就有了我的相关尝试
这个方法不知道以后会不会再被限制,不过其原理上是完全模拟用户操作,与真实用户使用浏览器相同。
本项目已经开源在 Github 上:Bili-LiveLuckDraw
原理
弹幕服务器
B 站是通过 WebSocket 连接形式来向客户端发送通知和弹幕,因此我们的想法就是加入 ws 会话,接受消息并解析
其过程就是 拿到房间号-> 获取服务器地址(登录态)-> 加入会话-> 解析消息-> 拿到弹幕
我们接下来一步一步解决
房间号
首先 B 站的直播有短房间号和真实房间号,我们要调用 API 获取真实房间号
[GET] https://api.live.bilibili.com/room/v1/Room/room_init?id =${shortId}
返回内容是这样的:
弹幕服务器地址和密钥
为了获取弹幕,我们要先拿到对应的服务器地址,和加入的密钥
[GET] https://api.live.bilibili.com/xlive/web-room/v1/index/getDanmuInfo?id =${roomId}
返回内容是这样的:
问题来了,这里拿到的 token 和服务器,你会发现是否登录都能拿到,但是如果你用未登录的 token 配合 uid,会被直接断开连接,所以这里请求的时候就要带好登录态。B 站的登录态就是靠得 cookie,所以你可以 F12 直接从网络请求中拿到:
在这之后,你也会发现,诶嘿,拿到的弹幕服务器也变多了!
通知数据格式
鉴权包
首先,我们需要向 ws 服务器推送一个鉴权包,来验证身份后续得到消息推送
包头部分
包头用于描述数据包的元信息。这里的 headerBuffer
长度是 16
字节,按照协议的规定,需要在包头中写入以下内容:
包体部分
包体包含实际的业务数据。body
作为 JSON 字符串,包含了用户、房间、平台等信息。转成二进制数据后存放在 bodyBuffer
中。
心跳包
根据咱们上边的格式,我们就可以生成一个心跳包啦,B 站的心跳包的内容是 [Object object]
:
数据包
这里就是比较恶心的地方啦,不过也不用担心,马上就可以看到效果嘞
我们拿到的数据根据其 op
的值对应着不同的操作类型:
我们这里着重处理弹幕内容哦:
获取数据包的元信息
说明:通过读取数据的前 12 字节,我们可以知道数据包的长度、协议类型和操作类型,这些都是数据包的元信息。
数据包切分
说明:若 data.length
大于 packetLen
,说明这是一个分包,需要切分数据并递归处理后续的数据包。
解压处理
说明:如果 proto
为 2,说明数据包采用了压缩算法,需要对包体部分进行解压。解压后再次调用 getDmMsg
递归处理。
心跳包判断
说明:操作类型为 3 时,表明这是一个心跳包,通常用于保持与服务器的连接活跃。这个不用处理捏,我就打印了一下()
弹幕消息解析与事件触发
说明:操作类型为 5 表示这是一个弹幕消息包,包体中包含了弹幕信息。我们将其解析为 JSON 对象,并通过 emit
触发 MsgData
事件,供其他部分的代码进行处理。
程序设计
好啦,在上文中,我们已经明白大致的思路,感觉复杂也没关系,我们一步步来完成这些操作
技术栈选择
我这里选择的是 Electron + Vite + React 的方式进行开发,比较快速构建用户界面+node 的能力+跨平台
electron 还有一个好处,由于其本身就是浏览器,因此搞登录操作非常方便,获取 cookie 也十分简单
登录获取
我们首先要在新窗口打开 B 站并登录,ipc 什么的就不说了,当我们关闭窗口时,我们可以得到 cookie:
为了带着 cookie 请求,我们可以用 fetch-cookie
这个库:
如何确认用户登录成功了呢?我们只要请求用户的信息看到能不能拿到就行了呗,比如主页 navbar 的头像昵称:
获取服务器信息
这里比较简单,发两个请求就行了,带好 cookie:
连接 ws 服务器
拿到信息然后就用 ws
库连接,记得带一个 UA(应该这个库才可以带)
发送鉴权包
这里的 uid 可以从之前获取的用户信息中拿到,buvid 从 cookie 拿到,key 是之前拿到的 token
心跳包类似实现一下就好。
解析数据
为了方便进程间通信,我加了 EventEmitter
,然后按照我们之前的分析就可以解析到数据啦
补充
其他就是我的业务逻辑了,比如关键词匹配进入队列,比如用 shadcn 画一个勉强能看的 UI,然后随机数出队。
其中...开发还是挺恶心的,解析方法,请求数据,还有分包,每一个都卡了好久,这个是研究好久得到的成品了,如果对你有帮助,恳请能到 github 上给个 star,我后期有时间会进一步完善更多信息和操作。
小结
这里就是一个简单的弹幕获取流程,原理就是模仿用户操作,拿到数据并解析