You need to enable JavaScript to run this app. Puppeteer批量爬取UPC关联网页时连续操作导致整台电脑系统冻结
优惠活动
大模型
产品
解决方案
定价
更多

Puppeteer批量爬取UPC关联网页时连续操作导致整台电脑系统冻结

问题根因
  • 代码中results.map(processRow)的写法会同步触发所有任务的Promise,没有任何等待逻辑、没有并发数限制,700个UPC对应的puppeteer.launch()会在极短时间内全部执行。每个独立的Chromium进程本身需要占用300-500MB内存,700个进程同时启动会直接把16GB内存全部占满,触发系统资源耗尽保护,导致整个系统无响应冻结。
  • 出现的MaxListenersExceededWarning是并发失控的附带表现:每次调用puppeteer.launch()时,Puppeteer内部会给Node进程的exit、SIGINT、SIGTERM、SIGHUP事件绑定资源清理的监听函数。正常浏览器退出后这些监听会被移除,但因为短时间内启动了上百个浏览器实例,绑定的监听数量超过了Node默认的10个上限,就会弹出对应警告。警告本身不会导致系统冻结,只是在提示进程的资源占用已经处于失控状态。
修复方案
  • 取消“每个UPC启动一个独立浏览器实例”的逻辑,全局仅启动1个Puppeteer浏览器实例,所有爬取任务复用这个实例,通过新开标签页的方式执行爬取,内存占用会降到原来的几十分之一。
  • 增加严格的并发数上限,杜绝一次性把所有700个任务全部触发的写法,根据机器内存调整同时运行的任务数,16GB内存的环境下同时跑3-5个爬取标签页完全不会造成内存压力。
  • 替换原来无等待、无错误捕获的results.map(processRow)调用,增加任务执行的完成等待、错误捕获机制,避免爬取任务异常退出后残留资源占用。
  • 给每个页面加载流程增加超时限制,避免目标网站响应慢导致任务一直卡住占着资源不释放。
修改后的参考实现
import puppeteer from 'puppeteer';
import parse from 'csv-parse';
import { promises as fs } from "fs";

// 全局单例浏览器,仅启动一次
let browser: puppeteer.Browser;
// 并发上限,16GB内存环境推荐设置为3
const MAX_CONCURRENT_TASKS = 3;

const parseCsv = async (filename: string): Promise<parse.Parser> => {
    let contents: Buffer = await fs.readFile(filename);
    return parse(contents);
}

const processRow = async (row: string[]) => {
    // 复用全局浏览器实例,不再重复启动Chromium
    const page = await browser.newPage();
    // 配置30秒页面加载超时,避免任务卡死
    await page.goto(`https://www.example.com/search/?q=${row[5]}`, {
        timeout: 30000,
        waitUntil: 'domcontentloaded'
    });
    // 爬取完成后立刻关闭标签页释放内存
    await page.close();
}

// 通用带并发限制的任务执行器
const runTasksWithConcurrency = async (taskList: (() => Promise<void>)[], limit: number) => {
    const activeTasks = new Set<Promise<void>>();
    for (const task of taskList) {
        const currentTask = task().then(() => activeTasks.delete(currentTask));
        activeTasks.add(currentTask);
        // 当正在执行的任务数达到上限时,等待任意一个任务完成再继续派发新任务
        if (activeTasks.size >= limit) {
            await Promise.race(activeTasks);
        }
    }
    // 等待所有剩余任务执行完成
    await Promise.all(activeTasks);
}

(async () => {
    console.log('Reading file');
    const parser = await parseCsv(process.argv[process.argv.length - 1]);

    const validRows: string[][] = [];
    console.log('Extracting data');
    for await (let row of parser) {
        if (/\d/.test(row[5])) {
            validRows.push(row);
        }
    }

    // 全流程仅启动一次浏览器实例
    browser = await puppeteer.launch({
        // 启用新版无头模式进一步降低内存占用
        headless: 'new'
    });

    try {
        console.log('Scraping');
        // 构造带行参数的任务列表,传入并发限制执行
        const taskList = validRows.map(row => () => processRow(row));
        await runTasksWithConcurrency(taskList, MAX_CONCURRENT_TASKS);
    } finally {
        // 无论任务成功或失败,强制关闭浏览器释放所有残留资源
        await browser.close();
    }
})();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.30 09:45:03