【问题标题】:Puppeteer - Page.evaluate using momentPuppeteer - Page.evaluate 使用时刻
【发布时间】:2019-06-02 17:06:12
【问题描述】:

我正在尝试使用"puppeteer": "^1.16.0","moment": "^2.24.0",。当运行 page.evaluate() 将字符串转换为日期 obj 时,我得到:

错误:评估失败:ReferenceError:未定义时刻

在下面找到我的最小示例:

const puppeteer = require("puppeteer-extra")
const moment = require('moment')

function shuffle(dataObjArr) {
    let res = dataObjArr.sort(() => Math.random() - 0.5);
    return res
}

let issuerUrls = JSON.parse('[{"id":62,"name":"Product 1","ecomUrl":"/product/252","createdAt":"2019-05-25T07:51:49.000Z","updatedAt":"2019-05-25T07:51:49.000Z"},  {"id":15,"name":"Product 2","ecomUrl":"/product/251","createdAt":"2019-05-25T07:51:49.000Z","updatedAt":"2019-05-25T07:51:49.000Z"}]')

let issuerUrlsShuffled = shuffle(issuerUrls)
let BASE_URL = "https://www.webscraper.io/test-sites/e-commerce/allinone"
// puppeteer usage as normal
puppeteer.launch({
    headless: false,
    args: ["--disable-notifications"]
}).then(async browser => {
    const page = await browser.newPage()
    await page.setViewport({
        width: 800,
        height: 600
    })

    for (let i = 0; i < issuerUrlsShuffled.length; i++) {
        try {

            let URL = BASE_URL + issuerUrlsShuffled[i].ecomUrl;

            await page.goto(URL)

            page.waitForNavigation({
                timeout: 60,
                waitUntil: 'domcontentloaded'
            });

            const data = await page.evaluate(() => {

                const priceData = []

                let date = "9/23/2016" // this is needed for testing purposes only!!!

                priceData.push({
                    price_date: moment(date, 'M/DD/YYYY').toDate()
                })
                return priceData
            }, moment)

            // show data
            console.log(JSON.stringify(data, null, 2))

            await page.waitFor(3000)
        } catch (error) {
            console.log(error)
        }
    }
    await browser.close()
})

如您所见,我尝试将 moment 实例传递给 evaluate 函数,但仍然出现错误。

任何建议我做错了什么?

感谢您的回复!

【问题讨论】:

    标签: javascript momentjs puppeteer


    【解决方案1】:

    您只能将可序列化的数据作为参数传递给page.evaluate 函数。 (有关更多信息,请参阅docs)。由于moment 是一个函数,一个函数不能被序列化,所以你不能那么容易地使用它。

    要从 Node.js 环境向页面公开函数,您可以使用 page.exposeFunction。引用文档:

    该方法在页面的window 对象上添加了一个名为name 的函数。调用时,该函数在 node.js 中执行 puppeteerFunction 并返回一个 Promise,该 Promise 解析为 puppeteerFunction 的返回值。

    代码示例:

    您的 Node.js 环境中的以下代码设置了一个函数 formatDate,它返回格式化的日期:

    await page.exposeFunction('formatDate', (date) =>
      moment(date, 'M/DD/YYYY').toDate()
    );
    

    请注意,您只需在页面上调用一次exposeFunction,因为它在导航后仍然存在。这意味着您可以将此代码放在循环之外。

    那么你的 puppeteer 代码可以使用如下函数:

    const data = await page.evaluate(async () => {
      const priceData = []
      let date = "9/23/2016"
      priceData.push({
        price_date: await window.formatDate(date)
      })
      return priceData
    })
    

    【讨论】:

    • @Vaviloff 感谢您的提示。 :) await 不见了……我自己试了一下,现在可以了。
    • 感谢代码有效。但是,当脚本结束时,我收到以下错误:Error: Failed to add page binding with name formatDate: window['formatDate'] already exists! 有什么建议吗?
    • @Anna.Klee 我猜你把函数放在循环中?您应该只将函数绑定到页面一次,而不是在每次页面导航之后。因此,您可以将其放在循环之外,并且错误应该消失了。
    • :D 很高兴看到 Thomas 和 Vaviloff 以及大家互相帮助。我认为我们应该注意答案,应该在任何类型的循环或导航之前添加暴露函数,因为“通过 page.exposeFunction 安装的函数在导航中存在。”
    • @Md.AbuTaher puppeteer-stackoverflow 小队聚集在一起:D 为答案添加了提示。
    猜你喜欢
    • 1970-01-01
    • 2019-10-20
    • 1970-01-01
    • 2018-02-22
    • 2020-05-09
    • 1970-01-01
    • 2019-09-03
    • 2020-07-20
    • 1970-01-01
    相关资源
    最近更新 更多