【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、傳送登入請求
客戶端向彈幕伺服器傳送登入請求,登入彈幕伺服器,格式如下:
[email protected]=loginreq/[email protected]=房間號/
3、傳送加組請求
客戶端收到登入成功訊息後傳送進入彈幕分組請求給彈幕伺服器,格式如下:
[email protected]=joingroup/[email protected]=房間號/[email protected]=-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 = "[email protected]=keeplive/[email protected]=十位時間戳" # 舊版心跳訊息
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:https://github.com/QAQ112233/DouYu