Melhorando o desempenho do crawler4j
Preciso escrever um webscraper que raspeie em torno de 1 milhão de sites e salve o título, a descrição e as palavras-chave em um arquivo grande (contendo o URL raspado e as palavras relacionadas). Os URLs devem ser extraídos de um arquivo grande.
Executei o Crawler4j no arquivo de 1 milhão de URLs e iniciei o webcrawler usando este:controller.start(MyCrawler.class, 20)
. 20 é um número arbitrário. Cada rastreador passa as palavras resultantes em uma fila de bloqueio para que um único thread grave essas palavras e URL no arquivo. Eu usei um thread de gravador para não sincronizar no arquivo. Defino a profundidade do rastreamento como 0 (só preciso rastrear minha lista de sementes)
Depois de executar isso durante a noite, baixei apenas cerca de 200 mil URLs. Estou executando o raspador em uma máquina usando uma conexão com fio. Como a maioria dos URLs é de hosts diferentes, não acho que o parâmetro polidez tenha importância aqui.
EDITAR
Tentei iniciar o Crawler4j usando o início sem bloqueio, mas ele ficou bloqueado. Minha versão do Crawler4j é: 4.2. Este é o código que estou usando:
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);
}
}
EDIT 1 - Este é o código para o 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;
}
}
E então eu tenho 2 perguntas:
alguém pode sugerir outra maneira de fazer esse processo levar menos tempo? Talvez de alguma forma ajustando o número de threads do rastreador? Talvez algumas outras otimizações? Prefiro uma solução que não exija várias máquinas, mas se você acha que essa é a única maneira de atuar, alguém poderia sugerir como fazer isso? talvez um exemplo de código?Existe alguma maneira de fazer o rastreador começar a trabalhar em alguns URLs e continuar adicionando mais URLs durante o rastreamento? Eu olheicrawler.startNonBlocking
mas não parece funcionar muito bemdesde já, obrigado