gpt4 book ai didi

javascript - Puppeteer:将循环结构转换为 JSON 你是否传递了一个嵌套的 JSHandle?

转载 作者:行者123 更新时间:2023-12-04 11:45:38 26 4
gpt4 key购买 nike

我正在尝试抓取一个单页网站。有多种选择组合会导致不同的搜索重定向。我在 page.evaluate 中写了一个 for 循环的回调函数来点击不同的选择,并在每个按钮中进行点击搜索。但是,我收到错误:将循环结构转换为 JSON 您是否传递了嵌套的 JSHandle?

请帮忙!

我当前版本的代码如下所示:

const res = await page.evaluate(async (i, courseCountArr, page) => {
for (let j = 1; j < courseCountArr[i]; j++) {
await document.querySelectorAll('.btn-group > button, .bootstrap-select > button')['1'].click() // click on school drop down
await document.querySelectorAll('div.bs-container > div.dropdown-menu > ul > li > a')[`${j}`].click() // click on each school option
await document.querySelectorAll('.btn-group > button, .bootstrap-select > button')['2'].click() // click on subject drop down
const subjectLen = document.querySelectorAll('div.bs-container > div.dropdown-menu > ul > li > a').length // length of the subject drop down
for (let k = 1; k < subjectLen; k++) {
await document.querySelectorAll('div.bs-container > div.dropdown-menu > ul > li > a')[`${k}`].click() // click on each subject option
document.getElementById('buttonSearch').click() //click on search button
page.waitForSelector('.strong, .section-body')
return document.querySelectorAll('.strong, .section-body').length
}
}
}, i, courseCountArr, page);

最佳答案

为什么会发生错误
虽然您没有显示足够的代码来重现问题(courseCountArr 是 ElementHandles 的数组吗?将 page 传递给 evaluate 也不起作用,这是一个 Node 对象),这里有一个最小的重现,它显示了可能图案:

const puppeteer = require("puppeteer");

let browser;
(async () => {
const html = `<ul><li>a</li><li>b</li><li>c</li></ul>`;
browser = await puppeteer.launch();
const [page] = await browser.pages();
await page.setContent(html);

// ...
const nestedHandle = await page.$$("li"); // $$ selects all matches
await page.evaluate(els => {}, nestedHandle); // throws
// ...

})()
.catch(err => console.error(err))
.finally(() => browser?.close())
;
输出是
TypeError: Converting circular structure to JSON
--> starting at object with constructor 'BrowserContext'
| property '_browser' -> object with constructor 'Browser'
--- property '_defaultContext' closes the circle Are you passing a nested JSHandle?
at JSON.stringify (<anonymous>)
为什么会这样?回调中的所有代码 page.evaluate (和系列: evaluateHandle $eval $$eval )由 Puppeteer 以编程方式在浏览器控制台内执行。浏览器控制台是一个不同于 Node 的环境,Puppeteer 和 ElementHandles 就在其中。为了弥补进程间的差距,回调到 evaluate ,参数和返回值被序列化和反序列化。
这样做的结果是您无法访问任何节点状态,就像您尝试使用 page.waitForSelector('.strong, .section-body') 一样。浏览器内部。 page与浏览器处于完全不同的过程中。 (顺便说一句, document.querySelectorAll 是纯同步的,所以 await 没有意义。)
puppeteer 师 ElementHandles是用于 Hook 页面的 DOM 的复杂结构,无法像您尝试那样序列化并传递给页面。 Puppeteer 必须在后台执行翻译。任何传递给 evaluate 的元素句柄(或调用 .evaluate())在浏览器中的 DOM 节点上显示它们,并且该 DOM 节点就是您的 evaluate。的回调被调用。截至撰写本文时,Puppeteer 无法使用嵌套的 ElementHandles 执行此操作。
可能的修复
在上面的代码中,如果更改 .$$ .$ ,您将只检索第一个 <li> .这个单一的、非嵌套的 ElementHandle 可以转换为一个元素:
// ...
const handle = await page.$("li");
const val = await page.evaluate(el => el.innerText, handle);
console.log(val); // => a
// ...
或者:
const handle = await page.$("li");
const val = await handle.evaluate(el => el.innerText);
console.log(val); // => a
在您的示例中进行这项工作是交换循环和 evaluate 的问题。调用电话以便您访问 courseCountArr[i]在 Puppeteer 领域,将嵌套的 ElementHandles 解压缩为单独的参数到 evaluate ,或者将大部分控制台浏览器调用以单击返回 Puppeteer(取决于您的用例和代码目标)。
您可以申请 evaluate调用每个 ElementHandle:
const nestedHandles = await page.$$("li");

for (const handle of nestedHandles) {
const val = await handle.evaluate(el => el.innerText);
console.log(val); // a b c
}
要获得一系列结果,您可以执行以下操作:
const nestedHandles = await page.$$("li");
const vals = await Promise.all(
nestedHandles.map(el => el.evaluate(el => el.innerText))
);
console.log(vals); // [ 'a', 'b', 'c' ]
您还可以将 ElementHandles 解压缩为 evaluate 的参数。并使用 (...els)回调中的参数列表:
const nestedHandles = await page.$$("li");
const vals = await page.evaluate((...els) =>
els.map(e => e.innerText),
...nestedHandles
);
console.log(vals); // => [ 'a', 'b', 'c' ]
如果除了句柄之外您还有其他参数,您可以执行以下操作:
const nestedHandle = await page.$$("li");
const vals = await page.evaluate((foo, bar, ...els) =>
els.map(e => e.innerText + foo + bar)
, 1, 2, ...nestedHandle);
console.log(vals); // => [ 'a12', 'b12', 'c12' ]
或者:
const nestedHandle = await page.$$("li");
const vals = await page.evaluate(({foo, bar}, ...els) =>
els.map(e => e.innerText + foo + bar)
, {foo: 1, bar: 2}, ...nestedHandle);
console.log(vals); // => [ 'a12', 'b12', 'c12' ]
另一种选择可能是使用 $$eval ,它选择多个句柄,然后在浏览器上下文中运行回调,并将所选元素的数组作为其参数:
const vals = await page.$$eval("li", els => 
els.map(e => e.innerText)
);
console.log(vals); // => [ 'a', 'b', 'c' ]
如果您不使用 Node.js 中的句柄做任何其他事情,这可能是最干净的。
同样,您可以完全绕过 Puppeteer 并在浏览器上下文中进行整个选择和操作:
const vals = await page.evaluate(() =>
[...document.querySelectorAll("li")].map(e => e.innerText)
);
console.log(vals); // => [ 'a', 'b', 'c' ]
(请注意,获取内部文本只是您可能拥有的任意复杂浏览器代码的占位符)

关于javascript - Puppeteer:将循环结构转换为 JSON 你是否传递了一个嵌套的 JSHandle?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58870660/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com