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

使用Puppeteer批量监控网站的内存占用优化方案问询

优化Puppeteer批量页面监控的内存占用方案

看起来你在Puppeteer的资源优化上踩了两个常见的坑——要么过度隔离(Docker每个实例一个浏览器),要么无限制并行(一次性开100个浏览器实例)。我来帮你拆解问题,然后给出几个能大幅降低内存占用的优化方案:

核心问题分析

你当前的代码有两个致命的内存浪费点:

  1. 无限制并行执行:循环里直接调用run()但没有await,导致100个浏览器实例几乎同时启动,瞬间把内存拉满。
  2. 重复创建浏览器实例:每个run()都启动一个全新的浏览器,而浏览器本身就是内存大户——启动一个浏览器的开销远大于在同一个浏览器里创建新页面的开销。

优化方案一:复用单个浏览器实例+控制并发数

这是最有效的优化方式,通过复用一个浏览器实例来处理所有页面,同时限制并发页面数量,避免内存过载。

示例代码

const puppeteer = require('puppeteer');
const pLimit = require('p-limit'); // 用这个库轻松控制并发量,也可以自己实现

// 控制并发页面数,建议根据服务器内存调整(比如10-20个)
const concurrentLimit = 10;
const limit = pLimit(concurrentLimit);

async function processPage(browser, url) {
  let page = null;
  try {
    const startTime = new Date();
    console.log(`开始处理 ${url} | ${startTime}`);

    // 在复用的浏览器中创建新页面
    page = await browser.newPage();
    
    // 访问目标页面,等待网络空闲
    await page.goto(`https://webpage.com/${url}`, { waitUntil: 'networkidle2' });
    
    // 等待5分钟(根据需求调整)
    await page.waitFor(5 * 60 * 1000);
    
    // 获取页面内容(如果只需要特定数据,建议用page.evaluate()提取,更省内存)
    const html = await page.content();
    
    // 这里可以添加你的数据处理逻辑(比如保存到数据库、分析内容等)
    
    const endTime = new Date();
    console.log(`完成处理 ${url} | ${endTime}`);
  } catch (error) {
    console.error(`处理 ${url} 出错:`, error);
  } finally {
    // 无论成功失败,都必须关闭页面释放资源
    if (page) await page.close();
  }
}

async function main() {
  // 启动单个浏览器实例,配置内存优化参数
  const browser = await puppeteer.launch({
    args: [
      '--no-sandbox',
      '--disable-setuid-sandbox',
      '--disable-dev-shm-usage', // 解决Linux下/dev/shm空间不足的问题,减少内存占用
      '--disable-accelerated-2d-canvas', // 禁用硬件加速画布
      '--no-first-run', // 跳过首次运行初始化
      '--no-default-browser-check', // 跳过默认浏览器检查
      '--disable-background-networking', // 禁用后台网络请求
      '--disable-renderer-backgrounding', // 禁止渲染器进入后台模式
      '--start-maximized=false' // 不要最大化窗口
    ],
    headless: 'new', // 使用新版无头模式(Puppeteer v19+支持),比旧版更省内存
    defaultViewport: { width: 1280, height: 720 } // 指定固定视口,避免默认大视口占用额外内存
  });

  try {
    // 假设webpage是你的100个目标URL数组
    const webpage = [/* 你的URL列表 */];
    
    // 用并发限制批量处理所有URL
    await Promise.all(webpage.map(url => limit(() => processPage(browser, url))));
  } catch (error) {
    console.error('主进程出错:', error);
  } finally {
    // 所有任务完成后关闭浏览器
    await browser.close();
  }
}

// 启动主程序
main();

优化方案二:分批次复用浏览器(应对反爬场景)

如果目标网站有反爬机制,限制单个浏览器的页面数量,你可以分批次创建浏览器实例,比如每次创建5个浏览器,每个处理20个页面,处理完一批再关闭浏览器并启动下一批。这种方式的内存开销远低于100个独立容器。

额外内存优化技巧

  • 避免获取完整HTML:如果只需要页面中的特定数据,用page.evaluate(() => { /* 提取数据逻辑 */ })直接在页面中提取,不要获取整个HTML字符串,减少内存占用。
  • 定期清理内存:如果长时间运行,可以定期重启浏览器实例(比如每处理50个页面重启一次),避免内存泄漏。
  • 监控内存使用:用process.memoryUsage()实时监控Node.js进程的内存占用,动态调整并发数。

为什么Docker Swarm方案内存过高?

每个Docker容器运行一个完整的浏览器实例,100个实例就是100份浏览器的内存开销(150-170MB/个),总内存轻松突破15GB。而复用浏览器实例的方案,一个浏览器处理100个页面(控制并发),总内存通常能控制在2-5GB以内,资源利用率提升数倍。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:58:25