如何提升基于Puppeteer的法国SPA网站爬虫程序运行速度?
优化Puppeteer爬虫速度的方案
针对你的法国动物保护协会(SPA)官网爬虫,以下是几个核心优化方向,能大幅缩短爬取时间:
1. 替换固定延迟为智能等待
原脚本里的setTimeout(5000)是硬编码等待,不管页面是否加载完成都要等够时间,这是最大的耗时点。改用Puppeteer的等待API,等待新元素加载完成或按钮状态变化后再继续,避免无效等待。
2. 合并多次$$eval调用
原脚本每次循环调用8次$$eval,每次调用都要在Node.js和浏览器进程间通信。合并成一次调用可减少7次往返开销,大幅提升效率。
3. 启动浏览器时禁用不必要资源
加载图片、CSS、非必要JS会占用大量时间,启动浏览器时禁用这些资源,只保留必要的网络请求;同时开启无头模式,减少渲染开销。
4. 移除冗余的初始滚动逻辑
原脚本的4次滚动+固定等待完全没必要,直接通过"查看更多"按钮加载所有内容即可,跳过该步骤节省时间。
5. 只处理新增元素,避免全量去重
每次点击"查看更多"后,只需要处理新加载的动物元素,而非全量查询所有元素再去重,减少DOM查询工作量。
优化后的完整代码
const puppeteer = require('puppeteer'); const fs = require('fs'); async function run() { // 启动无头浏览器,禁用不必要资源 const browser = await puppeteer.launch({ headless: 'new', args: [ '--disable-images', '--disable-css', '--no-sandbox', '--disable-setuid-sandbox' ], defaultViewport: null }); const page = await browser.newPage(); // 拦截不必要的请求,只保留核心资源 await page.setRequestInterception(true); page.on('request', (req) => { const blockedTypes = ['image', 'stylesheet', 'font']; if (blockedTypes.includes(req.resourceType())) { req.abort(); } else { req.continue(); } }); await page.goto('https://www.la-spa.fr/adoption/', { waitUntil: 'domcontentloaded', timeout: 30000 }); console.log('页面加载完成'); // 处理Cookie授权 try { await page.waitForSelector('#gdpr-accept', { timeout: 10000 }); await page.click('#gdpr-accept'); console.log('已接受Cookie'); } catch (err) { console.log('未找到Cookie按钮,跳过'); } let seeMoreActive = true; let animalData = []; let processedIds = new Set(); let counter = 0; let lastAnimalCount = 0; while (seeMoreActive) { counter++; try { // 等待"查看更多"按钮可点击 const seeMoreBtn = await page.waitForSelector('.c-see-more_link:not([disabled])', { timeout: 8000 }); await seeMoreBtn.click(); // 等待新动物卡片加载完成(通过元素数量变化判断) await page.waitForFunction( (lastCount) => document.querySelectorAll('a[data-animal-id]').length > lastCount, {}, lastAnimalCount ); lastAnimalCount = await page.$$eval('a[data-animal-id]', els => els.length); console.log(`第${counter}次加载完成`); } catch (error) { seeMoreActive = false; console.log('没有更多内容,结束加载'); } // 一次获取所有动物数据,减少进程通信次数 const currentAnimals = await page.$$eval('a[data-animal-id]', (elements) => { return elements.map(el => ({ id: el.getAttribute('data-animal-id'), link: el.href, race: el.getAttribute('data-animal-race'), age: el.getAttribute('data-animal-age'), sos: el.getAttribute('data-animal-sos'), gender: el.getAttribute('data-animal-gender'), species: el.getAttribute('data-animal-espece'), name: el.getAttribute('data-animal-nom'), establishment: el.closest('.f-miniAnimals_item')?.querySelector('a.f-miniAnimals_establishment span')?.textContent || '' })); }); // 过滤并添加新增数据 for (const animal of currentAnimals) { if (!processedIds.has(animal.id)) { processedIds.add(animal.id); animalData.push(animal); } } } console.log('爬取完成,共获取', animalData.length, '条动物数据'); const timestamp = new Date().getTime(); fs.writeFileSync(`animal_data_${timestamp}.json`, JSON.stringify(animalData, null, 2)); await browser.close(); } run();
额外优化建议
- 优先检查网站是否有公开API接口:直接调用API获取数据比模拟浏览器快10倍以上,可通过Chrome开发者工具的Network面板查看XHR请求。
- 若需爬取详情页,可使用
Promise.all并行处理多个页面,但注意控制请求频率避免触发反爬机制。 - 增加错误重试逻辑,避免单次请求失败导致整个爬虫中断。
内容的提问来源于stack exchange,提问作者francois LENNE
相关产品推荐
相关产品推荐

