【问题标题】:Multithreaded DFS for web crawler in JavaJava中用于网络爬虫的多线程DFS
【发布时间】:2016-01-24 18:13:35
【问题描述】:

我正在使用 Jsoup 在 Java 中编写一个网络爬虫。

目前我有一个使用深度优先搜索的单线程实现(它只需要爬取一个域,因此我可以选择 DFS 或 BFS,并选择 DFS,因为这意味着我可以使用队列而不是堆栈,因此在我执行多线程版本时使用LinkedBlockingQueue

我有一个要访问的链接的Queue 和一个已经访问过的链接的HashSet,我的主循环从队列中弹出一个链接,访问页面,并将页面中所有未访问的链接添加到队列中.

这是我的类实现我的单线程实现的内容(如果任何 throws 声明是虚假的,请告诉我为什么,因为我需要掌握它)

private static LinkedBlockingQueue<String> URLSToCrawl = new LinkedBlockingQueue<String>();
private static String baseURL;
private static String HTTPSBaseURL;
private static HashSet<String> alreadyCrawledSet = new HashSet<String>();
private static List<String> deadLinks = new LinkedList<String>();

public static void main(String[] args) throws IOException, InterruptedException {

    // should output a site map, showing the static assets for each page. 

    Validate.isTrue(args.length == 1, "usage: supply url to fetch");

    baseURL = args[0];
    HTTPSBaseURL = baseURL.replace("http://", "https://");

    alreadyCrawledSet.add(baseURL);
    URLSToCrawl.add(baseURL);

    while (!URLSToCrawl.isEmpty() ) {
        String url = URLSToCrawl.take();
        crawlURL(url);
    }


}

private static void crawlURL(String url) throws IOException, InterruptedException {
    print("%s", url);
    try {
        Document doc = Jsoup.connect(url).get();
        Elements links = doc.select("a[href]");

        for (Element link : links) {
            String linkURL = link.attr("abs:href");
            if (sameDomain(linkURL) && !alreadyCrawled(linkURL)) {
                alreadyCrawledSet.add(linkURL);
                URLSToCrawl.put(linkURL);
            }
        }
    } catch (HttpStatusException e) {
        deadLinks.add(url);
    }
}   

private static boolean alreadyCrawled(String url) {
    if (alreadyCrawledSet.contains(url)) {
        return true;
    } else {
        return false;
    }
}

我想让这个多线程,以利用单线程实现必须等待 Jsoup.connect(url).get() 调用中的 HTTP 请求返回才能继续处理的事实。我希望通过允许多个线程同时执行,一些工作将在这个 I/O 绑定延迟期间完成,从而加快程序。

我对并发不是很有经验 - 我的第一个想法是简单地创建一个 Executor 并将每个对 crawlURL 的调用提交给它。但我很困惑 - 我不知道如何确保以线程安全的方式访问我的 HashSetQueue,尤其是考虑到每个线程不仅 使用来自 Queue 的 URL但也会将新的 URL 推送到 Queue

我了解原子性概念的基础知识,以及线程可以“锁定”共享资源的想法,但我不知道如何在这种情况下将它们付诸实践。

有人对制作这个多线程有什么建议吗?

【问题讨论】:

    标签: java multithreading web-crawler executorservice depth-first-search


    【解决方案1】:

    我的解决方案是一次处理一层图表。因此,对于每个级别,将每个链接提交到要抓取的ExecutorService(多线程),然后等待该级别完成(使用CountDownLatch),然后再进入下一个级别。

    我使用FixedThreadPool 作为速率限制的一种形式。

    (最初我尝试异步调度每个 url,这一定更有效,但我不知道如何终止整个事情。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-01
      相关资源
      最近更新 更多