多线程网页爬虫
难度:
标签:
题目描述
代码结果
运行时间: 206 ms, 内存: 0.0 MB
/**
* Leetcode 1242: Web Crawler Multithreaded
* Problem statement: Given a start URL and an interface HtmlParser, implement a multithreaded web crawler to visit all URLs from the same domain.
*
* Approach:
* 1. Use a ConcurrentHashMap to store visited URLs for thread safety.
* 2. Use a ForkJoinPool to parallelize the crawling process.
* 3. For each URL, we create a task that fetches the URLs from the page.
* 4. Filter and add new URLs from the same domain to the pool for further processing.
*/
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.stream.Collectors;
class WebCrawlerStream {
private Set<String> visited = ConcurrentHashMap.newKeySet();
private HtmlParser parser;
private String startHost;
public WebCrawlerStream(HtmlParser parser) {
this.parser = parser;
}
public void startCrawling(String startUrl) {
startHost = getHostName(startUrl);
visited.add(startUrl);
ForkJoinPool.commonPool().invoke(new CrawlTask(startUrl));
}
private class CrawlTask extends RecursiveAction {
private String url;
CrawlTask(String url) {
this.url = url;
}
@Override
protected void compute() {
if (getHostName(url).equals(startHost)) {
parser.getUrls(url).stream()
.filter(nextUrl -> getHostName(nextUrl).equals(startHost))
.filter(visited::add)
.map(CrawlTask::new)
.collect(Collectors.toList())
.forEach(ForkJoinPool.commonPool()::invoke);
}
}
}
private String getHostName(String url) {
int idx = url.indexOf('/', 7); // Skip the 'http://' part
return idx == -1 ? url : url.substring(0, idx);
}
}
解释
方法:
该题解采用了多线程来加速网页爬虫的过程。首先,定义了一个帮助函数 `get_hostname` 用来从 URL 中提取主机名。在主函数 `crawl` 中,首先使用 `get_hostname` 得到初始 URL 的主机名。然后,使用一个集合 `seen` 来跟踪已经访问过的 URL,避免重复访问。接下来,使用列表 `to_visit` 来存储当前层待访问的 URL,并使用列表 `res` 来存储所有被访问过的 URL。对于 `to_visit` 中的每个 URL,使用线程池 `ThreadPoolExecutor` 来并行获取每个 URL 链接到的新 URL。对于每个新 URL,如果它未被访问过且与起始 URL 属于同一主机,就将其添加到 `seen`,`res`,以及下一轮的 `to_visit` 中。这个过程重复进行,直到没有新的 URL 可以访问。
时间复杂度:
O(n^2)
空间复杂度:
O(n)
代码细节讲解
🦆
在多线程网页爬虫中,如何确保对`seen`集合的访问是线程安全的?
▷🦆
你选择使用`ThreadPoolExecutor`的`max_workers`参数设为16的依据是什么?是否有可能因为过多线程而导致资源竞争增加?
▷🦆
在爬虫过程中,`to_visit`列表是如何保证不重复添加相同的URL的,特别是在多线程环境下?
▷