調(diào)用斗魚API爬取直播間彈幕信息(用戶昵稱及彈幕內(nèi)容)
- 查看《斗魚彈幕服務(wù)器第三方接入?yún)f(xié)議v1.4.1》,了解斗魚API的使用方法,即如何連接斗魚彈幕服務(wù)器、維持連接及獲取彈幕信息
- Python調(diào)用斗魚API爬取直播間彈幕信息代碼和注釋
- 爬取結(jié)果示例
1. 查看《斗魚彈幕服務(wù)器第三方接入?yún)f(xié)議v1.4.1》,了解斗魚API的使用方法,即如何連接斗魚彈幕服務(wù)器、維持連接及獲取彈幕信息
1.1 登陸授權(quán)
欲從后臺獲取彈幕信息的客戶端在于服務(wù)器建立TCP連接后,需發(fā)起登陸請求(包括相關(guān)驗證信息),后臺驗證請求信息無誤后,返回登陸成功相應(yīng)。
1.2 房間分組
為管理斗魚的直播間及彈幕,后臺服務(wù)器有兩個重要概念:房間號和分組號。
房間號與主播的直播間地址為一一對應(yīng)關(guān)系。一般直播間房間號可在其 URL地址中找到,例如 http://www.douyutv.com/301712其中301712即為房間號。
分組號為某特定直播間不同觀眾所在彈幕交流群體的標(biāo)識。其意義主要為將人數(shù)過多彈幕信息量過大的直播間觀眾進(jìn)行切割分片管理,以防止觀眾接收過多彈幕而導(dǎo)致機(jī)器負(fù)載過重。分組號為整數(shù),一般從 0 開始動態(tài)增加改變。特別注意-9999 特殊分組號,該組成員將接受對應(yīng)直播間全部彈幕,即“海量彈幕”分組。
登陸授權(quán)為獲取彈幕的基礎(chǔ),而加入房間及其分組為獲取指定直播間彈幕的必要條件。
1.3 彈幕信息
彈幕信息包括以下類型:
- 文字彈幕
- 領(lǐng)取在線魚丸暴擊消息
- 贈送禮物消息
- 用戶進(jìn)房通知消息(為感謝大力支持斗魚平臺的用戶而設(shè)置的進(jìn)房提示信息)
- 用戶贈送酬勤通知消息
- 用戶信息
- 房間開關(guān)播提醒
- 廣播排行榜消息
- 超級彈幕消息
- 房間內(nèi)禮物廣播
- 房間用戶搶紅包
- 房間內(nèi) top10 變化消息
1.4 心跳信息
斗魚的彈幕協(xié)議是建立在TCP長連接服務(wù)上的,為管理這些長連接,保證及時銷毀無用的連接以釋放資源服務(wù)于有需的用戶,斗魚后臺需要與客戶端保持心跳。(目前后臺設(shè)置每45秒向后臺發(fā)送一條心跳信息)
1.5 斗魚后臺協(xié)議頭設(shè)計
客戶端向斗魚彈幕服務(wù)器發(fā)起請求時,發(fā)送的信息必須包括如下設(shè)計的協(xié)議頭。

字段說明:
- 消息長度:4 字節(jié)小端整數(shù),表示整條消息(包括自身)長度(字節(jié)數(shù))。消息長度出現(xiàn)兩遍,二者相同。
- 消息類型:2 字節(jié)小端整數(shù),表示消息類型。取值如下:
689 客戶端發(fā)送給彈幕服務(wù)器的文本格式數(shù)據(jù)
(我們客戶端向服務(wù)器發(fā)送的協(xié)議頭要包括689)
690 彈幕服務(wù)器發(fā)送給客戶端的文本格式數(shù)據(jù)。 - 加密字段:暫時未用,默認(rèn)為 0。
- 保留字段:暫時未用,默認(rèn)為 0。(消息類型,加密字段,保留字段三個字段加在一起4字節(jié))
- 數(shù)據(jù)部分:斗魚獨創(chuàng)序列化文本數(shù)據(jù),結(jié)尾必須為'\0'。(即發(fā)送的請求信息最后一個字符必須為'\0')
1.6 序列化
詳情見文檔
2. Python調(diào)用斗魚API爬取直播間彈幕信息代碼和注釋
第三方客戶端通過 TCP 協(xié)議連接到彈幕服務(wù)器(依據(jù)指定的 IP 和端口);
第三方接入彈幕服務(wù)器列表:
IP 地址:openbarrage.douyutv.com 端口:8601
發(fā)送請求的數(shù)據(jù)部分格式:
- 客戶端心跳消息:
心跳信息 - 登陸請求消息:
登錄請求信息 -
入組消息
入組消息
獲取收到的數(shù)據(jù)中的文字彈幕信息,文字彈幕信息的格式:

參考資料:
'''
文件名:爬取斗魚直播間信息到j(luò)sonline文件.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))
# 獲取用戶昵稱及彈幕信息的正則表達(dá)式
danmu = re.compile(b'type@=chatmsg.*?/nn@=(.*?)/txt@=(.*?)/')
def sendmsg(msgstr):
'''
客戶端向服務(wù)器發(fā)送請求的函數(shù),集成發(fā)送協(xié)議頭的功能
msgHead: 發(fā)送數(shù)據(jù)前的協(xié)議頭,消息長度的兩倍,及消息類型、加密字段和保密字段
使用while循環(huán)發(fā)送具體數(shù)據(jù),保證將數(shù)據(jù)都發(fā)送出去
'''
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):
'''
發(fā)送登錄驗證請求后,獲取服務(wù)器返回的彈幕信息,同時提取昵稱及彈幕內(nèi)容
登陸請求消息及入組消息末尾要加入\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['彈幕內(nèi)容'] = 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():
'''
發(fā)送心跳信息,維持TCP長連接
心跳消息末尾加入\0
'''
while True:
msg = 'type@=keeplive/tick@=' + str(int(time.time())) + '/\0'
sendmsg(msg)
time.sleep(10)
def get_name(roomid):
'''
利用BeautifulSoup獲取直播間標(biāo)題
'''
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()
3. 爬取結(jié)果示例



