弹幕id格式错误_(2020年最新)斗鱼弹幕抓取及实时弹幕数据可视化(二)

6d0f920fc48a8cb492750cb9f072413f.png

前文链接:

mawkish:(2020年最新)斗鱼弹幕抓取及实时弹幕数据可视化(一)​zhuanlan.zhihu.com
1aad739e5971c4a64890919b95de3d71.png

上一篇中我们使用Python的websocket库成功连接上了斗鱼的弹幕服务器,最后讲到了我们连接成功后还需要给服务器发一条“登录请求消息”,来告诉服务器我们不是“占着茅坑不拉屎”,以及必要的选项,比如目标房间号等等。

阅读《协议》可知,客户端与弹幕服务器之间传输的数据并不是原文,而是在应用层经过了特殊的协议封装。

0baa4403f9b3973865b0a6afd1dbf0f0.png

如果学习过计算机网络的话这样的格式再熟悉不过,如果没学过也没事,听我慢慢道来。第一行“字节”只是为了表示在这个表中一行的宽度为4个字节,并不是实际的数据,真正的数据从第二行开始。

根据《协议》可得,在原数据的基础上,我们需要加上消息长度、特定的头部与尾部。消息长度为4B,头部结构为(4B消息长度、2B消息、1B加密字段、1B保留字段),尾部为(1B的'0')。(注意消息长度重复了两次)

这儿会涉及到一个字节流以及小端序的概念。在计算机网络中是以字节流进行数据传输的,因此传输字符数据之前需要将字符流转化为字节流,在对方接收到字节数据后再将其转化为字符数据,也就是人类可读的文字,大家常遇到的“乱码”问题其实就是字节流转化为字符流时出了问题。Python中提供了encode()方法来将字符串转化为字节流、decode()方法将字节流转化为字符串、以及bytearray([1, 2, 3])方法来将数组中的整数拼接为字节流。

而小端序指的是数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。有点抽象,来看个例子:《协议》中规定协议中消息类型固定为2字节小端整数,而客户端发送的消息类型为689。那么689对应的2字节小端整数是什么呢?首先将689转化为十六进制0x02b1,按字节顺序拆分即[0x02, 0xb1],按这样的顺序放在内存中是大端序;如果倒着拆分即[0xb1, 0x02],这样放在内存中就是小端序。

回到我们的需求:给服务器发一条“登录请求消息”,细化来说分为以下几个步骤:

  1. 构造登录请求消息字符串
  2. 将字符串转化为字节流数据,并且按照斗鱼协议格式进行封装(也就是加上头部和尾部)
  3. 调用websocket库的相关方法将数据发给服务器

下面直接上代码:

# 与前文(即系列第一篇文章)一样的代码省略

# 将字符串数据按照斗鱼协议封装为字节流
def dy_encode(msg):
    # 头部8字节,尾部1字节,与字符串长度相加即数据长度
    # 为什么不加最开头的那个消息长度所占4字节呢?这得问问斗鱼^^
    data_len = len(msg) + 9
    # 字符串转化为字节流
    msg_byte = msg.encode('utf-8')
    # 将数据长度转化为小端整数字节流
    len_byte = int.to_bytes(data_len, 4, 'little')
    # 前两个字节按照小端顺序拼接为0x02b1,转化为十进制即689(《协议》中规定的客户端发送消息类型)
    # 后两个字节即《协议》中规定的加密字段与保留字段,置0
    send_byte = bytearray([0xb1, 0x02, 0x00, 0x00])
    # 尾部以'0'结束
    end_byte = bytearray([0x00])
    # 按顺序拼接在一起
    data = len_byte + len_byte + send_byte + msg_byte + end_byte

    return data

# 发送登录请求消息
def login(ws):
    msg = 'type@=loginreq/roomid@=1126960/'
    msg_bytes = dy_encode(msg)
    ws.send(msg_byte)

def on_open(ws):
    print('open')
    # 在连接上服务器后发送登录请求消息
    login(ws)

def on_message(ws, message):
    # 将字节流转化为字符串,忽略无法解码的错误(即斗鱼协议中的头部尾部)
    print(message.decode(encoding='utf-8', errors='ignore'))

运行之后首先会输出一个"open"代表连接上服务器;之后会接着输出一串含有乱码的字符串,其中有"type@=loginres"等字样,代表服务器成功接收到了我们发出的登录请求消息,并且给了我们回馈,登录成功!但是还是没有弹幕数据,过了一段时间后我们的连接还是被服务器给踢了。

3f17a3155ff1b86191455ab4dc97bda4.png

如果是直播老粉的话应该遇到过这种情况:打开一个直播间看了很久,期间你没有动过鼠标和键盘,就这样看直播看入迷了,突然斗鱼会提醒你:“你还在电脑前吗?”由此我们可以联想到,斗鱼服务器也会这样。如果我们的客户端就这样一直收着弹幕消息,但是一直不给服务器点反馈,时间久了服务器也有点疑惑,“这客户端是不是已经溜了啊?”,然后就把你客户端给踢了。这个我们后续会讲到,怎么给服务器反馈,让服务器知道,“其实我一直都在。”

可能有人还会问,这服务器也没给我发弹幕消息啊,我这不啥都没收到么。让我们重回《协议》第25页,其中写到:

b577f5e465529dbf6679647d03fd945d.png

所以我们还差一条“入组消息”,有点类似TCP三次握手,通过三条消息来确保客户端与服务器之间的连接成功。根据协议要求以及前文经验,我们直接写出下列代码:

# 同样已有代码省略,只写出有改动部分

# 发送入组消息
def join_group(ws):
    msg = 'type@=joingroup/rid@=1126960/gid@=-9999/'
    msg_bytes = dy_encode(msg)
    ws.send(msg_bytes)

def on_open(ws):
    print('open')
    login(ws)
    # 登录后发送入组消息
    join_group(ws)

再次运行,弹幕消息就源源不断的传输给你啦(虽然过段时间还是会被踢)。我们终于看到了熟悉的“弹幕文化”以及直播间的观众们与主播“友好的互动”。希望到这里大家会有一点成就感,然后趁热打铁,自己动脑筋把这些“乱糟糟”的数据解析出来,得到自己想要的干干净净的弹幕。解析用到的知识点并不多,大家利用好协议头部的“消息长度”,以及一些基本的字符串处理函数即可。

ec238a3f0270429fe46682aada1a3425.png

我们下篇再见!附上Github仓库地址:

https://github.com/Crawler995/DouyuBarrage​github.com

(2020/03/01更:上面这个项目(即第一版)不再维护,自己重构了个第二版DouyuBarrage-Pro,提供弹幕抓取、抓取记录查询、弹幕下载、自定义关键词统计、铁粉统计、高光时刻自动捕获、弹幕实时发送速度可视化、高频弹幕词云等功能。正在开发中,打造企业级应用的使用体验!)

https://github.com/Crawler995/DouyuBarrage-Pro​github.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值