Python爬取斗鱼弹幕——多房间同时抓取实现(一)

Python爬取斗鱼弹幕——多房间同时抓取实现(一)从单个房间开始

技能储备需求
我们从零基础出发,但前提是假设我们已经有了基本python语法基础(比如我已经会了 Hello World!)现在需要学的:

  • 套接字socket
  • 正则表达式
  • 多线程爬虫
  • requests库的使用
  • json库的使用

以上五个是我们接下来会用到的,我们只要知道基本用法,如果只是单纯想实现爬弹幕功能,不需要深究。
斗鱼弹幕服务器第三方接入通讯协议

  • 此文档对斗鱼平台弹幕和相关消息通讯协议格式进行了详细说明,同时附带获取弹幕的详细操作步骤,意为第三方平台获取斗鱼弹幕等信息提供帮助。
  • 在谷歌上或者登陆官方论坛(http://dev-bbs.douyutv.com/forum.php) 都可以下载此文档

步入正题
Step1 建立与服务器连接
斗鱼弹幕通讯协议是一种基于 TCP 服务的应用层协议。我们可以从上文所提及文档中连接初始化的步骤。第三方客户端通过 TCP 协议连接到弹幕服务器(依据指定的 IP 和端口)。
第三方接入弹幕服务器列表:
IP 地址:openbarrage.douyutv.com 端口:8601
我们使用socket库来建立连接。
步骤很简单:

  1. 初始化socket
  2. 利用socket库中的connect方法建立连接

说明:connect方法需要的参数host我们可以通过IP地址来获取。
以下是代码

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  # create a socket
host = socket.gethostbyname("openbarrage.douyutv.com")  # get the host(8601) of Douyu
port = 8601
s.connect((host, port))  # connect the socket to Douyu

连接成功之后,我们可以用socket库中的recv方法获取服务器返回消息。但是到这一步,我们还不能收到弹幕消息。(房间还没进呢)
step2 登录及进入弹幕分组
根据斗鱼弹幕服务器协议要求,第三方接入需要先向服务器发送登录请求,并加入分组,服务器才会将客户端添加到请求指定弹幕分组,并返回相关消息。
这也很好理解,我们得先告诉对方我们想要获取什么,对方才好返回消息。
步骤

  1. 登录
    客户端向弹幕服务器发送登录请求,登录弹幕服务器;弹幕服务器收到客户端登录请求并完成登录后,返回登录成功消息给客户端。
    在此之前,我们需要知道斗鱼对于第三方发送的消息格式有自己的一套协议,所以,我们发送的登录请求必须符合这一规定。
    斗鱼后台协议头设计如下:
    Python爬取斗鱼弹幕——多房间同时抓取实现(一)_第1张图片
    字段说明如下:
    消息长度:4 字节小端整数,表示整条消息(包括自身)长度(字节数)。
    消息长度出现两遍,二者相同。
    消息类型:2 字节小端整数,表示消息类型。取值如下:
    689 客户端发送给弹幕服务器的文本格式数据
    690 弹幕服务器发送给客户端的文本格式数据。
    加密字段:暂时未用,默认为 0。
    保留字段:暂时未用,默认为 0。
    数据部分:斗鱼独创序列化文本数据,结尾必须为‘\0’。详细序列化、反
    序列化算法见下节。(所有协议内容均为 UTF-8 编码)
    简述
    简而言之,上述协议是规定了一套协议头的格式,就是说,我们想要发送给服务器的任何消息之前都需要加上上面的协议头。所以我们接下来的工作就是要写好这一段协议头。
    以下是代码:
data_length = len(info) + 8
value = 689  # the type of information for client sending to the server
infohead = int.to_bytes(data_length, 4, 'little') + int.to_bytes(data_length, 4, 'little') \
+ int.to_bytes(value, 4, 'little')
# transfer int into bytes and form a protocol head

到这里还是没有结束,因为我们还需要加上我们要发送的消息。
为增强兼容性、可读性斗鱼后台通讯协议采用文本形式的明文数据。也就是说,斗鱼对第三方发来的消息也有一套自己的协议,在文档中称之为序列化。
详细说明可参见文档,我们只需要知道怎么用就好了。
登录请求消息用于完成登陆授权,完整的数据部分应包含的字段如下:
type@=loginreq/roomid@=58839/
Python爬取斗鱼弹幕——多房间同时抓取实现(一)_第2张图片
例如我们想要爬取的房间号为688,那我们的登录消息就是:
‘type@=loginreq/roomid@=688/\0’
说明:
末尾加上‘\0’ 是斗鱼弹幕接入协议要求,详见上文所提文档。
此时我们只需加上上文所得协议头就可以向服务器发送登录请求了!代码如下:

login_info = 'type@=loginreq/username@=rieuse/password@=douyu/roomid@=688/\0' # sending login request
info = login_info.encode('utf-8')  # transfer it into utf-8 as requested
data_length = len(info) + 8
value = 689  # the type of information for client sending to the server
infohead = int.to_bytes(data_length, 4, 'little') + int.to_bytes(data_length, 4, 'little') \
+ int.to_bytes(value, 4, 'little')
s.send(infohead + info)
  1. 加入分组
    与登录类似,此时我们只需要把登录消息换成加入分组消息即可。这里我直接给出代码:
join_group = 'type@=joingroup/rid@=688/gid@=-9999/\0'# sending joingroup request
info = login_info.encode('utf-8')  # transfer it into utf-8 as requested
data_length = len(info) + 8
value = 689  # the type of information for client sending to the server
infohead = int.to_bytes(data_length, 4, 'little') + int.to_bytes(data_length, 4, 'little') \
+ int.to_bytes(value, 4, 'little')
s.send(infohead + info)
  1. 心跳消息
    斗鱼规定爬虫每间隔45s需要向服务器发送心跳消息,该消息用于维持与后台间的心跳,完整的数据部分应包含的字段如下:
    type@=keeplive/tick@=1439802131/
    Python爬取斗鱼弹幕——多房间同时抓取实现(一)_第3张图片
    这里,我写了一个函数来实现:
def keeplive(s):
    keeplive_info = 'type@=keeplive/tick@=' + str(int(time.time())) + '/\0'  # heartbeat info
    data_length = len(info) + 8
    value = 689  # the type of information for client sending to the server
    infohead = int.to_bytes(data_length, 4, 'little') +int.to_bytes(data_length, 4, 'little') \
+ int.to_bytes(value, 4, 'little')
    while True:
        info = keeplive_info.encode('utf-8')  # transfer it into utf-8 as requested
        s.send(infohead + info)
        time.sleep(45)

step3 弹幕获取
到此我们已经可以收到688房间返回的消息了!只需要利用socket库中的recv函数:

data = s.recv(1024)

但是你可能会发现它是这样的:
type@=chatmsg/rid@=58839/gid@=-9999/ct@=8/uid@=123456/nn@
=test/txt@=666/cid@=1111/ic@=icon/sahf@=0/level@=1/nl@=0/nc
@=0/cmt@=0/gt@=0/col@=0/rg@=0/pg@=0/dlv@=0/dc@=0/bdlv@
=0/gatin@=0/gatout@=0/chtin@=0/chtout@=0/repin@=0/repout@=
0/bnn@=test/bl@=0/brid@=58839/hc@=0/ol@=0/rev@=0/hl@=0/ifs
@=0/p2p@=0/el@=eid@AA=1@ASetp@AA=1@ASsc@AA=1@AS/

这和我们发送给服务器的消息是不是很像,这就是斗鱼独创的序列化消息格式,仔细阅读斗鱼给出的文档,发现这段消息中在’txt@=’ 后面是弹幕消息内容。那我们怎么提取出来呢?

此时就需要用到我们的第二项技能——正则表达式。没有接触过的话可以在菜鸟教程上简单学学,我们也只是需要用到很基础的部分。
在python中我们用re库来实现。为了匹配txt后面的弹幕内容,我们写出表达式:‘txt@=(.+?)/cid@’ 。 然后我们利用re中compile函数写出表达式。再利用findall函数进行全文匹配。代码如下:

data = s.recv(1024)
danmu = re.compile(b'txt@=(.+?)/cid@')
data_new = danmu.findall(data)

此时得到的还不是弹幕文本消息,我们还需要解码utf-8,再者,由于同一时间可能很多用户发弹幕,所以可能会同时收到多条弹幕消息,正则匹配出来的结果会是一个列表,此时我们需要做一些处理。

for i in range(0, len(data_new)):
    try:
        txt = data_new[0].decode(encoding='utf-8')  
    except AttributeError:
        print('Error')

到这里,得到的txt已经是解码成文本的弹幕消息了!我们已经完成了第一步。
后续我会继续更新如何同时爬取多个房间的弹幕消息。
说明
完整代码我会在第二部分里附上我在github上传的链接。

你可能感兴趣的:(python,爬虫)