Каков правильный метод использования циклов внутри функции очереди кластера?
У меня есть массив из 4656 URL-адресов, для индивидуального посещения которых требуется около 77 минут с одним экземпляром кукловода для каждого URL-адреса посещена, страница оценивается, и для получения всех элементов в определенном классе используется a for l oop.
Я хочу существенно сократить это время, так что кукловод-кластер определенно привлек мой интерес.
Пытаясь использовать его, я сначала подготовил URL 4656, чтобы их могли посещать 8 одновременных экземпляров / кластеров кукловода. Я сделал это, разделив 4656 URL-адресов на 8 массивов, содержащих по 582 URL-адреса каждый.
const concurrentGroupLength = Math.round(urlArray.length / 8);
const concurrentGroup1 = urlArray.slice(0, concurrentGroupLength);
const concurrentGroup2 = urlArray.slice(concurrentGroupLength, concurrentGroupLength * 2);
const concurrentGroup3 = urlArray.slice(concurrentGroupLength * 2, concurrentGroupLength * 3);
const concurrentGroup4 = urlArray.slice(concurrentGroupLength * 3, concurrentGroupLength * 4);
const concurrentGroup5 = urlArray.slice(concurrentGroupLength * 4, concurrentGroupLength * 5);
const concurrentGroup6 = urlArray.slice(concurrentGroupLength * 5, concurrentGroupLength * 6);
const concurrentGroup7 = urlArray.slice(concurrentGroupLength * 6, concurrentGroupLength * 7);
const concurrentGroup8 = urlArray.slice(concurrentGroupLength * 7, concurrentGroupLength * 8 + 1);
Затем я запустил puppeteer-cluster, установил maxConcurrency 8 и поставил в очередь 8 кластеров для каждого массива URL *. 1010 *
const results = [];
(async () => {
const cluster = await Cluster.launch({
concurrency: Cluster.CONCURRENCY_CONTEXT,
maxConcurrency: 8,
timeout: 960000,
});
const getPageData = async ({page, data: urls}) => {
for (i = 0; i < urls.length; i++) {
await page.goto(urls[i], {waitUntil: 'networkidle2'});
let titleArray = await page.evaluate(() => {
let pageTitles = [];
for (j = 0; j < document.getElementsByClassName('canvas')[0].getElementsByClassName('genre scanme').length; j++) {
pageTitles.push(document.getElementsByClassName('canvas')[0].getElementsByClassName('genre scanme')[j].innerText);
}
return pageTitles;
});
results.push({titles: titleArray});
console.log(results)
}
};
cluster.queue(concurrentGroup1, getPageData);
cluster.queue(concurrentGroup2, getPageData);
cluster.queue(concurrentGroup3, getPageData);
cluster.queue(concurrentGroup4, getPageData);
cluster.queue(concurrentGroup5, getPageData);
cluster.queue(concurrentGroup6, getPageData);
cluster.queue(concurrentGroup7, getPageData);
cluster.queue(concurrentGroup8, getPageData);
cluster.on('taskerror', (err, data, willRetry) => {
if (willRetry) {
console.warn(`Encountered an error while crawling ${data}. ${err.message}\nThis job will be retried`);
} else {
console.error(`Failed to crawl ${data}: ${err.message}`);
}
});
await cluster.idle();
await cluster.close();
})();
Однако кукловод-кластер не может запустить для l oop одновременно в функции. For l oop дает сбой и превышает максимальное количество итераций, определенное параметром i < urls.length
, которое всегда равно 582
. Это возвращает undefined
для каждой итерации, превышающей 582
, что, как оказалось, было таким же количеством запущенных кластеров в очереди.
Это происходит потому, что он пытается посетить страницы с await page.goto(urls[i], {waitUntil: 'networkidle2'});
, которые, очевидно, не существуют, потому что urls[i]
во время этих превышенных итераций было равно undefined
.
См. Видео, которое я сделал ниже, иллюстрирующий эту проблему. Журналы, которые вы видите в видео, относятся к console.log(results)
, которое вы можете найти в скрипте.
https://www.youtube.com/watch?v=G0pHypc7SBs&feature=youtu.be
Спасибо.