首页 > 解决方案 > 使用不可序列化参数的 Puppeteer exposeFunction

问题描述

我正在尝试使用执行一些复杂的 DOM 解析的第 3 方库:

/** 
 *  // Simplified for this example
 *  module.exports.parse = (document) => {return document.title; }
 */

const { parse } = require('./parse.js');

当我尝试公开和评估 puppeteer 中的功能时:

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://stackoverflow.com', { waitUntil: ['domcontentloaded'] });
await page.exposeFunction('parse', (document) => {
    return parse(document);
});

await page.evaluate(() => {
    return window.parse(window.document);
});

我收到一个错误:

评估失败:TypeError: Converting circular structure to JSON\n at JSON.stringify ()\n at window.(anonymous function) ( puppeteer_evaluation_script :13:22)\n at puppeteer_evaluation_script :3:31

在文档的示例中,它传递了一个字符串(可以序列化)。是否有任何已知的方法来评估采用windowdocument作为其参数的 node.js 方法?

标签: javascriptnode.jspuppeteer

解决方案


下面解释的这种方法有点不同,因此请谨慎使用。

我们可以将脚本带到浏览器,而不是将 dom 带到节点上下文。借助 webpack 或 browserify 的强大功能是可能的。同样这样,我们不需要序列化任何循环变量。

这是最小的 webpack 配置。

const path = require("path");

module.exports = {
  entry: "./browser/src.js",

  output: {
    path: path.resolve("browser"),
    filename: "dist.js",
    libraryTarget: "global"
  },

  module: {
    rules: [
      {
        test: /\.js$/,
        use: "babel-loader"
      }
    ]
  }
};

我们将浏览器脚本放在browser/src.js文件夹中。然后当我们运行时webpack,它会生成browser/dist.js我们可以在浏览器中注入的内容。

最后我可以用以下方法之一调用它,

await page.addScriptTag({path: "./browser/dist.js"});
await page.evaluate(fs.readFileSync("./browser/dist.js", 'utf8'));

只要我们不使用任何无法捆绑的本机二进制文件,它就可以完美运行。

为简单起见,这里是其他文件,

// browser/src.js
module.exports.parse = window => {
  return window.location.href;
};

// index.js
const fs = require("fs");
const puppeteer = require("puppeteer");
(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.evaluate(fs.readFileSync("./browser/dist.js", 'utf8'));
  const data = await page.evaluate(() => {
    return parse(window);
  });

  console.log({ data });
})();

// The result:
{ data: 'about:blank' }

推荐阅读