【问题标题】:Puppeteer: How to get the contents of each element of a nodelist?Puppeteer:如何获取节点列表的每个元素的内容?
【发布时间】:2019-03-20 11:52:05
【问题描述】:

我正在尝试实现一些非常微不足道的事情:获取元素列表,然后对每个元素的 innerText 执行一些操作。

const tweets = await page.$$('.tweet');

据我所知,这会返回一个节点列表,就像浏览器中的 document.querySelectorAll() 方法一样。

我如何遍历它并获得我需要的东西?我尝试了各种东西,例如:

[...tweets].forEach(tweet => {
  console.log(tweet.innerText)
});

【问题讨论】:

  • 在尝试forEach之前,您是否尝试过for循环,例如for(var i = 0; i< tweet.length; i++){ console.log(tweet[i].innerText)}

标签: javascript node.js google-chrome-devtools puppeteer headless-browser


【解决方案1】:

page.$$():

您可以使用elementHandle.getProperty() 和jsHandle.jsonValue() 的组合从ElementHandle 中获取innerText,使用page.$$():

const tweets = await page.$$('.tweet');

for (let i = 0; i < tweets.length; i++) {
  const tweet = await (await tweets[i].getProperty('innerText')).jsonValue();
  console.log(tweet);
}

如果您设置使用forEach() 方法,您可以将循环包装在一个promise 中:

const tweets = await page.$$('.tweet');

await new Promise((resolve, reject) => {
  tweets.forEach(async (tweet, i) => {
    tweet = await (await tweet.getProperty('innerText')).jsonValue();
    console.log(tweet);
    if (i === tweets.length - 1) {
      resolve();
    }
  });
});

page.evaluate():

或者,您可以完全跳过使用page.$$(),而使用page.evaluate():

const tweets = await page.evaluate(() => Array.from(document.getElementsByClassName('tweet'), e => e.innerText));

tweets.forEach(tweet => {
  console.log(tweet);
});

【讨论】:

  • 我真的很喜欢最后一个例子。我什至不知道 Array.from() 方法。
  • 是否有可能摆脱'for'循环并使用一些数组转换来为page$$的样本构建innerText值数组?
  • +1 多年来一直使用数组.. 从来不知道 Array.from!比写 Array.prototype.slice.call 好多了!
  • page.$$ 版本看起来很糟糕,我只是不明白它的用例吗?
【解决方案2】:

根据 puppeteer 文档 here,$$ 不返回节点列表,而是返回 ElementHandle 的 Promise of Array。它与 NodeList 完全不同。

有几种方法可以解决这个问题。

1。使用名为 page.$$eval 的循环的内置函数

此方法在页面内运行Array.from(document.querySelectorAll(selector)),并将其作为第一个参数传递给pageFunction。

所以要获取 innerText 如下所示,

// Find all .tweet, and return innerText for each element, in a array.
const tweets = await page.$$eval('.tweet', element => element.innerText);

2。将elementHandle 传递给page.evaluate

无论你从await page.$$('.tweet') 得到什么,都是一个 elementHandle 数组。如果您控制台,它会根据类型显示JShandle 或ElementHandle。

忘记硬解释,演示更容易。

// let's just call them tweetHandle 
const tweetHandles = await page.$$('.tweet');

// loop thru all handles
for(const tweethandle of tweetHandles){

   // pass the single handle below
   const singleTweet = await page.evaluate(el => el.innerText, tweethandle)

   // do whatever you want with the data
   console.log(singleTweet) 
}

当然有多种方法可以解决这个问题,Grant Miller 在另一个答案中也回答了其中的几个。

【讨论】:

  • 是的,我或多或少地做了你在第二个例子中展示的内容。男孩 Puppeteer 看起来是否过于复杂......我真的想知道它的机制与我们在浏览器中习惯的机制有何不同。我的意思是,它不只是一个“虚拟浏览器”吗?
  • 它是一个浏览器,但问题是,告诉人类使用浏览器与告诉计算机有很大不同。 :D 我以为我为您的问题提供了最简单的示例。叹息。
  • 对于.$$eval,第二个参数回调不是应用于每个元素,而是应用于所有元素。所以page.$$eval('.tweet', elements =&gt; elements.map(e =&gt; e.innerText))
猜你喜欢
  • 2015-12-12
  • 1970-01-01
  • 1970-01-01
  • 2016-01-24
  • 2018-09-29
  • 2020-11-03
  • 1970-01-01
  • 1970-01-01
  • 2019-07-01
相关资源
最近更新 更多