为什么我不能在使用Puppeteer的exposeFunction()函数中访问'window'?

mik*_*ana 7 google-chrome node.js headless-browser puppeteer

我有一个非常简单的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();

})()
Run Code Online (Sandbox Code Playgroud)

这失败了:

ReferenceError: window is not defined
Run Code Online (Sandbox Code Playgroud)

其中指的是注入的功能.如何window在无头Chrome内部进行访问?

我知道我可以做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();

})()
Run Code Online (Sandbox Code Playgroud)

Md.*_*her 7

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();
})()
Run Code Online (Sandbox Code Playgroud)

addScriptTagreadFileSync

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

await page.addScriptTag({path: 'my-script.js'});
Run Code Online (Sandbox Code Playgroud)

evaluatereadFileSync

await page.evaluate(fs.readFileSync(filePath, 'utf8'));
Run Code Online (Sandbox Code Playgroud)

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

await page.evaluate(new Function('foo', 'console.log(foo);'), {foo: 'bar'});
Run Code Online (Sandbox Code Playgroud)

动态创建新功能

您是否需要其他方法来解决这个问题?把它变成一个runnable函数:D怎么样?

function runnable(fn) {
  return new Function("arguments", `return ${fn.toString()}(arguments)`);
}
Run Code Online (Sandbox Code Playgroud)

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

例如带有的以下函数window以及参数,

function functionToInject() {
  return window.location.href;
};
Run Code Online (Sandbox Code Playgroud)

也可以完美地履行承诺

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

加上论点

async function functionToInject(someargs) {
  return someargs; // {bar: 'foo'}
};
Run Code Online (Sandbox Code Playgroud)

通过调用所需的功能evaluate

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


mik*_*ana 5

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

来自Puppeteer文档

page.exposeFunction(name,puppeteerFunction)

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

“在木偶的背景下”有点模糊,但请查看文档evaluate()

page.evaluateHandle(pageFunction,... args)

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

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

我必须使用evaluate()