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 bem

desde já, obrigado

questionAnswers(1)

yourAnswerToTheQuestion