node.js - PuppeteerでExposureFunction()関数の「ウィンドウ」にアクセスできないのはなぜですか?

node.js google-chrome headless-browser puppeteer

Puppeteerを使用してヘッドレスChrome内で何かを実行する非常に単純なexposeFunction()スクリプトがあります。

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

})()
答え
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();
})()


addScriptTagおよびreadFileSync

関数を別のファイルに保存し、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関数:Dにするとどうですか?

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


上記は、提供された引数で新しい関数を作成します。必要な関数を渡すことができます。

windowを伴う次の関数と引数とともに、

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


約束も完璧に機能し、

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
関連記事

javascript - npm installはpinkie-promiseに使用可能な有効なバージョンを返しません

node.js - オプション[useMongoClient]はサポートされていません

javascript - ハンドラー自体からイベントハンドラーを削除する

javascript - ネイティブJavaScriptおよびnode.jsでロングポーリングを使用する方法

node.js - Mongooseは `$ set`フィールドではなくドキュメントを上書きします

node.js - 複数の環境の復号化。 AWS Lambdaの変数

node.js - Herokuで複数のコマンドを使用してProcfileを実行する方法は?

javascript - REPLでアプリを使用するときに約束を解決する方法

javascript - PHP exec()からNode.jsスクリプトを呼び出すときにパラメーターを渡すにはどうすればよいですか?

node.js - node.js v5.12.0をインストールしましたが、バージョンはv0.12.2を示しています