我正在编写一个简单的多线程Web搜寻器。我看到许多消息源都将Web爬网程序视为并行的,因为您可以开始从不同的URL进行爬网,但是我从未见过他们讨论过Web爬网程序如何处理他们之前已经看到的URL。似乎某种全局 map 对于避免一遍又一遍地重新爬网相同页面至关重要,但是关键部分将如何构成?锁的粒度如何才能最大化性能?我只是想看看一个很好的例子,它不太密集也不简单。
最佳答案
如果您坚持只使用Java并发框架来做到这一点,那么ConcurrentHashMap
可能是可行的方法。其中有趣的方法是ConcurrentHashMap.putIfAbsent
方法,它将为您带来非常好的效率,并且如何使用它的想法是:
您将从爬网的页面中获得一些“传入URL地址的多线程源”-您可以使用一些并发队列来存储它们,或者只创建带有(无边界?)队列的ExecutorService,在其中放置将对URL进行爬网的Runnables。
在爬网的Runnable内部,您应该已经引用了这个已经爬网的页面的常见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,则说明您知道该页面未被任何人抓取,因为此方法从根本上将使您可以继续抓取该页面的值放入-您是幸运的线程,如果它将返回非null的话值,那么您就知道有人已经在处理此url,因此您的runnable应该会完成,并且线程会返回到池中,以供下一个Runnable使用。关于java - 并发Web爬网程序是否通常将访问的URL存储在并发映射中,还是使用同步来避免对同一页面进行两次爬网?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45405321/