我正在编写一个简单的多线程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/

10-09 01:12