[英]Multithreaded DFS for web crawler in Java
我正在使用Jsoup用Java编写Web搜寻器。
目前,我有一个使用深度优先搜索的单线程实现(它只需要爬网一个域,因此我可以选择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请求返回然后继续处理的Jsoup.connect(url).get()
。 我希望通过允许多个线程同时作用,可以在此I / O约束延迟期间完成一些工作,从而加快程序的速度。
我对并发不是很有经验-我的第一个想法是简单地创建一个Executor
然后将每个对crawlURL
调用提交给它。 但是我很困惑-我不知道如何确保以线程安全的方式访问我的HashSet
和Queue
,特别是考虑到每个线程不仅消耗 Queue
URL,还将新URL推送到Queue
。
我了解原子性概念的基础,以及线程可以“锁定”共享资源的想法,但是我不知道如何在这种情况下将它们付诸实践。
有没有人建议使这种多线程?
我的解决方案是一次处理一张图表。 因此,对于每个级别,将每个链接提交到要爬网(多线程)的ExecutorService
,然后等待该级别完成(使用CountDownLatch
),然后再进入下一个级别。
我使用了FixedThreadPool
作为速率限制的一种形式。
(最初,我试图异步地分派每个URL,这必须更加有效,但我无法弄清楚如何终止整个过程。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.