在我們調(diào)試爬蟲程序的時(shí)候,單線程爬蟲沒什么問題,但是當(dāng)我們?cè)诰€上環(huán)境使用單線程爬蟲程序去采集網(wǎng)頁(yè)時(shí),單線程就暴露出了兩個(gè)致命的問題:
采集效率特別慢,單線程之間都是串行的,下一個(gè)執(zhí)行動(dòng)作需要等上一個(gè)執(zhí)行完才能執(zhí)行
對(duì)服務(wù)器的CUP等利用率不高,想想我們的服務(wù)器都是 8核16G,32G 的只跑一個(gè)線程會(huì)不會(huì)太浪費(fèi)啦
線上環(huán)境不可能像我們本地測(cè)試一樣,不在乎采集效率,只要能正確提取結(jié)果就行。在這個(gè)時(shí)間就是金錢的年代,不可能給你時(shí)間去慢慢的采集,所以單線程爬蟲程序是行不通的,我們需要將單線程改成多線程的模式,來(lái)提升采集效率和提高計(jì)算機(jī)利用率。
多線程的爬蟲程序設(shè)計(jì)比單線程就要復(fù)雜很多,但是與其他業(yè)務(wù)在高并發(fā)下要保證數(shù)據(jù)安全又不同,多線程爬蟲在數(shù)據(jù)安全上到要求不是那么的高,因?yàn)槊總€(gè)頁(yè)面都可以被看作是一個(gè)獨(dú)立體。要做好多線程爬蟲就必須做好兩點(diǎn):第一點(diǎn)就是統(tǒng)一的待采集 URL 維護(hù),第二點(diǎn)就是 URL 的去重,下面我們簡(jiǎn)單的來(lái)聊一聊這兩點(diǎn)。
維護(hù)待采集的 URL
多線程爬蟲程序就不能像單線程那樣,每個(gè)線程獨(dú)自維護(hù)這自己的待采集 URL,如果這樣的話,那么每個(gè)線程采集的網(wǎng)頁(yè)將是一樣的,你這就不是多線程采集啦,你這是將一個(gè)頁(yè)面采集的多次?;谶@個(gè)原因我們就需要將待采集的 URL 統(tǒng)一維護(hù),每個(gè)線程從統(tǒng)一 URL 維護(hù)處領(lǐng)取采集 URL ,完成采集任務(wù),如果在頁(yè)面上發(fā)現(xiàn)新的 URL 鏈接則添加到 統(tǒng)一 URL 維護(hù)的容器中。下面是幾種適合用作統(tǒng)一 URL 維護(hù)的容器:
JDK 的安全隊(duì)列,例如 LinkedBlockingQueue
高性能的 NoSQL,比如 Redis、Mongodb
MQ 消息中間件
URL 的去重
URL 的去重也是多線程采集的關(guān)鍵一步,因?yàn)槿绻蝗ブ氐脑挘敲次覀儗⒉杉酱罅恐貜?fù)的 URL,這樣并沒有提升我們的采集效率,比如一個(gè)分頁(yè)的新聞列表,我們?cè)诓杉谝豁?yè)的時(shí)候可以得到 2、3、4、5 頁(yè)的鏈接,在采集第二頁(yè)的時(shí)候又會(huì)得到 1、3、4、5 頁(yè)的鏈接,待采集的 URL 隊(duì)列中將存在大量的列表頁(yè)鏈接,這樣就會(huì)重復(fù)采集甚至進(jìn)入到一個(gè)死循環(huán)當(dāng)中,所以就需要 URL 去重。URL 去重的方法就非常多啦,下面是幾種常用的 URL 去重方式:
將 URL 保存到數(shù)據(jù)庫(kù)進(jìn)行去重,比如 redis、MongoDB
將 URL 放到哈希表中去重,例如 hashset
將 URL 經(jīng)過 MD5 之后保存到哈希表中去重,相比于上面一種,能夠節(jié)約空間
使用 布隆過濾器(Bloom Filter)去重,這種方式能夠節(jié)約大量的空間,就是不那么準(zhǔn)確。
關(guān)于多線程爬蟲的兩個(gè)核心知識(shí)點(diǎn)我們都知道啦,下面我畫了一個(gè)簡(jiǎn)單的多線程爬蟲架構(gòu)圖,如下圖所示:
多線程爬蟲架構(gòu)圖
上面我們主要了解了多線程爬蟲的架構(gòu)設(shè)計(jì),接下來(lái)我們不妨來(lái)試試 Java 多線程爬蟲,我們以采集虎撲新聞為例來(lái)實(shí)戰(zhàn)一下 Java 多線程爬蟲,Java 多線程爬蟲中設(shè)計(jì)到了 待采集 URL 的維護(hù)和 URL 去重,由于我們這里只是演示,所以我們就使用 JDK 內(nèi)置的容器來(lái)完成,我們使用 LinkedBlockingQueue 作為待采集 URL 維護(hù)容器,HashSet 作為 URL 去重容器。下面是 Java 多線程爬蟲核心代碼,詳細(xì)代碼以上傳 GitHub,地址在文末:
我們用 5 個(gè)線程去采集虎撲新聞列表頁(yè)看看效果如果?運(yùn)行該程序,得到如下結(jié)果:
多線程采集結(jié)果
結(jié)果中可以看出,我們啟動(dòng)了 5 個(gè)線程采集了 61 頁(yè)頁(yè)面,一共耗時(shí) 2 秒鐘,可以說(shuō)效果還是不錯(cuò)的,我們來(lái)跟單線程對(duì)比一下,看看差距有多大?我們將線程數(shù)設(shè)置為 1 ,再次啟動(dòng)程序,得到如下結(jié)果:
單線程運(yùn)行結(jié)果
可以看出單線程采集虎撲 61 條新聞花費(fèi)了 7 秒鐘,耗時(shí)差不多是多線程的 4 倍,你想想這可只是 61 個(gè)頁(yè)面,頁(yè)面更多的話,差距會(huì)越來(lái)越大,所以多線程爬蟲效率還是非常高的。
分布式爬蟲架構(gòu)
分布式爬蟲架構(gòu)是一個(gè)大型采集程序才需要使用的架構(gòu),一般情況下使用單機(jī)多線程就可以解決業(yè)務(wù)需求,反正我是沒有分布式爬蟲項(xiàng)目的經(jīng)驗(yàn),所以這一塊我也沒什么可以講的,但是我們作為技術(shù)人員,我們需要對(duì)技術(shù)保存熱度,雖然不用,但是了解了解也無(wú)妨,我查閱了不少資料得出了如下結(jié)論:
分布式爬蟲架構(gòu)跟我們多線程爬蟲架構(gòu)在思路上來(lái)說(shuō)是一樣的,我們只需要在多線程的基礎(chǔ)上稍加改進(jìn)就可以變成一個(gè)簡(jiǎn)單的分布式爬蟲架構(gòu)。因?yàn)榉植际脚老x架構(gòu)中爬蟲程序部署在不同的機(jī)器上,所以我們待采集的 URL 和 采集過的 URL 就不能存放在爬蟲程序機(jī)器的內(nèi)存中啦,我們需要將它統(tǒng)一在某臺(tái)機(jī)器上維護(hù)啦,比如存放在 Redis 或者 MongoDB 中,每臺(tái)機(jī)器都從這上面獲取采集鏈接,而不是從 LinkedBlockingQueue 這樣的內(nèi)存隊(duì)列中取鏈接啦,這樣一個(gè)簡(jiǎn)單的分布式爬蟲架構(gòu)就出現(xiàn)了,當(dāng)然這里面還會(huì)有很多細(xì)節(jié)問題,因?yàn)槲覜]有分布式架構(gòu)的經(jīng)驗(yàn)
-
JAVA
+關(guān)注
關(guān)注
20文章
2989瀏覽量
109785 -
多線程
+關(guān)注
關(guān)注
0文章
279瀏覽量
20445
發(fā)布評(píng)論請(qǐng)先 登錄
多線程的安全注意事項(xiàng)
工控一體機(jī)多線程任務(wù)調(diào)度優(yōu)化:聚徽分享破解工業(yè)復(fù)雜流程高效協(xié)同密碼
一種實(shí)時(shí)多線程VSLAM框架vS-Graphs介紹

請(qǐng)問如何在Python中實(shí)現(xiàn)多線程與多進(jìn)程的協(xié)作?
請(qǐng)問rt-thread studio如何進(jìn)行多線程編譯?
ADS1232信號(hào)輸入從零點(diǎn)到滿度兩點(diǎn)變化,其數(shù)據(jù)建立過程需要兩次到三次轉(zhuǎn)換,為什么?
IP地址數(shù)據(jù)信息和爬蟲攔截的關(guān)聯(lián)
socket 多線程編程實(shí)現(xiàn)方法
Python中多線程和多進(jìn)程的區(qū)別

評(píng)論