调用斗鱼API爬取直播间弹幕信息(用户昵称及弹幕内容)
- 查看《斗鱼弹幕服务器第三方接入协议v1.4.1》,了解斗鱼API的使用方法,即如何连接斗鱼弹幕服务器、维持连接及获取弹幕信息
- Python调用斗鱼API爬取直播间弹幕信息代码和注释
- 爬取结果示例
1. 查看《斗鱼弹幕服务器第三方接入协议v1.4.1》,了解斗鱼API的使用方法,即如何连接斗鱼弹幕服务器、维持连接及获取弹幕信息
1.1 登陆授权
欲从后台获取弹幕信息的客户端在于服务器建立TCP连接后,需发起登陆请求(包括相关验证信息),后台验证请求信息无误后,返回登陆成功相应。
1.2 房间分组
为管理斗鱼的直播间及弹幕,后台服务器有两个重要概念:房间号和分组号。
房间号与主播的直播间地址为一一对应关系。一般直播间房间号可在其 URL地址中找到,例如 http://www.douyutv.com/301712其中301712即为房间号。
分组号为某特定直播间不同观众所在弹幕交流群体的标识。其意义主要为将人数过多弹幕信息量过大的直播间观众进行切割分片管理,以防止观众接收过多弹幕而导致机器负载过重。分组号为整数,一般从 0 开始动态增加改变。特别注意-9999 特殊分组号,该组成员将接受对应直播间全部弹幕,即“海量弹幕”分组。
登陆授权为获取弹幕的基础,而加入房间及其分组为获取指定直播间弹幕的必要条件。
1.3 弹幕信息
弹幕信息包括以下类型:
- 文字弹幕
- 领取在线鱼丸暴击消息
- 赠送礼物消息
- 用户进房通知消息(为感谢大力支持斗鱼平台的用户而设置的进房提示信息)
- 用户赠送酬勤通知消息
- 用户信息
- 房间开关播提醒
- 广播排行榜消息
- 超级弹幕消息
- 房间内礼物广播
- 房间用户抢红包
- 房间内 top10 变化消息
1.4 心跳信息
斗鱼的弹幕协议是建立在TCP长连接服务上的,为管理这些长连接,保证及时销毁无用的连接以释放资源服务于有需的用户,斗鱼后台需要与客户端保持心跳。(目前后台设置每45秒向后台发送一条心跳信息)
1.5 斗鱼后台协议头设计
客户端向斗鱼弹幕服务器发起请求时,发送的信息必须包括如下设计的协议头。
字段说明:
- 消息长度:4 字节小端整数,表示整条消息(包括自身)长度(字节数)。消息长度出现两遍,二者相同。
- 消息类型:2 字节小端整数,表示消息类型。取值如下:
689 客户端发送给弹幕服务器的文本格式数据
(我们客户端向服务器发送的协议头要包括689)
690 弹幕服务器发送给客户端的文本格式数据。 - 加密字段:暂时未用,默认为 0。
- 保留字段:暂时未用,默认为 0。(消息类型,加密字段,保留字段三个字段加在一起4字节)
- 数据部分:斗鱼独创序列化文本数据,结尾必须为'\0'。(即发送的请求信息最后一个字符必须为'\0')
1.6 序列化
详情见文档
2. Python调用斗鱼API爬取直播间弹幕信息代码和注释
第三方客户端通过 TCP 协议连接到弹幕服务器(依据指定的 IP 和端口);
第三方接入弹幕服务器列表:
IP 地址:openbarrage.douyutv.com 端口:8601
发送请求的数据部分格式:
- 客户端心跳消息:
- 登陆请求消息:
-
入组消息
获取收到的数据中的文字弹幕信息,文字弹幕信息的格式:
参考资料:
'''
文件名:爬取斗鱼直播间信息到jsonline文件.py
参考的github:https://github.com/rieuse
'''
from __future__ import unicode_literals
import multiprocessing
import socket
import time
import re
import requests
from bs4 import BeautifulSoup
import json
# 配置socket的ip和端口
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = socket.gethostbyname("openbarrage.douyutv.com")
port = 8601
client.connect((host, port))
# 获取用户昵称及弹幕信息的正则表达式
danmu = re.compile(b'type@=chatmsg.*?/nn@=(.*?)/txt@=(.*?)/')
def sendmsg(msgstr):
'''
客户端向服务器发送请求的函数,集成发送协议头的功能
msgHead: 发送数据前的协议头,消息长度的两倍,及消息类型、加密字段和保密字段
使用while循环发送具体数据,保证将数据都发送出去
'''
msg = msgstr.encode('utf-8')
data_length = len(msg) + 8
code = 689
msgHead = int.to_bytes(data_length, 4, 'little') \
+ int.to_bytes(data_length, 4, 'little') + int.to_bytes(code, 4, 'little')
client.send(msgHead)
sent = 0
while sent < len(msg):
tn = client.send(msg[sent:])
sent = sent + tn
def start(roomid):
'''
发送登录验证请求后,获取服务器返回的弹幕信息,同时提取昵称及弹幕内容
登陆请求消息及入组消息末尾要加入\0
'''
msg = 'type@=loginreq/roomid@={}/\0'.format(roomid)
sendmsg(msg)
msg_more = 'type@=joingroup/rid@={}/gid@=-9999/\0'.format(roomid)
sendmsg(msg_more)
print('---------------欢迎连接到{}的直播间---------------'.format(get_name(roomid)))
while True:
data = client.recv(1024)
danmu_more = danmu.findall(data)
if not data:
break
else:
with open('bullet_curtain.jl', 'a') as f:
try:
for i in danmu_more:
dmDict={}
dmDict['昵称'] = i[0].decode(encoding='utf-8', errors='ignore')
dmDict['弹幕内容'] = i[1].decode(encoding='utf-8', errors='ignore')
dmJsonStr = json.dumps(dmDict, ensure_ascii=False)+'\n'
print(dmDict['昵称'])
f.write(dmJsonStr)
danmuNum = danmuNum + 1
except:
continue
def keeplive():
'''
发送心跳信息,维持TCP长连接
心跳消息末尾加入\0
'''
while True:
msg = 'type@=keeplive/tick@=' + str(int(time.time())) + '/\0'
sendmsg(msg)
time.sleep(10)
def get_name(roomid):
'''
利用BeautifulSoup获取直播间标题
'''
r = requests.get("http://www.douyu.com/" + roomid)
soup = BeautifulSoup(r.text, 'lxml')
return soup.find('a', {'class', 'zb-name'}).string
# 启动程序
if __name__ == '__main__':
room_id = input('请输入房间ID: ')
p1 = multiprocessing.Process(target=start, args=(room_id,))
p2 = multiprocessing.Process(target=keeplive)
p1.start()
p2.start()