Puppeteer如何循环点击动态View More链接直至全部内容加载完成
问题原因分析
你的代码存在3个核心问题,导致只能触发第一次内容加载:
- 选择器变量不统一:等待元素时用的是
viewMoreSelector,执行点击时用的是selector,第一次如果碰巧两个变量值相同可以正常运行,后续逻辑会找不到对应元素 - 误用
waitForNavigation:动态加载更多内容是前端发起AJAX请求做局部渲染,没有发生页面跳转,waitForNavigation会直接超时或者无意义等待,还没等新的「View More」渲染完成就进入了下一轮循环,自然找不到新加载的元素 - 没有处理点击后的元素状态变化:点击后旧的「View More」会先隐藏/移除,新内容加载完成后才会重新渲染按钮,缺少这一步的等待很容易拿到失效的DOM节点
修复后的代码
const clickViewMoreLoop = async () => { try { // 统一使用同一个选择器查询按钮 const viewMoreBtn = await page.waitForSelector(viewMoreSelector, { timeout: 5000 }); console.log("still loading…"); // 直接调用元素点击方法,避免二次查询出错 await viewMoreBtn.click(); // 等待当前按钮消失,说明点击已生效、内容正在加载 await page.waitForSelector(viewMoreSelector, { hidden: true, timeout: 5000 }); // 等待网络空闲,确保AJAX请求返回、内容渲染完成 await page.waitForNetworkIdle({ idleTime: 500 }); // 递归进入下一轮判断 return clickViewMoreLoop(); } catch (e) { // 超时说明已经没有更多内容可加载 console.log("all content loaded"); } } await clickViewMoreLoop(); // 此处可编写后续爬取完整列表的逻辑
可选优化方案
如果部分场景下按钮不会在点击后隐藏,只是加载完新内容后保持显示、所有内容加载完成才消失,可以把等待按钮隐藏的逻辑改成等待列表内容长度变化,稳定性更高:
// 替换为你要爬取的列表项选择器 const listItemSelector = ".list-item"; const clickViewMoreLoop = async (prevCount = 0) => { try { await page.waitForSelector(viewMoreSelector, { timeout: 5000 }); console.log("still loading…"); // 点击前获取当前列表项数量 const currentCount = await page.$$eval(listItemSelector, els => els.length); await page.click(viewMoreSelector); // 等待列表项数量增加,说明新内容已经渲染完成 await page.waitForFunction( (selector, oldCount) => document.querySelectorAll(selector).length > oldCount, {}, listItemSelector, currentCount ); return clickViewMoreLoop(currentCount); } catch (e) { console.log("all content loaded"); } } await clickViewMoreLoop();
内容的提问来源于stack exchange,提问作者regan
相关产品推荐
相关产品推荐

