Chaque fois que j'essaie de passer une fonction, comme ceci:
var myFunc = function() { console.log("lol"); };
await page.evaluate(func => {
func();
return true;
}, myFunc);
Je reçois:
(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.
Pourquoi? Comment le faire correctement?
Je vous remercie!
€: permettez-moi de clarifier: je le fais de cette façon parce que je veux d'abord trouver certains éléments DOM et les utiliser à l'intérieur de cette fonction, plus comme ceci (simplifié):
var myFunc = function(element) { element.innerHTML = "baz" };
await page.evaluate(func => {
var foo = document.querySelector('.bar');
func(foo);
return true;
}, myFunc);
Des problèmes similaires ont été discutés dans un marionnettiste issue .
Il existe plusieurs façons de résoudre votre problème. La première règle est de rester simple.
C'est le moyen le plus rapide de faire les choses, vous pouvez simplement passer la fonction et l'exécuter.
await page.evaluate(() => {
var myFunc = function(element) { element.innerHTML = "baz" };
var foo = document.querySelector('.bar');
myFunc(foo);
return true;
});
Vous pouvez exposer la fonction au préalable à l'aide d'un page.evaluate ou d'un 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;
});
Vous pouvez passer une poignée d'élément pour .évaluer et apporter des modifications comme bon vous semble.
const bodyHandle = await page.$('body');
const html = await page.evaluate(body => body.innerHTML, bodyHandle);
Vous pouvez cibler un élément et apporter des modifications à votre guise.
const html = await page.$eval('.awesomeSelector', e => {
e.outerHTML = "whatever"
});
L'astuce consiste à lire la documentation et à rester simple.
Passer la fonction avec le paramètre
// l'ajouter manuellement et l'exposer à la fenêtre
await page.evaluate(() => {
window.myFunc = function(element) { element.innerHTML = "baz" };
});
// puis appeler la fonction déclarée ci-dessus
await page.evaluate((param) => {
myFunc (param);
}, param);
Création d'une fonction d'assistance qui enveloppe 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;
Prend tous les paramètres et convertit les fonctions en chaîne.
Recrée ensuite les fonctions dans le contexte du navigateur.
Voir https://github.com/puppeteer/puppeteer/issues/1474
Vous pouvez utiliser cette fonction comme ceci:
const featuredItems = await evaluate(page, _getTile, selector)((get, s) => {
const items = Array.from(document.querySelectorAll(s));
return items.map(node => get(node));
});
Vous ne pouvez pas passer une fonction directement dans page.evaluate()
, mais vous pouvez appeler une autre méthode spéciale (page.exposeFunction
), Qui expose votre fonction en tant que fonction globale (également disponible en tant qu'attribut de votre page window
object), vous pouvez donc l'appeler lorsque vous êtes à l'intérieur de page.evaluate()
:
var myFunc = function() { console.log("lol"); };
await page.exposeFunction("myFunc", myFunc);
await page.evaluate(async () => {
await func();
return true;
});
N'oubliez pas que page.exposeFunction()
fera que votre fonction retournera un Promise
, puis, vous devrez utiliser async
et await
. Cela se produit car votre fonction ne sera pas exécutée dans votre navigateur , mais dans votre application nodejs
.
L'erreur est renvoyée car vous exécutez func();
mais func
n'est pas une fonction. Je mets à jour ma réponse pour répondre à votre question mise à jour:
Option 1: exécutez votre fonction dans le contexte de la page:
var myFunc = function(element) { element.innerHTML = "baz" };
await page.evaluate(func => {
var foo = document.querySelector('.bar');
myFunc(foo);
return true;
});
Option 2: passer le descripteur d'élément comme arguments
const myFunc = (element) => {
innerHTML = "baz";
return true;
}
const barHandle = await page.$('.bar');
const result = await page.evaluate(myFunc, barHandle);
await barHandle.dispose();
"