
本文详解 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 分页爬虫。










