问题
该错误表示您正在访问由于导航而变得过时/无效的数据。错误在您的脚本中引用了变量
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,因为这也减少了必要的导航请求,因此可以加快脚本的速度。



