如何在点击加载更多后等待网站新组件加载完成(Puppeteer)
解决思路与修改方案
你的核心问题是同步循环点击加载按钮时,没有等待每次点击后的内容加载完成,直接触发后续抓取,导致数据不全。DOMContentLoaded和window.onload只在初始页面加载时生效,对后续动态加载的内容无效,确实需要等待每次点击后的API请求完成或DOM更新。
关键问题分析
- 原代码中
val()是同步循环点击,浏览器根本来不及处理每次点击后的加载请求,就已经执行完所有点击,此时大部分内容还没加载。 page.evaluate里的函数没有异步等待逻辑,无法感知动态加载的完成状态。- 硬编码
setTimeout等待10分钟完全不可靠,既浪费时间又可能在网络慢时仍未加载完成。
修改后的代码
const puppeteer = require('puppeteer'); const fs = require('fs'); (async () => { const browser = await puppeteer.launch({ headless: false, args: [ '--no-sandbox', '--disable-setuid-sandbox', '--window-size=1600,1200', ], defaultViewport: null, }); const page = await browser.newPage(); await page.goto( 'https://www.gelbeseiten.de/suche/friseur/bundesweit?umkreis=4000', { waitUntil: 'networkidle2' } ); // 异步点击加载更多,直到按钮消失或无法点击 async function loadAllContent() { while (true) { try { // 等待加载按钮出现,最多等5秒 await page.waitForSelector('#mod-LoadMore--button', { timeout: 5000 }); // 点击按钮,同时等待网络空闲(等待API请求完成)+ DOM更新 const prevItemCount = await page.evaluate(() => document.querySelectorAll('.mod-AdresseKompakt__phoneNumber').length ); await Promise.all([ page.click('#mod-LoadMore--button'), page.waitForNetworkIdle({ idleTime: 1000 }), // 等待1秒无网络请求 // 等待列表项数量增加,确保新内容已渲染 page.waitForFunction( prevCount => document.querySelectorAll('.mod-AdresseKompakt__phoneNumber').length > prevCount, {}, prevItemCount ) ]); } catch (err) { // 按钮消失或超时,说明加载完成 break; } } } // 抓取数据 async function getData() { return await page.evaluate(() => { const liTags = document.querySelectorAll('.mod-AdresseKompakt__phoneNumber'); return Array.from(liTags).map(tag => tag.innerText.trim()); }); } // 执行流程:先加载所有内容,再抓取数据 await loadAllContent(); const phoneNumbers = await getData(); // 写入CSV const writeStream = fs.createWriteStream('data.csv'); writeStream.write(`id, phone number\n`); phoneNumbers.forEach((number, index) => { writeStream.write(`${index},${number}\n`); }); writeStream.end(); await browser.close(); })();
核心改进点
- 异步循环加载:用
while(true)循环替代同步for循环,每次点击后等待三个条件:- 网络空闲
waitForNetworkIdle:确保加载新内容的API请求完成。 - 列表项数量增加:确保新内容已经渲染到DOM中。
- 等待按钮存在:如果按钮消失,说明没有更多内容可加载。
- 网络空闲
- 分离加载与抓取逻辑:把加载所有内容和抓取数据分成两个独立函数,逻辑更清晰。
- 移除硬编码超时:不再依赖
setTimeout,而是根据实际加载状态判断是否完成。
额外提示
- 如果网站有反爬机制,可适当增加点击间隔(比如
await page.waitForTimeout(500)),避免被封。 waitForNetworkIdle的idleTime可根据网站加载速度调整,确保所有请求都已完成。
内容的提问来源于stack exchange,提问作者BlackPearl02
相关产品推荐
相关产品推荐

