【问题标题】:Page.evaluate() won't execute in a Promise chainPage.evaluate() 不会在 Promise 链中执行
【发布时间】:2019-01-04 22:05:04
【问题描述】:

刚开始使用 Puppeteer。尝试解析页面,但评估方法无法以某种方式工作。

var Browser
var Page
var Result
puppeteer.launch()
  .then(function (browser) {
    console.log('Browser Created\nCreating Blank Page')
    Browser = browser
    return Browser.newPage()
  })
  .then(function (page) {
    console.log('Page Created\nVisiting URL')
    Page = page
    return Page.goto(URL)
  })
  .then(function (resp) {
    console.log('Website Loaded')
    return Page.evaluate(function () {
      // Completely Sync Stuff
      console.log('Evaluating Selectors')
      var myElems = document.getElementsByClassName('challenge-type light')
      Result = myElems
    })
  })
  .then(function (val) {
    console.log(Result)
    console.log('Done! Exiting')
    Browser.close()
    process.exit()
  })
  .catch(function (err) {
    Browser.close()
    console.log(err)
    process.exit(1)
  })

输出:

Browser Created
Creating Blank Page
Page Created
Visiting URL
Website Loaded
undefined
Done! Exiting

可能是什么错误?更喜欢没有 async/await 的解决方案。

编辑:“评估选择器”也没有记录到控制台,所以代码永远不会到达那里,这是我的担心。

【问题讨论】:

  • 你确定 document.getElementsByClassName('challenge-type light') 真的返回结果吗?
  • 不要使用那些全局的BrowserPageResult变量。有many much better ways to access previous promise results in a .then() chain
  • 我上次检查时,page.evaluate 不支持闭包。尽量不要给Result赋值,而是给return赋值。
  • ... 返回一个 serializable 值。如果传递给page.evaluate() 的函数返回一个不可序列化的值,则page.evaluate() 解析为未定义。 Ref

标签: node.js promise puppeteer


【解决方案1】:

我会仔细检查

document.getElementsByClassName('challenge-type light') 

返回一个结果。

我相信您使用的是无头浏览器,因此有时元素可能无法按预期加载。

【讨论】:

  • 我明白,但如果您注意到,“评估选择器”也不会记录到控制台。它从不运行该代码。
【解决方案2】:

终于搞定了。

  1. evaluate 中的控制台将在页面上下文中,因此这是 chromium 页面的控制台。
  2. 我们需要从评估函数返回一些东西。 DOM 元素不会按原样返回,因为它们在评估之外丢失了上下文。

这行得通:

.then(function (resp) {
    console.log('Website Loaded')
    return Page.evaluate(function () {
      return document.querySelector('.cover-heading').innerText
    })
  })

【讨论】:

    【解决方案3】:

    好的,你在正确的道路上,但你有一些问题。

    根据您自己的回答:您注意到控制台日志在以evaluate 方法执行时在页面上下文中执行。你说得对,但说你不能从 evaluate 方法返回 DOM 元素是不正确的。你可以只是你的代码不太正确。

    所以你拥有的是这样的:

    .then(function (resp) {
      console.log('Website Loaded')
      return Page.evaluate(function () {
        // Completely Sync Stuff
        console.log('Evaluating Selectors')
        var myElems = document.getElementsByClassName('challenge-type light')
        Result = myElems
      })
    })
    .then(function (val) {
      console.log(Result)
      console.log('Done! Exiting')
    });
    

    这不起作用,因为您试图将 myElems 分配给 evaluate 方法内的 Result 变量。 evaluate 方法在浏览器中执行。 不知道 Result 变量存在于您的 puppeteer 脚本中。这就是为什么你的变量最后输出为undefined

    解决方法如下:

    .then(function () {
      return Page.evaluate(function () {
        // Return the array of elements from inside the evaluate method
        return document.getElementsByClassName('challenge-type light')
      });
    })
    .then(function (elements) {
      console.log(elements) // Will be your array of elements
    });
    

    希望这会有所帮助!

    【讨论】:

    • 我这样做并得到了一个已知选择器的空白对象。在写答案之前,我完全尝试了您所写的内容,当时我没有返回内部文本并依靠下一个承诺来做到这一点,但它所能得到的只是一个空白对象。选择器查询返回的 HTMLDivElement 类型在评估之外的其他地方没有任何意义,因此我得出了这个结论。如果我错了,请随时纠正我。
    • 好的,如果你得到一个空白对象,那么我建议你在getElementsByClassName 中使用的类名不要对应于 UI 中的任何元素。另一个令人困惑的事情是,您最初的问题是尝试返回具有您指定的类名的所有元素的列表,但您发布的答案在原始帖子中使用了类名 not 并且它还返回了innerText 这又是你介绍的新东西。
    猜你喜欢
    • 2020-05-23
    • 2012-09-15
    • 2016-06-30
    • 1970-01-01
    • 2016-07-15
    • 2013-09-26
    • 1970-01-01
    • 1970-01-01
    • 2018-11-08
    相关资源
    最近更新 更多