Задача
Ошибка означает, что вы обращаетесь к данным, которые устарели / недействительны из-за навигации. В вашем скрипте ошибка ссылается на переменную listeCompanies
:
const listeCompanies = await page.$$('.list-firms > div.firm');
Сначала вы используете эту переменную в цикле, затем перемещаетесь по page.goto
, и после этого ваш цикл пытается извлечь следующий элемент из переменной listeCompanies
. Но после того, как произошла навигация, дескрипторы элемента в этой переменной больше не присутствуют, и поэтому выдается ошибка. Вот почему первая итерация работает.
Решение
Есть несколько способов исправить это.
- Извлечение данных со страницы сразу (перед использованием цикла)
- Используйте вторую страницу, чтобы выполнить "циклическую навигацию", чтобы вашей главной странице не нужно было перемещаться
- «Обновить» вашу переменную путем повторного выполнения селектора после вызова
page.goBack
Вариант 1. Извлечение данных перед входом в цикл
Это самый чистый способ сделать это. Вы извлекаете информацию на первой странице сразу, а затем перебираете извлеченные данные. nameLinkList
будет массивом со значениями name
и link
(например, [{name: '..', link: '..'}, {name: '..', link: '..'}]
). Также нет необходимости вызывать page.goBack
в конце цикла, поскольку данные уже извлечены.
const nameLinkList = await page.$$eval(
'.list-firms > div.firm',
(firms => firms.map(firm => {
const a = firm.querySelector('.listing-body > h3 > a');
return {
name: a.innerText,
link: a.href
};
}))
);
for (const {name, link} of arr) {
await Promise.all([
page.waitForNavigation(),
page.goto(link),
page.waitForSelector('.firm-panel'),
]);
const info = await page.$eval('#info', e => e.innerText);
const data = [{
name: name,
information: info,
}];
}
Вариант 2: использовать вторую страницу
В этом случае в вашем браузере будет две открытые страницы. Первый будет использоваться только для чтения данных, второй - для навигации.
const page2 = await browser.newPage();
for (const companie of listeCompanies ){
const name = await companie.$eval('.listing-body > h3 > a', name => name.innerText);
const link = await companie.$eval('.listing-body > h3 > a', link => link.href);
await Promise.all([
page2.goto(link),
page2.waitForSelector('.firm-panel'),
]);
const info = await page2.$eval('#info', e => e.innerText);
// ...
}
Вариант 3: «Обновить» селекторы
Здесь вы просто повторно запускаете свой селектор после возврата на свою "главную страницу". Обратите внимание, что for..of
должен быть заменен на цикл итератора при замене массива.
let listeCompanies = await page.$$('.list-firms > div.firm');
for (let i = 0; i < listeCompanies.length; i++){
// ...
await page.goBack();
listeCompanies = await page.$$('.list-firms > div.firm');
}
Я рекомендую перейти к варианту 1, поскольку это также уменьшит количество необходимых запросов навигации и, следовательно, ускорит ваш сценарий.