gpt4 book ai didi

java - 提高crawler4j的性能

转载 作者:塔克拉玛干 更新时间:2023-11-02 08:06:52 24 4
gpt4 key购买 nike

我需要编写一个网络抓取工具来抓取大约 100 万个网站并将它们的标题、描述和关键字保存到一个大文件中(包含抓取的 URL 和相关词)。应该从一个大文件中提取 URL。

我已经在 1M 的 URL 文件上运行了 Crawler4j,并使用以下代码启动了网络爬虫:controller.start(MyCrawler.class, 20)。 20 是一个任意数字。每个爬虫将结果词传递到一个阻塞队列中,以便单个线程将这些词和 URL 写入文件。为了不在文件上同步,我使用了 1 个编写器线程。我设置爬取深度为0(我只需要爬取我的种子列表)

运行一整晚之后,我只下载了大约 200K 个 URL。我使用有线连接在一台机器上运行爬虫。由于大多数 URL 属于不同的主机,因此我认为礼貌参数在这里没有任何重要性。

编辑

我尝试使用非阻塞启动方式启动 Crawler4j,但它被阻塞了。我的 Crawler4j 版本是:4.2。这是我正在使用的代码:

CrawlConfig config = new CrawlConfig();
List<Header> headers = Arrays.asList(
new BasicHeader("Accept", "text/html,text/xml"),
new BasicHeader("Accept-Language", "en-gb, en-us, en-uk")
);
config.setDefaultHeaders(headers);
config.setCrawlStorageFolder(crawlStorageFolder);
config.setMaxDepthOfCrawling(0);
config.setUserAgentString("testcrawl");
config.setIncludeBinaryContentInCrawling(false);
config.setPolitenessDelay(10);

PageFetcher pageFetcher = new PageFetcher(config);
RobotstxtConfig robotstxtConfig = new RobotstxtConfig();
RobotstxtServer robotstxtServer = new RobotstxtServer(robotstxtConfig, pageFetcher);

BlockingQueue<String> urlsQueue = new ArrayBlockingQueue<>(400);
controller = new CrawlController(config, pageFetcher, robotstxtServer);

ExecutorService executorService = Executors.newSingleThreadExecutor();
Runnable writerThread = new FileWriterThread(urlsQueue, crawlStorageFolder, outputFile);

executorService.execute(writerThread);

controller.startNonBlocking(() -> {
return new MyCrawler(urlsQueue);
}, 4);

File file = new File(urlsFileName);
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
String url;
while ((url = br.readLine()) != null) {
controller.addSeed(url);
}
}

编辑 1 - 这是 MyCrawler 的代码

public class MyCrawler extends WebCrawler {
private final static Pattern FILTERS = Pattern.compile(".*(\\.(css|js|gif|jpg|png|mp3|mp3|zip|gz))$");
public static final String DELIMETER = "||||";
private final StringBuilder buffer = new StringBuilder();
private final BlockingQueue<String> urlsQueue;

public MyCrawler(BlockingQueue<String> urlsQueue) {
this.urlsQueue = urlsQueue;
}

@Override
public boolean shouldVisit(Page referringPage, WebURL url) {
String href = url.getURL().toLowerCase();
return !FILTERS.matcher(href).matches();
}

@Override
public void visit(Page page) {
String url = page.getWebURL().getURL();

if (page.getParseData() instanceof HtmlParseData) {
HtmlParseData parseData = (HtmlParseData) page.getParseData();
String html = parseData.getHtml();
String title = parseData.getTitle();

Document document = Jsoup.parse(html);
buffer.append(url.replaceAll("[\n\r]", "")).append(DELIMETER).append(title);
Elements descriptions = document.select("meta[name=description]");
for (Element description : descriptions) {
if (description.hasAttr("content"))
buffer.append(description.attr("content").replaceAll("[\n\r]", ""));
}

Elements elements = document.select("meta[name=keywords]");
for (Element element : elements) {
String keywords = element.attr("content").replaceAll("[\n\r]", "");
buffer.append(keywords);
}
buffer.append("\n");
String urlContent = buffer.toString();
buffer.setLength(0);
urlsQueue.add(urlContent);
}
}

private boolean isSuccessful(int statusCode) {
return 200 <= statusCode && statusCode < 400;
}
}

所以我有两个问题:

  1. 有人可以建议任何其他方法来缩短此过程所需的时间吗?也许以某种方式调整爬虫线程的数量?也许其他一些优化?我更喜欢不需要多台机器的解决方案,但如果您认为这是发挥作用的唯一方法,有人可以建议如何做到这一点吗?也许是代码示例?
  2. 有没有什么方法可以让抓取工具开始处理某些网址并在抓取过程中不断添加更多网址?我查看了 crawler.startNonBlocking,但它似乎工作得不是很好

提前致谢

最佳答案

crawler4j 默认设计为在一台机器上运行。从网络爬虫领域我们知道,网络爬虫的性能主要取决于以下四种资源:

  • 磁盘
  • 中央处理器
  • 带宽
  • (内存)

定义最佳线程数取决于您的硬件设置。因此,更多的机器将导致更高的吞吐量。下一个硬限制是网络带宽。如果您没有通过高速互联网连接,这将成为您方法的瓶颈。

此外,crawler4j 并非设计用于默认加载如此巨大的种子文件。这是因为 crawler4j 尊重爬虫策略。这意味着,在抓取开始之前,每个种子点都将被检查是否有 robots.txt,这可能会花费相当多的时间。

如果以非阻塞模式启动爬网,则在启动爬网后添加种子是可能的并且应该有效。但是,处理 URL 可能需要一段时间。

对于多机设置,您可以查看 Apache Nutch .然而,Nutch 有点难学。

编辑:

重现您的设置后,我能够以动态方式回答您关于添加种子页面的问题。

以这种方式启动爬虫

controller.startNonBlocking(() -> {
return new MyCrawler(urlsQueue);
}, 4);

将调用每个爬虫线程的 run() 方法。研究这个方法,我们发现一个名为 frontier.getNextURLs(50, assignedURLs); 的方法,它负责从 frontier 中获取看不见的 URL 以便处理它们。在这个方法中,我们找到了一个所谓的waitingList,它会导致线程等待。由于 notifyAll 永远不会在 waitingList 上调用,直到 Controller 关闭,线程永远不会重新安排新的 URL。

要克服这个问题,您有两种可能的解决方案:

  1. 只需为每个线程添加至少一个 URL 作为种子点。不会出现死锁情况。以非阻塞模式启动线程后,您可以随意添加种子。

    controller.addSeed("https://www.google.de");

    controller.startNonBlocking(() -> {
    return new MyCrawler(urlsQueue);
    }, 4);

    controller.addSeed("https://www.google.de/test");

    controller.waitUntilFinish();
  2. 从 Github 项目中获取一个分支并修改 Frontier.java 的代码,以便可以从动态添加种子页面后的 CrawlController

关于java - 提高crawler4j的性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35405116/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com