技能储备需求
我们从零基础出发,但前提是假设我们已经有了基本python语法基础(比如我已经会了 Hello World!)现在需要学的:
以上五个是我们接下来会用到的,我们只要知道基本用法,如果只是单纯想实现爬弹幕功能,不需要深究。
斗鱼弹幕服务器第三方接入通讯协议
步入正题
Step1 建立与服务器连接
斗鱼弹幕通讯协议是一种基于 TCP 服务的应用层协议。我们可以从上文所提及文档中连接初始化的步骤。第三方客户端通过 TCP 协议连接到弹幕服务器(依据指定的 IP 和端口)。
第三方接入弹幕服务器列表:
IP 地址:openbarrage.douyutv.com 端口:8601
我们使用socket库来建立连接。
步骤很简单:
说明: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 登录及进入弹幕分组
根据斗鱼弹幕服务器协议要求,第三方接入需要先向服务器发送登录请求,并加入分组,服务器才会将客户端添加到请求指定弹幕分组,并返回相关消息。
这也很好理解,我们得先告诉对方我们想要获取什么,对方才好返回消息。
步骤
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/
例如我们想要爬取的房间号为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)
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)
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上传的链接。