每当我尝试传递一个函数时,就像这样:
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);
答案 0 :(得分:14)
您不能将函数直接传递到page.evaluate()
中,但是可以调用另一个特殊方法(page.exposeFunction
),该方法会将您的函数公开为全局函数(也可以作为页面的属性使用{ {1}}对象),因此您可以在window
内部时调用它:
page.evaluate()
请记住,var myFunc = function() { console.log("lol"); };
await page.exposeFunction("myFunc", myFunc);
await page.evaluate(async () => {
await myFunc();
return true;
});
将使您的函数返回page.exposeFunction()
,然后,您需要使用Promise
和async
。发生这种情况是因为您的功能不是running inside your browser,而是在您的await
应用程序内部。
答案 1 :(得分:4)
在木偶操纵者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);
您可以定位一个元素并根据需要进行更改。
const html = await page.$eval('.awesomeSelector', e => {
e.outerHTML = "whatever"
});
诀窍是read the docs并保持简单。
答案 2 :(得分:1)
抛出错误是因为您执行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();
`
答案 3 :(得分:0)
具有参数的传递功能
//手动添加并显示在窗口中
await page.evaluate(() => {
window.myFunc = function(element) { element.innerHTML = "baz" };
});
//,然后调用上面声明的函数
await page.evaluate((param) => {
myFunc (param);
}, param);
答案 4 :(得分:0)
创建了一个包装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));
});
答案 5 :(得分:0)
// External function to run inside evaluate context
function getData() {
return document.querySelector('title').textContent;
}
function mainFunction(url, extractFunction){
let browser = await puppeteer.launch({});
let page = await browser.newPage();
await page.goto(url);
let externalFunction = Object.assign(extractFunction);
let res = await this.page.evaluate(externalFunction)
console.log(res);
}
// call it here
mainFunction('www.google.com',getData);
答案 6 :(得分:0)
function stringifyWithFunc(obj) {
const str = JSON.stringify(obj, function(key, val) {
if (typeof val === "function") {
return val + "";
return val;
});
return str;
}
function parseWithFunction(str) {
const obj = JSON.parse(str, function(key, val) {
if (typeof val === 'string' && val.includes("function")) {
return eval(`(${val})`);
}
return val;
});
return obj;
}
function testFunc() {
console.log(123);
};
const params = {
testFunc,
a: 1,
b: null
}
await page.exposeFunction("parseWithFunction", parseWithFunction);
await pageFrame.$eval(".category-content", (elem, objStr) => {
const params = parseWithFunction(objStr);
params.testFunc()
},
stringifyWithFunc(params)
);