我用Java爬取了鬥魚刀塔區的彈幕

本篇文章我們來寫一下鬥魚彈幕爬蟲的Java版。可能有人會說Python版本的爬蟲到處都可以搜到,而且用Python的scrapy、beautifulsoup等庫又快又方便,那麼為什麼我們還要用Java寫爬蟲呢?

事實是這樣沒錯,但是這次的彈幕爬蟲嚴格來講主要涉及到網絡數據傳輸,並不需要構造html標籤,直接用socket + mybatis請求到數據後存入數據庫即可(我會說是python的多線程不熟悉嗎),其實也很方便,也不用去加腳本加py文件來實現。總而言之就兩個字,簡單。

由於頭條的編輯器對於寫代碼不友好,因此我都用圖片進行表示,需要源碼的同學可以到文末的github鏈接上下載。

下載鬥魚協議文檔

鬥魚彈幕爬蟲原理很簡單:

  • 客戶端依次發送,登陸請求、加入房間請求、加入彈幕組請求。
  • 鬥魚的服務端返回彈幕

當然由於是一個長鏈接,還需要另起一個心跳線程(heart thread)來不斷髮送alive消息,以保持連接。

Step1 下載協議文檔

去鬥魚第三方開放平臺下載兩份文檔:

  • 《鬥魚第三方開放平臺API文檔v2.2》
  • 《鬥魚彈幕服務器第三方接入協議v1.6.2》

鬥魚第三方地址:http://dev-bbs.douyutv.com/forum.php?mod=forumdisplay&fid=37

Step2 瞭解協議

根據第三方接入協議所述,我們需要按照鬥魚消息協議格式向鬥魚的彈幕服務器發送請求。

發送消息

就像TCP的3次握手一樣,首先由客戶端發出第一次握手,我們需要先發出登陸請求,為了擴展,我們將所有請求都認為是msg,先寫一個請求函數。

我用Java爬取了鬥魚刀塔區的彈幕

我們定義一個發送請求的函數,這裡的client就是我們的連接,msg就是我們具體的請求類型。學過計算機網絡的應該知道,兩個網絡節點之間的交互是通過套接字來進行的,而Java中的Socket類就可以被作為網絡中的套接字,client將會通過以下方式進行初始化:

Socket client = new Sokcet("openbarrage.douyutv.com", "8601");

指定彈幕服務器與端口號。在我的電腦上不知道為什麼在請求openbarrage.douyutv.com時會報:Unknow Host。因此我直接使用它的ip來初始化Socket。

我們在命令行ping一下openbarrage.douyutv.com,就可以看到它的ip地址:119.96.201.28

因此我採用的初始化方式為

Socket client = new Sokcet("119.96.201.28", "8601");

然後我們看如何構造鬥魚的請求頭。

我用Java爬取了鬥魚刀塔區的彈幕

可以看到,該請求頭一共是 4 字節的長度 + 8 字節的頭部 + 數據部。那麼表格中的消息長度是多少呢?

消息長度 = 頭部 + 數據部

這裡將消息長度這個信息重複了兩次,但是第一次不算在長度的計算之中,所以消息長度為 8 + 數據的長度 + 1(結尾的'\0')。即9 + 數據長度。

我們需要將數據分5塊,分別是4字節消息長度,4字節消息長度,4字節的消息類型+加密字段+保留字段,數據部和最後一字節的結尾符(0代表'\0'的字節形式),寫入byte數組輸出流中。

這些數據都需要轉成字節形式,再看鬥魚協議,裡面說鬥魚的消息格式都是小端模式(不像字符串有String.getBytes()函數,int得自己定義了)。我們定義兩個轉換函數:

我用Java爬取了鬥魚刀塔區的彈幕

解釋一下這個函數,int是4字節32位的,將data右移24位後,左邊補0,得到最左邊的8位(位於最右邊)。此時通過與上0xFF,即與上0000 0000 0000 0000 0000 0000 1111 1111。將左邊24位都清0,留下右邊8位,存在byte數組中。

我用Java爬取了鬥魚刀塔區的彈幕

接受消息

我用Java爬取了鬥魚刀塔區的彈幕

在接受消息時,先拿到返回消息的長度,以便在下面申請buffer的時候不需要申請多餘的buffer,然後將幾個無用數據(都是4字節)都讀出來。

消息長度-8就是真正的數據返回長度。通過循環讀入,直到讀入的數據長度等於鬥魚告訴我們的消息長度為止。

爬蟲線程

然後我們來寫一個爬蟲線程類。

首先發送登陸請求與加入彈幕組請求:

我用Java爬取了鬥魚刀塔區的彈幕

然後在連接到鬥魚後,開始處理消息:

我用Java爬取了鬥魚刀塔區的彈幕

這裡的消息處理函數並非像Python那樣通過正則來取,而是:

我用Java爬取了鬥魚刀塔區的彈幕

將消息用'/'劃分,然後另key為@=前面的值,value為@=後面的值。

心跳線程

我用Java爬取了鬥魚刀塔區的彈幕

主函數

創建一個主函數類後,直接用psmv一鍵生成public static void main。

我用Java爬取了鬥魚刀塔區的彈幕

以下是老陳和肚皮怪的彈幕:

我用Java爬取了鬥魚刀塔區的彈幕

當然,後續將會用線程池優化一下,事實上,上面的代碼已經加入了springboot與mysql了。想要源碼的同學可以去github上下載:https://github.com/9plus/As/tree/master/demo/DyTest。

相關推薦

推薦中...