Node.js/Puppeteer - DOM NodeList 转 JS 对象

Node.js/Puppeteer - DOM NodeList to JS Object

我想将 NodeList 转换为对象。

H1 为 object.name 依此类推。

我仍然无法理解 page.evaluate() 的确切行为。

这是我需要的:

这是我的尝试之一,但 gp 始终未定义:

await page.waitForNavigation();

const selG = 'body > div.content-home > div > div.box > div > div:nth- child(2) > div.col-md-12.no-padding > div:nth-child(4) > div:nth-child(2) > div.col-xs-12';
await page.waitForSelector(selG);
const g = await page.evaluate( (selG) => {
    let gp = document.querySelector(selG); //null
    let n = Array.from(gp.querySelectorAll('h1'), element => element.textContent);
    console.log(n[0]);
    return n;
});

您必须使用以下方法将变量 selG 传递给 page.evaluate()

const g = await page.evaluate(selG => { /* ... */ }, selG);

Note: Notice the that I added selG as a separate argument after the page function.

page.evaluate(pageFunction, ...args)

这应该可以防止 document.querySelector(selG) 返回 null

page.evaluate() 运行您将其直接传递给浏览器的函数并且它没有范围(访问变量)启动 Puppetter 的 NodeJS 脚本。

要完全理解,试试这个:

1 - 按原样复制您的函数

2 - 把它包装成一个自调用函数([your-function])(),结果如下(我又加了一行console.log(selG);

((selG) => {
  console.log(selG); // I added this line
  let gp = document.querySelector(selG);
  let n = Array.from(gp.querySelectorAll('h1'), element => element.textContent);
  console.log(n[0]);
  return n;
})()

3 - 直接粘贴到 devtools 控制台

这样做是在多做少(从理解的角度)page.evaluate()做的是运行你正在做的功能直接传递给浏览器。 结果如何?它是 Cannot read property 'querySelectorAll' of null 因为,正如您所指出的,gp 为空。

但请注意我添加的 console.log(selG);...它记录 undefined...这是个大问题!

为什么会这样?

看看函数本身,selG 变量不存在,所以 let gp = document.querySelector(selG); 不能 return 任何东西。 selG 被定义到您用来启动 Puppeteer 的脚本中,但是您传递给 page.evaluate() 的函数在浏览器中将是 运行,而不是在 Node 执行上下文中。

直接引用 Puppeteer 文档

page.evaluate(pageFunction, ...args)

pageFunction Function to be evaluated in the page context

...args <...Serializable|JSHandle> Arguments to pass to pageFunction

使用(如 Grant 所说)第二个剩余 argsselG 变量传递给您的函数。

按照您的原始代码稍作更改

await page.waitForNavigation();

const selG = 'body > div.content-home > div > div.box > div > div:nth- child(2) > div.col-md-12.no-padding > div:nth-child(4) > div:nth-child(2) > div.col-xs-12';
await page.waitForSelector(selG);
const g = await page.evaluate( (SELECTOR) => {
    let gp = document.querySelector(SELECTOR);
    let n = Array.from(gp.querySelectorAll('h1'), element => element.textContent);
    console.log(n[0]);
    return n;
}, selG);

请注意:

  • 我将 selG 变量(最后一行)传递给 pageFunction(您的函数)

  • pageFunction接收一个变量并将其存储到SELECTOR变量中

  • pageFunction 比消耗 SELECTOR 收到

总结:传递给 page.evaluate() 的函数不能使用在其外部声明的变量,因为它会 运行 进入浏览器,a上下文与您的 NodeJS 脚本分离(编写以启动 Puppeteer 本身)。

试试我的代码,它应该可以正常工作而无需任何更改。 让我知道是否足够清楚。

奖金

请记住,如果您想使用一些与 DOM 相关的数据,您至少可以使用三种不同的方法来执行相同的操作。

下面是我的示例,我想读取在页面中找到的第一个 link 的 href 属性。第一个示例像您一样使用 page.evaluate(),后两个示例向您展示了使用其他一些 Puppeteer API 的不同方法。

const SELECTOR = '[href]:not([href=""])';
let link;

// compare the three following examples, they all do the same
link = await page.evaluate((sel) => 
    document.querySelector(sel).getAttribute('href')
, SELECTOR);
link = await page.$eval(SELECTOR, el => el.getAttribute('href'));
link = await page.$(SELECTOR).getProperty('href').jsonValue();