
本文详解 puppeteer 分页爬取中常见的 url 重复处理、导航失效及页码错乱问题,提供可落地的解决方案,确保每页仅处理一次,并正确识别末页边界。
在使用 Puppeteer 进行分页爬取(如 https://clerk.house.gov/Votes 这类 前端 分页站点)时,一个典型陷阱是:页面通过哈希跳转(#)或异步 路由 更新 URL,导致 page.waitForNavigation() 无法可靠触发——这正是原代码反复打印 ?page=2#、?page=3# 等重复 URL 的根本原因。waitForNavigation 仅监听完整的导航事件(如 GET 请求),而 SPA 或锚点驱动的分页常不触发该事件。
✅ 正确做法:用 browser.waitForTarget() 监听新页面加载
替代脆弱的 waitForNavigation(),应监听 浏览器 目标(Target)的创建与 URL 变化。关键逻辑如下:
const url = page.url(); console.log('Processing page:', url); // ✅ 提取当前页码(兼容初始页无参数的情况)const currentPageNum = url.includes('?page=') ? parseInt(url.match(/page=(d+)/)[1], 10) : 1; // ✅ 点击“下一页”按钮(注意:直接 click() 即可,无需 selector 参数)await nextButton.click(); // ✅ 等待目标 URL 变为预期的下一页(更鲁棒,不受 hash 干扰)await browser.waitForTarget(target => target.url().endsWith(`?page=${currentPageNum + 1}`), {timeout: 10000} );
⚠️ 注意事项:nextButton.click() 不接受 CSS 选择器参数(原代码 click(‘a[aria-label=”Next”]…’) 是错误用法,会报错);必须在点击前获取当前 URL,否则 page.url() 可能仍是旧地址(因点击后 URL 更新有延迟);waitForTarget 需设置合理超时(如 10s),避免无限等待;初始页(/Votes)无 ?page= 参数,需显式判断并设为 page=1,否则后续页码计算错误。
✅ 补充:处理末页边界(防止漏掉最后一页)
原逻辑在「下一页按钮消失时退出」,意味着 最后一页的数据从未被提取 。修正方式是: 先处理当前页,再尝试翻页。完整结构建议如下:
while (true) {// ✅ 1. 先处理当前页(无论是否为末页)console.log('Processing page:', page.url()); // ? 在此处插入你的数据提取逻辑,例如:// const votes = await page.$$eval('.vote-item', els => els.map(e => e.textContent)); // ✅ 2. 尝试查找并点击下一页按钮 const nextButton = await page .waitForSelector('a[aria-label="Next"] span[class~="fa"]', {timeout: 3000}) .catch(() => null); if (!nextButton) {console.log('No more pages. Scraping completed.'); break; } // ✅ 3. 执行翻页(使用上述 waitForTarget 方案)const currentUrl = page.url(); const pageNum = currentUrl.includes('?page=') ? parseInt(currentUrl.match(/page=(d+)/)[1], 10) : 1; await nextButton.click(); await browser.waitForTarget( t => t.url().endsWith(`?page=${pageNum + 1}`), {timeout: 10000} ); }
✅ 总结
- ❌ 避免 page.waitForNavigation() 处理哈希 /SPA 分页;
- ✅ 使用 browser.waitForTarget() + URL 断言,精准等待目标页加载;
- ✅ 始终先处理当前页,再判断是否翻页,确保末页不遗漏;
- ✅ 点击操作后立即捕获 page.url(),避免页码解析错误;
- ? 调试时可添加 await page.screenshot({path:page-${pageNum}.png}); 辅助验证页面状态。
遵循以上模式,即可构建稳定、可维护的 Puppeteer 分页 爬虫。