Java 多線程爬蟲及分佈式爬蟲架構探索

Java 多線程爬蟲及分佈式爬蟲架構探索

這是 Java 爬蟲系列博文的第五篇,在上一篇 Java 爬蟲服務器被屏蔽,不要慌,咱們換一臺服務器中,我們簡單的聊反爬蟲策略和反反爬蟲方法,主要針對的是 IP 被封及其對應辦法。前面幾篇文章我們把爬蟲相關的基本知識都講的差不多啦。這一篇我們來聊一聊爬蟲架構相關的內容。

前面幾章內容我們的爬蟲程序都是單線程,在我們調試爬蟲程序的時候,單線程爬蟲沒什麼問題,但是當我們在線上環境使用單線程爬蟲程序去採集網頁時,單線程就暴露出了兩個致命的問題:

  • 採集效率特別慢,單線程之間都是串行的,下一個執行動作需要等上一個執行完才能執行
  • 對服務器的CUP等利用率不高,想想我們的服務器都是 8核16G,32G 的只跑一個線程會不會太浪費啦

線上環境不可能像我們本地測試一樣,不在乎採集效率,只要能正確提取結果就行。在這個時間就是金錢的年代,不可能給你時間去慢慢的採集,所以單線程爬蟲程序是行不通的,我們需要將單線程改成多線程的模式,來提升採集效率和提高計算機利用率。

多線程的爬蟲程序設計比單線程就要複雜很多,但是與其他業務在高併發下要保證數據安全又不同,多線程爬蟲在數據安全上到要求不是那麼的高,因為每個頁面都可以被看作是一個獨立體。要做好多線程爬蟲就必須做好兩點:第一點就是統一的待採集 URL 維護,第二點就是 URL 的去重,下面我們簡單的來聊一聊這兩點。

維護待採集的 URL

多線程爬蟲程序就不能像單線程那樣,每個線程獨自維護這自己的待採集 URL,如果這樣的話,那麼每個線程採集的網頁將是一樣的,你這就不是多線程採集啦,你這是將一個頁面採集的多次。基於這個原因我們就需要將待採集的 URL 統一維護,每個線程從統一 URL 維護處領取採集 URL ,完成採集任務,如果在頁面上發現新的 URL 鏈接則添加到 統一 URL 維護的容器中。下面是幾種適合用作統一 URL 維護的容器:

  • JDK 的安全隊列,例如 LinkedBlockingQueue
  • 高性能的 NoSQL,比如 Redis、Mongodb
  • MQ 消息中間件

URL 的去重

URL 的去重也是多線程採集的關鍵一步,因為如果不去重的話,那麼我們將採集到大量重複的 URL,這樣並沒有提升我們的採集效率,比如一個分頁的新聞列表,我們在採集第一頁的時候可以得到 2、3、4、5 頁的鏈接,在採集第二頁的時候又會得到 1、3、4、5 頁的鏈接,待採集的 URL 隊列中將存在大量的列表頁鏈接,這樣就會重複採集甚至進入到一個死循環當中,所以就需要 URL 去重。URL 去重的方法就非常多啦,下面是幾種常用的 URL 去重方式:

  • 將 URL 保存到數據庫進行去重,比如 redis、MongoDB
  • 將 URL 放到哈希表中去重,例如 hashset
  • 將 URL 經過 MD5 之後保存到哈希表中去重,相比於上面一種,能夠節約空間
  • 使用 布隆過濾器(Bloom Filter)去重,這種方式能夠節約大量的空間,就是不那麼準確。

關於多線程爬蟲的兩個核心知識點我們都知道啦,下面我畫了一個簡單的多線程爬蟲架構圖,如下圖所示:

Java 多線程爬蟲及分佈式爬蟲架構探索

上面我們主要了解了多線程爬蟲的架構設計,接下來我們不妨來試試 Java 多線程爬蟲,我們以採集虎撲新聞為例來實戰一下 Java 多線程爬蟲,Java 多線程爬蟲中設計到了 待採集 URL 的維護和 URL 去重,由於我們這裡只是演示,所以我們就使用 JDK 內置的容器來完成,我們使用 LinkedBlockingQueue 作為待採集 URL 維護容器,HashSet 作為 URL 去重容器。下面是 Java 多線程爬蟲核心代碼,詳細代碼以上傳 GitHub,地址在文末:

Java 多線程爬蟲及分佈式爬蟲架構探索

Java 多線程爬蟲及分佈式爬蟲架構探索

Java 多線程爬蟲及分佈式爬蟲架構探索

Java 多線程爬蟲及分佈式爬蟲架構探索

Java 多線程爬蟲及分佈式爬蟲架構探索

我們用 5 個線程去採集虎撲新聞列表頁看看效果如果?運行該程序,得到如下結果:

Java 多線程爬蟲及分佈式爬蟲架構探索

結果中可以看出,我們啟動了 5 個線程採集了 61 頁頁面,一共耗時 2 秒鐘,可以說效果還是不錯的,我們來跟單線程對比一下,看看差距有多大?我們將線程數設置為 1 ,再次啟動程序,得到如下結果:

Java 多線程爬蟲及分佈式爬蟲架構探索

可以看出單線程採集虎撲 61 條新聞花費了 7 秒鐘,耗時差不多是多線程的 4 倍,你想想這可只是 61 個頁面,頁面更多的話,差距會越來越大,所以多線程爬蟲效率還是非常高的。

分佈式爬蟲架構

分佈式爬蟲架構是一個大型採集程序才需要使用的架構,一般情況下使用單機多線程就可以解決業務需求,反正我是沒有分佈式爬蟲項目的經驗,所以這一塊我也沒什麼可以講的,但是我們作為技術人員,我們需要對技術保存熱度,雖然不用,但是瞭解瞭解也無妨,我查閱了不少資料得出瞭如下結論:

分佈式爬蟲架構跟我們多線程爬蟲架構在思路上來說是一樣的,我們只需要在多線程的基礎上稍加改進就可以變成一個簡單的分佈式爬蟲架構。因為分佈式爬蟲架構中爬蟲程序部署在不同的機器上,所以我們待採集的 URL 和 採集過的 URL 就不能存放在爬蟲程序機器的內存中啦,我們需要將它統一在某臺機器上維護啦,比如存放在 Redis 或者 MongoDB 中,每臺機器都從這上面獲取採集鏈接,而不是從 LinkedBlockingQueue 這樣的內存隊列中取鏈接啦,這樣一個簡單的分佈式爬蟲架構就出現了,當然這裡面還會有很多細節問題,因為我沒有分佈式架構的經驗,我也無從說起,如果你有興趣的話,歡迎交流。

源代碼:

https://github.com/BinaryBall/java-base/blob/master/crawler/src/main/java/com/jamal/crawler/ThreadCrawler.java

文章不足之處,望大家多多指點,共同學習,共同進步

作者:平頭哥的技術博文
來源:掘金
商業用途請與原作者聯繫,本文只做展示分享,不妥侵刪!
"


分享到:


相關文章: