次のように関数を渡そうとすると、
var myFunc = function() { console.log("lol"); };
await page.evaluate(func => {
func();
return true;
}, myFunc);
私は得ます:
(node:13108) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Evaluation failed: TypeError: func is not a function
at func (<anonymous>:9:9)
(node:13108) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
どうして?それを正しく行う方法は?
ありがとうございました!
€:明確にしましょう:最初にいくつかのDOM要素を見つけ、その関数内で使用したいので、このようにしています。
var myFunc = function(element) { element.innerHTML = "baz" };
await page.evaluate(func => {
var foo = document.querySelector('.bar');
func(foo);
return true;
}, myFunc);
同様の問題が操り人形師 issue で議論されています。
問題に対処する方法はいくつかあります。最初のルールは、シンプルにすることです。
これが最も速い方法です。関数を渡して実行するだけです。
await page.evaluate(() => {
var myFunc = function(element) { element.innerHTML = "baz" };
var foo = document.querySelector('.bar');
myFunc(foo);
return true;
});
Page.evaluateまたはpage.addScriptTagを使用して、事前に関数を公開できます。
// add it manually and expose to window
await page.evaluate(() => {
window.myFunc = function(element) { element.innerHTML = "baz" };
});
// add some scripts
await page.addScriptTag({path: "myFunc.js"});
// Now I can evaluate as many times as I want
await page.evaluate(() => {
var foo = document.querySelector('.bar');
myFunc(foo);
return true;
});
エレメントハンドルを渡して、.evaluateに変更し、必要に応じて変更を加えることができます。
const bodyHandle = await page.$('body');
const html = await page.evaluate(body => body.innerHTML, bodyHandle);
1つの要素を対象として、必要に応じて変更を加えることができます。
const html = await page.$eval('.awesomeSelector', e => {
e.outerHTML = "whatever"
});
秘訣は ドキュメントを読む でシンプルにすることです。
パラメーター付きのパス関数
//手動で追加してウィンドウに公開します
await page.evaluate(() => {
window.myFunc = function(element) { element.innerHTML = "baz" };
});
//次に、上記で宣言された関数を呼び出します
await page.evaluate((param) => {
myFunc (param);
}, param);
page.evaluate
をラップするヘルパー関数を作成しました:
const evaluate = (page, ...params) => browserFn => {
const fnIndexes = [];
params = params.map((param, i) => {
if (typeof param === "function") {
fnIndexes.Push(i);
return param.toString();
}
return param;
});
return page.evaluate(
(fnIndexes, browserFnStr, ...params) => {
for (let i = 0; i < fnIndexes.length; i++) {
params[fnIndexes[i]] = new Function(
" return (" + params[fnIndexes[i]] + ").apply(null, arguments)"
);
}
browserFn = new Function(
" return (" + browserFnStr + ").apply(null, arguments)"
);
return browserFn(...params);
},
fnIndexes,
browserFn.toString(),
...params
);
};
export default evaluate;
すべてのパラメータを受け取り、関数を文字列に変換します。
次に、ブラウザコンテキストで関数を再作成します。
参照 https://github.com/puppeteer/puppeteer/issues/1474
この関数は次のように使用できます。
const featuredItems = await evaluate(page, _getTile, selector)((get, s) => {
const items = Array.from(document.querySelectorAll(s));
return items.map(node => get(node));
});
関数を直接page.evaluate()
に渡すことはできませんが、関数をグローバル関数として公開する別の特別なメソッド(_page.exposeFunction
_)を呼び出すことができます(ページの属性としても使用できますwindow
オブジェクト)なので、page.evaluate()
の内部で呼び出すことができます:
_var myFunc = function() { console.log("lol"); };
await page.exposeFunction("myFunc", myFunc);
await page.evaluate(async () => {
await func();
return true;
});
_
page.exposeFunction()
を指定すると、関数がPromise
を返すようになるので、async
とawait
を使用する必要があります。これは、関数が ブラウザー内で実行される ではなく、nodejs
アプリケーション内であるために発生します。
func();
を実行したが、func
が関数ではないため、エラーがスローされます。私はあなたの更新された質問に答えるために私の答えを更新します:
オプション1:ページコンテキストで関数を実行します。
var myFunc = function(element) { element.innerHTML = "baz" };
await page.evaluate(func => {
var foo = document.querySelector('.bar');
myFunc(foo);
return true;
});
オプション2:要素ハンドルを引数として渡す
const myFunc = (element) => {
innerHTML = "baz";
return true;
}
const barHandle = await page.$('.bar');
const result = await page.evaluate(myFunc, barHandle);
await barHandle.dispose();
`