【问题标题】:Why can't I access 'window' in an exposeFunction() function with Puppeteer?为什么我不能使用 Puppeteer 在exposeFunction() 函数中访问“窗口”?
【发布时间】:2018-06-25 03:42:51
【问题描述】:

我有一个非常简单的 Puppeteer 脚本,它使用 exposeFunction() 在无头 Chrome 中运行某些东西。

(async function(){

    var log = console.log.bind(console),
        puppeteer = require('puppeteer');


    const browser = await puppeteer.launch();
    const page = await browser.newPage();

    var functionToInject = function(){
        return window.navigator.appName;
    }

    await page.exposeFunction('functionToInject', functionToInject);

    var data = await page.evaluate(async function(){
        console.log('woo I run inside a browser')
        return await functionToInject();
    });

    console.log(data);

    await browser.close();

})()

这失败了:

ReferenceError: window is not defined

指的是注入函数。如何在无头 Chrome 中访问window

我知道我可以改为 evaluate(),但这不适用于我动态传递的函数:

(async function(){

    var log = console.log.bind(console),
        puppeteer = require('puppeteer');

    const browser = await puppeteer.launch();
    const page = await browser.newPage();

    var data = await page.evaluate(async function(){
        console.log('woo I run inside a browser')
        return window.navigator.appName;
    });

    console.log(data);

    await browser.close();

})()

【问题讨论】:

  • 我不太确定为什么会这样,但您可以将 window 作为变量传递并在您的函数中使用它。
  • 你确定吗? Unhandled promise rejection (rejection id: 1): Error: Evaluation failed: TypeError: Converting circular structure to JSON
  • 第二个脚本不起作用是什么意思?它会抛出与第一个相同的错误吗?

标签: node.js google-chrome headless-browser puppeteer


【解决方案1】:

evaluate函数

您可以使用evaluate 传递动态脚本

(async function(){
    var puppeteer = require('puppeteer');
    const browser = await puppeteer.launch();
    const page = await browser.newPage();

    var functionToInject = function(){
        return window.navigator.appName;
    }

    var data = await page.evaluate(functionToInject); // <-- Just pass the function
    console.log(data); // outputs: Netscape
    
    await browser.close();
})()

addScriptTagreadFileSync

您可以将函数保存到单独的文件中,并使用addScriptTag 使用该函数。

await page.addScriptTag({path: 'my-script.js'});

evaluatereadFileSync

await page.evaluate(fs.readFileSync(filePath, 'utf8'));

或者,将参数化函数作为字符串传递给page.evaluate

await page.evaluate(new Function('foo', 'console.log(foo);'), {foo: 'bar'});

动态创建新函数

把它变成runnable 函数怎么样?

function runnable(fn) {
  return new Function("arguments", `return ${fn.toString()}(arguments)`);
}

上面将使用提供的参数创建一个新函数。我们可以传递任何我们想要的函数。

比如下面这个带有window的函数,连同参数,

function functionToInject() {
  return window.location.href;
};

promise 也能完美运行,

function functionToInject() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(window.location.href);
    }, 5000);
  });
}

还有参数,

async function functionToInject(someargs) {
  return someargs; // {bar: 'foo'}
};

evaluate调用想要的函数,

var data = await page.evaluate(runnable(functionToInject), {bar: "foo"});
console.log(data); // shows the location

【讨论】:

    【解决方案2】:

    exposeFunction() 不是这项工作的正确工具。

    来自Puppeteer docs

    page.exposeFunction(name, puppeteerFunction)

    puppeteerFunction 回调函数,将在Puppeteer 的上下文中调用

    “在 puppeteer 的上下文中”有点含糊,但请查看 evaluate() 的文档:

    page.evaluateHandle(pageFunction, ...args)

    pageFunction 要评估的函数在页面上下文中

    exposeFunction() 不公开要在页面内运行的函数,而是公开要在节点中运行的函数从页面调用

    我必须使用evaluate():

    【讨论】:

    • 嗨,我遇到了同样的问题,我想知道您是否设法找到比 page.evaluate 更好的解决方案?
    • 你的代码是怎么变成不是你在使用evaluate的?
    【解决方案3】:

    您的问题可能与page.exposeFunction() 将使您的函数返回Promise(需要使用asyncawait)这一事实有关。发生这种情况是因为您的函数将不是running inside your browser,而是在您的nodejs 应用程序中,并且它的结果将在浏览器代码中来回发送。这就是为什么传递给page.exposeFunction() 的函数现在返回的是一个promise 而不是实际结果。它解释了为什么没有定义 window 函数,因为您的函数在 nodejs(不是您的浏览器)内运行,而在 nodejs 内没有可用的 window 定义。

    相关问题:

    1. exposeFunction() does not work after goto()
    2. exposed function queryseldtcor not working in puppeteer
    3. How to use evaluateOnNewDocument and exposeFunction?
    4. exposeFunction remains in memory?
    5. Puppeteer: pass variable in .evaluate()
    6. Puppeteer evaluate function
    7. allow to pass a parameterized funciton as a string to page.evaluate
    8. Functions bound with page.exposeFunction() produce unhandled promise rejections
    9. How to pass a function in Puppeteers .evaluate() method?
    10. How can I dynamically inject functions to evaluate using Puppeteer?

    【讨论】:

      猜你喜欢
      • 2020-03-27
      • 2012-06-17
      • 2016-03-16
      • 1970-01-01
      • 1970-01-01
      • 2021-03-19
      • 2013-10-01
      • 2019-08-14
      • 1970-01-01
      相关资源
      最近更新 更多