情况:
这就是我想要做的:
1)我加载第0页。第0页包含指向不同页面的可点击链接。我想加载所有这些页面的内容。所以:
2)单击第一个链接。加载页面1.获取数据。返回上一页(第0页)
3)单击第二个链接,该链接会加载页面2。等等,直到所有链接都被单击为止。
使用我当前的代码加载第0页,然后单击第一个链接并加载第1页,然后发生崩溃,并显示以下错误:
(node:2629) UnhandledPromiseRejectionWarning: Error: Protocol error (Runtime.callFunctionOn): Execution context was destroyed.
问题:
我在做错什么,如何使脚本按照预期的方式运行?
代码:
const puppeteer = require('puppeteer');
const fs = require('fs');
let getData = async () => {
const browser = await puppeteer.launch({headless: false});
const page = await browser.newPage();
await page.goto('url', { waitUntil: 'networkidle2' });
await page.setViewport({width: ..., height:...});
const result = await page.evaluate(async () => {
let data = [];
let elements = document.querySelector('.items').querySelectorAll('.item');
for (const element of elements) {
element.click();
await new Promise((resolve) => setTimeout(resolve, 2000));
// GETTING THE DATA THEN PUSHING IT INTO THE DATA ARRAY
await page.goBack();
}
return data; // Return our data array
});
browser.close();
return result; // Return the data
};
答案 0 :(得分:4)
好的,这是我的看法。首先,您错误地使用了evaluate
方法。主要是因为您实际上并不需要它,还因为您要它执行它无法执行的操作。仅说明一下:evaluate
方法仅在您的网页上下文中运行。它几乎只允许您在远程浏览器的当前页面上直接执行Javascript指令。它没有在该函数外部声明的变量的概念-因此,在这种情况下,当您执行此操作时:
await page.goBack();
evaluate
方法不知道page
是什么,也不知道如何使用它。现在,有种方法可以将page
注入到evaluate
方法中,但这也不能解决您的问题。 Puppeteer API调用根本无法在evaluate
方法内运行(我自己尝试过此方法,它总是返回异常)。
因此,现在让我们回到您遇到的问题上-在evaluate
函数中正在执行的操作是检索一个类为.items
的UI元素,然后在该UI中搜索每个UI元素类为.item
的元素。然后,您将遍历所有找到的UI元素,单击每个UI元素,获取某种数据,然后返回以单击下一个。
您无需使用evaluate
方法就可以实现所有这些目的,而无需使用Puppeteer API调用,如下所示:
const itemsList = await page.$('.items'); // Using '.$' is the puppeteer equivalent of 'querySelector'
const elements = await itemsList.$$('.item'); // Using '.$$' is the puppeteer equivalent of 'querySelectorAll'
const data = [];
elements.forEach(async (element) => {
await element.click();
// Get the data you want here and push it into the data array
await page.goBack();
});
希望这对您有所帮助!
答案 1 :(得分:2)
与其来回导航以单击第一页上的下一个链接,不如将第一页中的链接存储到数组中,然后使用{{3一次将其打开。 }}。
换句话说,您可以使用以下示例完成此任务:
await page.goto('https://example.com/page-1');
const urls = await page.evaluate(() => Array.from(document.querySelectorAll('.link'), element => element.href));
for (let i = 0, total_urls = urls.length; i < total_urls; i++) {
await page.goto(urls[i]);
// Get the data ...
}
答案 2 :(得分:1)
您也应该可以在 eval 函数中使用 window.history.go(-1) 来完成。 MDN for history.go
const puppeteer = require('puppeteer');
const fs = require('fs');
let getData = async () => {
const browser = await puppeteer.launch({headless: false});
const page = await browser.newPage();
await page.goto('url', { waitUntil: 'networkidle2' });
await page.setViewport({width: ..., height:...});
const result = await page.evaluate(async () => {
let data = [];
let elements = document.querySelector('.items').querySelectorAll('.item');
for (const element of elements) {
element.click();
await new Promise((resolve) => setTimeout(resolve, 2000));
// GETTING THE DATA THEN PUSHING IT INTO THE DATA ARRAY
await window.history.go(-1);
}
return data; // Return our data array
});
browser.close();
return result; // Return the data
};
答案 3 :(得分:0)
@ AJC24对我不起作用。问题是单击并返回到原始页面时页面上下文已被破坏。
我最终要做的事情与格兰特建议的类似。我将所有按钮标识符收集到一个数组中,回到原始页面后,我会再次单击。
答案 4 :(得分:0)
使用@Grant的迭代
执行上下文已被破坏,很可能是由于导航所致。
然后,我在迭代中打开一个新选项卡,它解决了问题!
for (let i = 0, total_urls = urls.length; i < total_urls; i++) {
const page = await browser.newPage();
await page.goto(url), { waitUntil: 'networkidle0', timeout: 0 };
await page.goto(urls[i]);
// Get the data ...
}