Playwright waitForFunction无法获取循环id变量 多页爬虫报错
报错根因
Playwright的waitForFunction、evaluate类API,传入的回调不是在Node.js环境运行,而是会被序列化后下发到浏览器的独立页面上下文执行,两侧的作用域完全隔离:
- 浏览器上下文的代码无法通过闭包直接读取Node.js作用域内的变量
- 本例中循环定义的
id是Node.js侧的循环变量,浏览器端执行的回调完全无法感知这个变量,因此访问时会被识别为undefined
这个机制也匹配之前的验证结果:在循环内外写的console.log(foodGroupIdsOnPage1)运行在Node.js侧,可正常输出;但回调里的id运行在浏览器侧,自然拿不到Node侧的变量值。
修复方案
按照Playwright的跨上下文传参规则,将Node侧的变量显式传递到浏览器上下文,核心修改如下:
- 调整
waitForFunction的调用结构:给浏览器端的回调添加对应形参,在API调用的第二个参数位置,传入Node侧的id值。
修改后的核心代码片段:
for (let id of foodGroupIdsOnPage1) { await tab1.waitForFunction( // 形参targetId用于接收从Node侧传入的id值 function (targetId) { const page1_foodGroupButton = document.querySelector(targetId).querySelector('a') page1_foodGroupButton.click(); }, // 第二个参数传入Node侧的id,会被传递到浏览器上下文 id ); await tab1.waitForTimeout(2000); await tab1.click('[id^=grp] > a'); await tab1.waitForTimeout(2000); const ArrayOfTabs = context.pages(); let tab2 = ArrayOfTabs[1]; await tab2.evaluate(function extractFoodGroupData() { let tableOfAllFoods = []; let rowsOnPage3 = document.querySelector(".colorTable2").querySelectorAll("tr"); for (let row_OnPage3 of rowsOnPage3) { let arrayNutritionOfOneFood = []; let cellsInOneRow = row_OnPage3.querySelectorAll("td"); for (let cell of cellsInOneRow) { arrayNutritionOfOneFood.push(cell.innerText); } tableOfAllFoods.push(arrayNutritionOfOneFood); } console.log(tableOfAllFoods); }); tab2.close(); tab1.goBack(); // 返回后增加等待,确保页面加载完成再进入下一轮循环 await tab1.waitForTimeout(1000); }
- 配套注意事项,避免循环运行报错:
- 跨上下文传参仅支持可结构化克隆的纯数据类型(字符串、数字、普通对象、数组),本例中的
id是字符串格式的CSS选择器,完全符合传参要求,无需额外转换 - 不需要在浏览器端的回调里对点击操作加
await,waitForFunction本身会等待DOM状态满足执行条件,多余的异步等待反而会拖慢爬取效率 - 之前代码里注释掉的将
id作为参数数组传入的思路是正确的,只是没有给回调定义对应的形参,才导致变量没有被成功传递
- 跨上下文传参仅支持可结构化克隆的纯数据类型(字符串、数字、普通对象、数组),本例中的
内容的提问来源于stack exchange,提问作者Ken LEE
相关产品推荐
相关产品推荐

