【问题标题】:how can I inject nodejs native module to pupppeteer page如何将 node js 本机模块注入 puppeteer 页面
【发布时间】:2019-11-08 11:08:38
【问题描述】:

我有一个使用 NWjs 的应用程序,在我的应用程序页面中,它使用了很多很多 nodejs 原生模块(如 fs/http/etc)

然后我想用 puppeteer 来测试我的页面,所以我们需要注入 nodejs 原生模块来运行页面

我尝试在代码下方运行,但它无法将 nodejs 本机模块注入页面

const fs = require("fs");
const puppeteer = require('puppeteer');

puppeteer.launch().then(async browser => {
    const page = await browser.newPage();
    page.on('console', msg => console.log(msg.text()));
    await page.exposeFunction("require", function (name) {
        console.log("require module name:"+name);
        return require(name) // or return fs , result is same
    })
    await page.evaluate(async () => {

        const fs = await window.require("fs");
        console.log(fs,typeof fs.readFile);//fs.readFile is undefined

    });
    await browser.close();
});

【问题讨论】:

  • 您是否看到至少在控制台中打印了 fs 对象?
  • 是的,它返回一个 JSHandle@object 。如果我返回一个普通对象,它会起作用。
  • 我在您的代码示例中没有看到page.goto,您打开的是什么网址?

标签: javascript node.js google-chrome puppeteer


【解决方案1】:

您的代码没问题。问题是 puppeteer 只能与页面上下文交换可序列化的数据。也就是说,可以通过 JSON.stringify 传输的对象。

函数和其他复杂的 Web Api 不可转让。这就是您在页面上下文中看到 JSHandle@object 的原因。它是一个包含来自fs 模块的所有module.exports 可序列化值的对象。

你可以做一个简单的测试来看看。在与您的代码相同的文件夹中设置另一个包含简单模块的文件,并尝试在您的代码中使用它。示例:

// file mod1.js
module.exports = {
  number: 1,
  string: 'test',
  object: { complex: 'object' },
  function: function() {}, // this will not be transfered
  fs: require('fs')
};

现在你运行你的代码调用这个模块:

const puppeteer = require('puppeteer');

puppeteer.launch().then(async browser => {
  const page = await browser.newPage();
  page.on('console', msg => console.log(msg.text()));
  await page.exposeFunction("require", function (name) {
    console.log("required module name: " + name);
    return require(name); // or return fs , result is same
  });

  await page.evaluate(async () => {
    const module = await window.require("./mod1");
    // changed to JSON.stringify for you to see module's content
    console.log('module:', JSON.stringify(module, null, 2));
  });
  await browser.close();
});

不幸的是,关于 page.exposeFunction 方法的文档在这一点上并不清楚。

编辑:我想出了一个需要本机模块的可能解决方案。我只测试了 fs.unlikSync、fs.writeFileSync 和 fs.readFileSync 方法,但它们有效。 :) 这是代码:

const puppeteer = require('puppeteer');

// expose every methods of the moduleName to the Page, under window[moduleName] object.
async function exposeModuleMethods(page, moduleName) {
  const module = require(moduleName);
  const methodsNames = Object.getOwnPropertyNames(module);
  for (const methodName of methodsNames) {
    await page.exposeFunction('__' + moduleName + '_' + methodName, module[methodName]);
    await page.evaluate((moduleName, methodName) => {
      window[moduleName] = window[moduleName] || {};
      window[moduleName][methodName] = window['__' + moduleName + '_' + methodName]; // alias
    }, moduleName, methodName);
  }
}

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  page.on('console', msg => console.log(msg.text()));

  // expose `require` on Page. When it is used on Page, the function `exposeModuleMethods`
  // expose the individual module's methods on window[moduleName].
  await page.exposeFunction('require', async function (moduleName) {
    await exposeModuleMethods(page, moduleName);
  });

  // make the Page require "fs" native module
  await page.evaluate(async (moduleName) => await require(moduleName), 'fs');

  // make the Page test the "fs" module
  await page.evaluate(async (moduleName) => {
    // save a file on the current directory named "mod2.js"
    await window[moduleName]['writeFileSync']('./mod2.js', 'any text');
    // read the file "mod2.js"
    const mod2 = await window[moduleName]['readFileSync']('./mod2.js', { encoding: 'utf8' });
    // log it's content
    console.log(JSON.stringify(mod2, null, 2));
    // delete the file
    await window[moduleName]['unlinkSync']('./mod2.js');
  }, 'fs');

  await browser.close();
})();

【讨论】:

    【解决方案2】:

    今天,我终于解决了这个问题。

    解决步骤:

    1. 使用 nwjs-sdk 用于 puppeteer 启动 executeablePath(ps:nw 版本必须高于 v0.35.4)
    2. nwjs-sdk package.json 添加 cmd args "--enable-features=nw2" (让 nwjs 启动使用 chrome 原生标签页,因为 puppeteer 需要它来使用和控制)

    示例代码:

    puppeteer.launch({
        executablePath: 'D:/nwjs-sdk-v0.42.2-win-x64/nw.exe',
        ignoreDefaultArgs:true,
        headless:false
    }).then(async browser => {
        const res = await  browser.pages();
        const page = res[0];
        await page.evaluate(async () => {
            const os = require('os');
            console.log("free memory="+os.freemem()/1024/1024);
        });
    })
    

    【讨论】:

      猜你喜欢
      • 2018-08-29
      • 2018-04-09
      • 2019-09-25
      • 1970-01-01
      • 2019-09-30
      • 1970-01-01
      • 1970-01-01
      • 2021-09-25
      • 1970-01-01
      相关资源
      最近更新 更多