Puppeteer调试:evaluate/$$eval内部断点无法触发问题排查
我用以下参数启动Puppeteer:
const browser = await puppeteer.launch({ args: ["--no-sandbox"], headless: "false", devtools: "true", ignoreDefaultArgs: ["--disable-extensions"], slowMo: 150, });
遇到的问题是:Puppeteer直接跳过相关函数,在$$eval或evaluate方法上打断点,调试器只会进入Node.js代码,完全无法进入传入的自定义函数。以下两个示例都无法正常工作:
第一个示例:
const res = await page.evaluate((day) => { const div = day.querySelector("summary div div div"); debugger; console.log(div); }, day);
第二个示例:
const days = await page.$$eval( "main div section details", async (days, getMoonPhase) => { debugger; return await Promise.all( days.map(async (dayEl, dayIndex) => { // get just moon phase if (dayIndex === 0) { const moonPhase = await getMoonPhase(dayIndex); return { moonPhase }; } ....a lot of other code... return allInfo; }) ); }, await getMoonPhase(page) );
问题原因及解决办法
核心原因
evaluate/$$eval传入的自定义函数是运行在浏览器页面的上下文里,和Node.js代码的上下文完全隔离。你在Node端调试器打的断点只能追踪Node侧的代码,浏览器里的函数是被序列化后传到浏览器执行的,Node端调试器根本管不到这部分逻辑。
具体解决步骤
利用浏览器DevTools调试自定义函数
保留你函数里的debugger;语句,启动Puppeteer后别关掉自动弹出的浏览器DevTools。当代码执行到浏览器上下文的debugger时,浏览器的DevTools会自动暂停,此时就能调试自定义函数里的代码了——你可以查看变量、单步执行,和调试普通前端页面代码一样。修复第二个示例的参数错误
你把await getMoonPhase(page)作为参数传给$$eval是错误的:$$eval的额外参数只能传可序列化的值(比如字符串、数字、普通对象),函数没法被序列化传到浏览器上下文,所以浏览器里根本拿不到getMoonPhase这个函数,自然会出错。
解决方式:要么把getMoonPhase的逻辑直接写到$$eval的自定义函数里,要么在Node端提前用page.evaluate执行getMoonPhase拿到结果,再把结果作为普通值传给$$eval。异步函数的注意事项
如果在$$eval里用异步函数,要确保内部的Promise逻辑正确。另外$$eval本身会处理返回的Promise,外部加await是没问题的,但要注意浏览器上下文里的异步操作必须能在浏览器环境中正常执行,不能依赖Node端的API。
内容的提问来源于stack exchange,提问作者lowzyyy

