You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Puppeteer实现并行爬取:多页面打开多链接

解决Puppeteer并行爬取的超时与卡顿问题

我来帮你排查下代码里的问题,以及给出针对性的修复方案——你的核心思路(多页面并行+分组串行处理链接)是对的,但细节上的缺失导致了不稳定:

原代码的核心问题

  • 没有错误兜底:不管是页面加载还是元素查询,一旦出错就会直接中断整个Promise链,Promise.all会直接失败,这就是你时而正常时而崩溃的主要原因之一。
  • 无限制加载资源:默认情况下Puppeteer会加载页面所有资源(图片、广告脚本、视频等),3个并行页面同时疯狂拉资源,很容易把CPU/内存占满,导致浏览器卡顿超时。
  • 内存泄漏风险:爬完页面后没调用page.close(),页面实例一直占着内存,运行越久越卡。
  • 超时配置缺失:page.goto默认超时30秒,遇到慢页面直接抛出错误,没有自定义超时或重试机制。

改进后的代码

const totBrowserInstances = 3;
const promises = [];

// 封装单个链接的爬取逻辑,包含错误处理和基础重试
const scrapeSingleLink = async (page, link) => {
  // 最多重试2次
  for (let retry = 0; retry < 2; retry++) {
    try {
      await page.goto(link, {
        waitUntil: 'domcontentloaded',
        timeout: 20000 // 自定义20秒超时,避免无限制等待
      });

      // 等待目标元素渲染完成再查询,比直接evaluate更可靠
      await page.waitForSelector('.competizione .pagina h3', { timeout: 5000 });
      const result = await page.evaluate(() => {
        return document.querySelector('.competizione .pagina h3').innerText;
      });

      console.log('response:', result);
      return result;
    } catch (error) {
      console.warn(`爬取链接 ${link} 失败(第${retry+1}次):`, error.message);
      if (retry === 1) {
        console.error(`链接 ${link} 最终爬取失败`);
        return null;
      }
      // 重试前短暂等待
      await new Promise(resolve => setTimeout(resolve, 1000));
    }
  }
};

for (let i = 0; i < totBrowserInstances; i++) {
  console.log('Page ID Spawned', i);
  promises.push(
    (async () => {
      const page = await browser.newPage();

      // 拦截非必要资源,大幅降低加载压力
      await page.setRequestInterception(true);
      page.on('request', (request) => {
        const blockedResources = ['image', 'stylesheet', 'font', 'media'];
        if (blockedResources.includes(request.resourceType())) {
          request.abort();
        } else {
          request.continue();
        }
      });

      // 串行处理当前页面的所有链接(同一个页面不能同时加载多个地址)
      for (const link of instancesLinksMatrix[i]) {
        await scrapeSingleLink(page, link);
      }

      // 爬完后关闭页面,释放内存
      await page.close();
    })()
  );
}

await Promise.all(promises);
console.log('所有爬取任务执行完毕');

关键改动说明

  1. 错误处理+重试:把单个链接的爬取封装成函数,加入重试逻辑,即使某个链接临时失败也不会中断整个任务,还能输出错误信息方便排查。
  2. 资源拦截:拦截图片、样式表等非核心资源,每个页面的加载时间和内存占用能减少70%以上,彻底解决卡顿问题。
  3. 等待元素渲染:用page.waitForSelector代替直接evaluate,确保目标元素已经出现在DOM里再查询,避免元素找不到的错误。
  4. 自定义超时:给页面加载和元素查询都设置了合理的超时时间,避免无限制等待。
  5. 及时释放资源:每个页面完成任务后立即关闭,杜绝内存泄漏。

额外优化建议

  • 调整并发数:如果你的机器性能一般,totBrowserInstances建议设为4-6,太多会导致浏览器进程过载。
  • 使用浏览器上下文:如果需要更高的隔离性(比如不同页面的Cookie不共享),可以用browser.createIncognitoBrowserContext()创建独立上下文,每个页面在单独上下文里运行。
  • 日志优化:可以把爬取结果和错误信息写入文件,方便后续分析。

内容的提问来源于stack exchange,提问作者Jacob

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 10:04:25