我正在使用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请求返回才能继续处理的事实。我希望通过允许多个线程立即执行操作,一些工作将在此I / O限制延迟期间完成,从而加快程序。
我对并发性不是很有经验 - 我的第一个想法就是简单地创建一个Executor
并将每次调用提交给crawlURL
。但我很困惑 - 我不知道如何确保以线程安全的方式访问我的HashSet
和Queue
,特别是考虑到每个线程不仅使用来自Queue
,但也将新网址推送到Queue
。
我理解原子性概念的基础知识,以及线程可以“锁定”共享资源的想法,但我不知道如何在这种情况下将它们付诸实践。
有没有人对制作这个多线程有任何建议?
答案 0 :(得分:0)
我的解决方案是一次处理一层图表。因此,对于每个级别,将每个链接提交到要进行爬网的ExecutorService
(多线程),然后等待该级别完成(使用CountDownLatch
),然后再进入下一级别。
我使用FixedThreadPool
作为速率限制的一种形式。
(最初我试图只是异步调度每个url,这必须更高效,但我无法弄清楚如何终止整个事情。)