Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Puppeteer - Page.evaluate using moment

I am trying to use "puppeteer": "^1.16.0", and "moment": "^2.24.0",. When running page.evaluate() to convert a string to a date obj via moment I get:

Error: Evaluation failed: ReferenceError: moment is not defined

Find below my minimum example:

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()
})

As you can see I tried to pass the moment instance to the evaluate function, however I still get the error.

Any suggestions what I am doing wrong?

I appreciate your replies!

like image 727
Carol.Kar Avatar asked Mar 03 '23 22:03

Carol.Kar


1 Answers

You can only pass serializable data to the page.evaluate function as argument. (see the docs for more information). As moment is a function and a function cannot be serialized you cannot use it that easy.

To expose a function to the page from your Node.js environment, you can use the page.exposeFunction. Quote from the docs:

The method adds a function called name on the page's window object. When called, the function executes puppeteerFunction in node.js and returns a Promise which resolves to the return value of puppeteerFunction.

Code sample:

The following code inside your Node.js environment sets up a function formatDate which returns the formatted date:

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

Be aware that you only need to call exposeFunction once on the page as it survives navigation. That means you can put this code outside of the loop.

Then your puppeteer code could use the function like this:

const data = await page.evaluate(async () => {
  const priceData = []
  let date = "9/23/2016"
  priceData.push({
    price_date: await window.formatDate(date)
  })
  return priceData
})
like image 111
Thomas Dondorf Avatar answered Mar 15 '23 11:03

Thomas Dondorf