使用Puppeteer+NestJS爬取网站时无法循环点击下一页
Puppeteer + NestJS 分页爬取问题解决(循环终止、数据提取失效)
核心问题根源
- 循环仅执行一次:循环条件判断时机错误,或点击下一页后未等待DOM更新,误判已到最后一页
- 第二页数据无法提取:单页应用切换分页后,原有DOM元素被销毁,未重新定位表格且未等待新数据加载
- 截图未生成:截图时机过早(页面未渲染完成)或路径权限问题
解决方案步骤
等待页面更新再操作
点击下一页后,不要立刻执行数据提取或条件判断,用waitForFunction或waitForSelector等待表格内容更新,避免操作未渲染完成的DOM。修正循环条件逻辑
不要提前判断下一页状态,需在提取完当前页数据后,动态查询下一页按钮的disabled属性(仅最后一页会添加该属性),确保判断的是实时状态。重新定位DOM元素
每一页加载完成后,重新查询表格行元素,不要复用之前的元素句柄——单页应用分页切换会重建DOM,旧句柄会失效。调整截图时机
在提取完当前页数据后执行截图,确保页面渲染完成,同时指定明确的文件路径(如带页码的文件名)。
修改后的NestJS服务代码示例
import { Injectable } from '@nestjs/common'; import puppeteer from 'puppeteer'; @Injectable() export class ScraperService { async scrapeRcaData(): Promise<Array<{ id: string; numeroImmatriculation: string; dateImmat: string; titre: string; refId: string; }>> { const browser = await puppeteer.launch({ headless: false }); // 调试时关闭无头模式,方便观察页面 const page = await browser.newPage(); await page.goto('https://rca.cnc.fr/rca.frontoffice/#', { waitUntil: 'networkidle2' }); // 此处添加表单提交逻辑(你已实现的部分) // ... const allResults: Array<any> = []; let currentPage = 1; let hasNextPage = true; do { // 等待当前页表格加载完成 await page.waitForSelector('table tbody tr'); // 替换为实际表格行选择器 // 提取当前页数据 const pageData = await page.evaluate(() => { const rows = Array.from(document.querySelectorAll('table tbody tr')); return rows.map(row => ({ // 替换为实际DOM字段选择器 id: row.querySelector('td:nth-child(1)').textContent.trim(), numeroImmatriculation: row.querySelector('td:nth-child(2)').textContent.trim(), dateImmat: row.querySelector('td:nth-child(3)').textContent.trim(), titre: row.querySelector('td:nth-child(4)').textContent.trim(), refId: row.querySelector('td:nth-child(5)').textContent.trim(), })); }); allResults.push(...pageData); console.log(`已爬取第${currentPage}页,共${pageData.length}条数据`); // 生成当前页截图 await page.screenshot({ path: `./screenshots/page-${currentPage}.png`, fullPage: true }); // 动态判断是否有下一页 hasNextPage = await page.$eval('.pagination-btn.next', btn => !btn.disabled); // 替换为实际下一页按钮选择器 if (hasNextPage) { // 点击下一页并等待新数据加载 await page.click('.pagination-btn.next'); // 等待表格内容更新(确保新页数据已渲染) await page.waitForFunction( () => document.querySelectorAll('table tbody tr').length > 0, { timeout: 10000 } ); currentPage++; } } while (hasNextPage); await browser.close(); return allResults; } }
关键注意事项
- 选择器准确性:务必将代码中所有选择器(表格行、下一页按钮)替换为网站实际DOM选择器,可通过浏览器开发者工具查看
- 等待策略:单页应用不要依赖
waitForNavigation,无页面跳转时,用waitForFunction等待DOM内容变化更可靠 - 调试技巧:开启
headless: false,直观观察页面操作流程,排查点击失败、数据提取失效等问题
内容的提问来源于stack exchange,提问作者nicocrest
相关产品推荐
相关产品推荐

