我正在玩一個簡單的多線程網絡爬蟲。我看到很多消息來源都將抓取工具稱爲「明顯並行」,因爲您可以從不同的URL進行抓取,但我從來沒有看到他們討論過抓取工具如何處理他們以前見過的URL。看起來,某種全球地圖對於避免重複搜索同一頁面至關重要,但是關鍵部分將如何構建?爲了最大限度地提高性能,鎖具有多細密?我只想看到一個不太密集,不太簡單的例子。併發的Web爬蟲是否通常將訪問的URL存儲在併發映射中,或者使用同步來避免同一頁面爬行兩次?
回答
如果你堅持只使用Java併發框架做,那麼ConcurrentHashMap
可能是要走的路。其中有趣的方法是ConcurrentHashMap.putIfAbsent
方法,它會給你非常好的效率,並且如何使用它的想法是:
你會有一些來自爬網頁面的「多線程來源URL地址」 - 你可以使用一些併發隊列來存儲它們,或者只是創建一個帶有(無界?)隊列的ExecutorService,在該隊列中放置可以抓取URL的Runnables。
爬行內可運行您應該有已經抓取網頁這種常見的ConcurrentHashMap的引用,並在很開始run
方法做:
private final ConcurrentHashMap<String, Long> crawledPages = new ConcurrentHashMap<String, Long>();
...
private class Crawler implements Runnable {
private String urlToBeCrawled;
public void Crawler(String urlToBeCrawled) {
this.urlToBeCrawled = urlToBeCrawled;
}
public void run() {
if (crawledPages.putIfAbsent(urlToBeCrawled, System.currentTimeMillis())==null) {
doCrawlPage(urlToBeCrawled);
}
}
}
如果crawledPages.putIfAbsent(urlToBeCrawled)
將返回null給你,然後你知道這個網頁沒有被任何人抓取,因爲這個方法原子地放置了你爬行這個頁面的進展值 - 你是幸運線程,如果它會返回一個非空值,那麼你知道有人已經照顧關於這個URL,所以你的runnable應該完成了,並且線程返回到下一個Runnable使用的池。
履帶不使用的ConcurrentHashMap,而使用DATABSE
visisted URL的數量會增長得非常快,所以它不是將它們存儲在內存中的好東西,更好的使用databese,商店網址和上次抓取的日期,然後只檢查該網址是否已存在於數據庫中或有資格進行刷新。我在嵌入模式下使用了一個Derby數據庫,它對我的網絡爬蟲非常有用。我不建議像H2一樣在內存數據庫中使用,因爲隨着爬網頁數的增加,你最終會得到OutOfMemoryException。
您很少會在同一時間內多次抓取同一頁面,因此檢查數據庫是否最近已經被抓取,足以避免浪費大量資源「重新抓取相同的頁面並結束「。我相信這是「一個很好的解決方案,不是太密集,也不是太簡單」
另外,使用Databse和url的「上次訪問日期」,您可以在需要時停止並繼續工作,使用ConcurrentHashMap當應用程序退出時,會丟失所有結果您可以使用「上次訪問日期」來確定網址是否需要重新抓取。
您可以使用ConcurrentHashMap
來存儲以查找重複的網址。 ConcurrentHashMap
也使用分離鎖定機制,而不是使用全局鎖定。
或者您可以使用您自己的實現,您可以將所有數據拆分爲不同的密鑰。
番石榴API
Striped<ReadWriteLock> rwLockStripes = Striped.readWriteLock(10);
String key = "taskA";
ReadWriteLock rwLock = rwLockStripes.get(key);
try{
rwLock.lock();
.....
}finally{
rwLock.unLock();
}
的ConcurrentHashMap實例的實例
private Set<String> urls = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
特定域的用例:使用內存
如果是特定於域說abc.com那麼最好是有vistedURL設置或內存並行哈希映射,在內存中會更快,檢查訪問狀態,內存消耗會相對較少。數據庫將有IO開銷並且代價高昂,訪問狀態檢查將非常頻繁。它會大大地影響你的表現。根據您的使用情況,您可以在內存或數據庫中使用。我的用例特定於訪問URL不會被再次訪問的域,所以我使用了Concurrent哈希映射。
- 1. 網絡爬蟲是否僅依靠主頁上的鏈接來進行爬網?
- 2. 使用自定義爬蟲訪問所有分頁頁面
- 3. 面對Go同步映射的併發問題
- 4. 如何檢查我的網站是否使用爬蟲訪問?
- 5. 使用python beautifulsoup進行網頁爬蟲
- 6. 檢測訪問者是瀏覽器而不是爬蟲
- 7. Ruby,Mongodb,Anemone:可能會發生內存泄漏的Web爬蟲?
- 8. 通過IIS URL Rewrite模塊發送訪問者同意頁面
- 9. 從Web頁面(Web爬蟲)中提取所有鏈接
- 10. 單頁網頁爬蟲PHP
- 11. Python爬蟲的問題
- 12. C#web和ftp爬蟲庫
- 13. Web爬蟲用單引號截斷URL。可能是壞的sitemap.xml?
- 14. 爬蟲程序會在什麼時候通知頁面是404?
- 15. 同步兩種方法並避免同時運行
- 16. 如何使用Node.js創建Web爬蟲?
- 17. 我可以告訴網站爬蟲訪問某個頁面嗎?
- 18. 什麼是Ruby Web爬蟲庫允許xpath訪問和等同於保存爲網頁完成?
- 19. 如何在ASP.NET中創建Web爬蟲?
- 20. 的Web爬行使用PHP
- 21. 在MYSQL中使用JAVA網絡爬蟲存儲印地文詞
- 22. 有沒有免費的PHP爬蟲?
- 23. Python爬蟲 - html.fromstring
- 24. 爬蟲實例
- 25. 網絡爬蟲
- 26. 遞歸爬行使用javascript與scrapy和splash的相同頁面
- 27. 併發/同步問題
- 28. 用飛鏢寫的網頁爬蟲
- 29. 同步數據,以避免造成同一觀察者再次
- 30. MYSQL避免插入同一行兩次
由於您只是想插入一組訪問過的網址,並檢查該網址是否存在,那麼設置redis可能是最好的解決方案。您也可以存儲一個散列,其中密鑰將成爲訪問的網址,而值則是上次訪問的日期。在任何情況下,幾乎任何類型的數據庫都比內存中的結構更好。 – Vasil
這很好,但我仍然想確定避免競爭條件所需的最小交易保證。我很欣賞數據庫是要走的路,但是這個項目本質上主要是自學的,如果我只是把它扔到數據庫中,我就會失去它。將問題限制爲使用數據結構可以讓我看到引擎蓋下的所有事情,即關鍵部分。 –