Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 提高crawler4j的性能_Java_Multithreading_Optimization_Web Scraping_Crawler4j - Fatal编程技术网

Java 提高crawler4j的性能

Java 提高crawler4j的性能,java,multithreading,optimization,web-scraping,crawler4j,Java,Multithreading,Optimization,Web Scraping,Crawler4j,我需要写一个webscraper,它可以抓取约100万个网站,并将它们的标题、描述和关键字保存到一个大文件中(包含抓取的URL和相关单词)。URL应该从一个大文件中提取 我在1M URL文件上运行了Crawler4j,并使用以下命令启动了webcrawler:controller.start(MyCrawler.class,20)。20是一个任意数。每个爬虫程序将生成的单词传递到一个阻塞队列中,以便单个线程将这些单词和URL写入文件。为了不同步文件,我使用了1个writer线程。我将爬网深度设置

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

我在1M URL文件上运行了Crawler4j,并使用以下命令启动了webcrawler:
controller.start(MyCrawler.class,20)
。20是一个任意数。每个爬虫程序将生成的单词传递到一个阻塞队列中,以便单个线程将这些单词和URL写入文件。为了不同步文件,我使用了1个writer线程。我将爬网深度设置为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);
    }
}
CrawlConfig config=new CrawlConfig();
列表标题=Arrays.asList(
新的BasicHeader(“接受”、“文本/html、文本/xml”),
新BasicHeader(“接受语言”、“英国、美国、英国”)
);
config.setDefaultHeaders(headers);
config.setCrawlStorageFolder(crawlStorageFolder);
config.setMaxDepthOfCrawling(0);
config.setUserAgentString(“testcrawl”);
config.setIncludeBinaryContentInCrawling(false);
配置设置礼貌延迟(10);
PageFetcher PageFetcher=新的PageFetcher(配置);
RobotstxtConfig RobotstxtConfig=new RobotstxtConfig();
RobotstxtServer RobotstxtServer=新的RobotstxtServer(robotstxtConfig,pageFetcher);
BlockingQueue URLSQUUE=新的ArrayBlockingQueue(400);
控制器=新的爬行控制器(配置、页面抓取器、robotstxtServer);
ExecutorService ExecutorService=Executors.newSingleThreadExecutor();
Runnable writerThread=新文件writerThread(urlsQueue、crawlStorageFolder、outputFile);
executorService.execute(writerThread);
控制器。启动非阻塞(()->{
返回新的MyCrawler(URLSQUUE);
}, 4);
文件=新文件(urlsFileName);
try(BufferedReader br=new BufferedReader(new FileReader(file))){
字符串url;
而((url=br.readLine())!=null){
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;
    }
}
公共类MyCrawler扩展WebCrawler{
private final static Pattern FILTERS=Pattern.compile(*(\\(css|js|gif | jpg | png | mp3 | mp3 | zip | gz))$;
公共静态最终字符串DELIMETER=“| | | |”;
私有最终StringBuilder缓冲区=新StringBuilder();
私有最终阻塞队列URLSQUUE;
公共MyCrawler(阻止队列URLSQUUE){
this.urlsQueue=urlsQueue;
}
@凌驾
公共布尔值应访问(页面引用页面、WebURL){
String href=url.getURL().toLowerCase();
return!FILTERS.matcher(href.matches();
}
@凌驾
公众访问(第页){
字符串url=page.getWebURL().getURL();
if(page.getParseData()实例为HtmlParseData){
HtmlParseData parseData=(HtmlParseData)page.getParseData();
字符串html=parseData.getHtml();
String title=parseData.getTitle();
documentdocument=Jsoup.parse(html);
buffer.append(url.replaceAll(“[\n\r]”,“”)).append(DELIMETER.append(title);
元素描述=文档。选择(“元[名称=描述]”;
用于(元素描述:描述){
if(description.hasAttr(“内容”))
buffer.append(description.attr(“content”).replaceAll([\n\r],”);
}
元素=文档。选择(“元[名称=关键字]”;
for(元素:元素){
字符串关键字=element.attr(“内容”).replaceAll([\n\r],”);
buffer.append(关键字);
}
buffer.append(“\n”);
字符串urlContent=buffer.toString();
buffer.setLength(0);
添加(urlContent);
}
}
私有布尔值isSuccessful(int状态码){

return 200
crawler4j
默认设计为在一台机器上运行。从
web爬行
字段中,我们知道,web爬行器的性能主要取决于以下四种资源:

  • 圆盘
  • 中央处理器
  • 带宽
  • (RAM)
定义最佳线程数取决于您的硬件设置。因此,更多的计算机将导致更高的吞吐量。下一个硬限制是网络带宽。如果您没有通过高速Internet连接,这将是您方法的瓶颈

此外,
crawler4j
的设计不是为了在默认情况下加载如此大的种子文件。这是因为
crawler4j
重新检查了爬虫程序的策略性。这意味着,在爬网开始之前,会检查每个种子点是否有
robots.txt
,这可能需要相当长的时间

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

对于多机设置,你可以看一看。然而,Nutch有点难学

编辑:

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

以这种方式启动爬虫程序

controller.startNonBlocking(() -> {
    return new MyCrawler(urlsQueue);
}, 4);
将调用每个爬虫线程的
run()
方法。研究此方法,我们发现一个名为
frontier.getNextURLs(50,assignedURLs)的方法;
,负责从frontier获取看不见的URL以便对其进行处理。在该方法中,我们找到一个所谓的
等待列表
,它会导致线程等待。因为
notifyAll
controller.addSeed("https://www.google.de");

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

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

controller.waitUntilFinish();