【Python3爬蟲】斗魚彈幕爬蟲


在網上找到了一份斗魚彈幕服務器第三方接入協議v1.6.2,有了第三方接口,做起來就容易多了。

 

一、協議分析

斗魚后台協議頭設計如下:

 

這里的消息長度是我們發送的數據部分的長度和頭部的長度之和,兩個消息長度是一樣。然后要注意的是該協議使用的是小端整數,所以我們要對數據進行處理后再發送,這里可以使用int.to_bytes()將整數轉變成小端整數的形式。示例如下:

int.to_bytes(12,4,'little')   # b'\x0c\x00\x00\x00'

int.to_bytes(12,4,'big')     # b'\x00\x00\x00\x0c'

然后消息類型是689(689表示客戶端發送給服務器,690表示服務器發送給客戶端),加密字段和保留字段都是默認為0。這里由於消息類型是兩個字節的,加密字段和保留字段都是一個字節,但是因為加密字段和保留字段都是0,所以這四個字節可以使用int.to_bytes(689,4,'little')來表示。最后該協議使用的是utf-8編碼,所以我們需要對整個數據進行編碼后再發送。

 

二、具體步驟

1、連接服務器

第三方客戶端通過 TCP 協議連接到彈幕服務器(依據指定的 IP 和端口),其中IP 地址為openbarrage.douyutv.com,端口為8601,相關代碼如下:

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = socket.gethostbyname("openbarrage.douyutv.com")
port = 8601
client.connect((host, port))

2、發送登錄請求

客戶端向彈幕服務器發送登錄請求,登錄彈幕服務器,格式如下:

type@=loginreq/roomid@=房間號/

3、發送加組請求

客戶端收到登錄成功消息后發送進入彈幕分組請求給彈幕服務器,格式如下:

type@=joingroup/rid@=房間號/gid@=-9999/

gid表示分組號,第三方平台建議選擇-9999即海量彈幕模式。

4、接收廣播消息

接收服務器發送的廣播消息,包括用戶發的彈幕和送的禮物信息,然后解析得到具體的內容。但這些數據里只有禮物的id而沒有具體的禮物名稱,然后我通過抓包找到了兩個鏈接,里面包含了禮物id和名稱的對應關系,相關代碼如下:

 1 gift_dict = {}
 2 headers = {
 3     "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36"
 4 }
 5 url1 = "https://webconf.douyucdn.cn/resource/common/gift/flash/gift_effect.json"
 6 res1 = requests.get(url1, headers=headers)
 7 js1 = json.loads(res1.text.lstrip('DYConfigCallback(').rstrip(');'))
 8 gift_data1 = js1['data']['flashConfig']
 9 for i in gift_data1.keys():
10     gift_dict[gift_data1[i]['id']] = gift_data1[i]['name']
11 
12 url2 = "https://webconf.douyucdn.cn/resource/common/prop_gift_list/prop_gift_config.json"
13 res2 = requests.get(url2, headers=headers)
14 js2 = json.loads(res2.text.lstrip('DYConfigCallback(').rstrip(');'))
15 gift_data2 = js2['data']
16 for i in gift_data2.keys():
17     gift_dict[int(i)] = gift_data2[i]['name']

5、發送心跳消息

客戶端每隔45秒給服務器發送一次心跳消息,用於維護和服務器后台間的聯系,格式如下:

keep_msg = "type@=keeplive/tick@=十位時間戳"  # 舊版心跳消息
keep_msg = "mrkl/" # 新版心跳消息

6、核心代碼

在發送數據的時候,有可能會出現一次無法發送完的情況,所以就需要多發送幾次,確保把數據都發送出去:

 1 msg = msg + '\0'  # 數據以'\0'結尾
 2 msg = msg.encode('utf-8')  # 使用utf-8編碼
 3 length = len(msg) + 8  # 消息長度
 4 code = 689  # 消息類型
 5 # 消息頭部:消息長度+消息長度+消息類型+加密字段(默認為0)+保留字段(默認為0)
 6 head = int.to_bytes(length, 4, 'little') + int.to_bytes(length, 4, 'little') + int.to_bytes(code, 4, 'little')
 7 # 發送頭部部分
 8 client.send(head)
 9 # 發送數據部分
10 sent = 0
11 while sent < len(msg):
12     n = client.send(msg[sent:])  # 返回已發送的數據長度
13     sent = sent + n

 

三、運行結果

運行截圖:

進入數據庫查看結果:

db.getCollection('DouYu-6039226').find({"data_type":"gift"})

結果如下:

還可以看看大家都發了什么彈幕:

db.getCollection('DouYu-6039226').find({"data_type":"chat"},{"user_name":1,"chat_txt":1,"_id":0})

結果如下:

 

完整代碼已上傳到GitHub


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM