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

Puppeteer定时爬取实现及Heroku部署API运行异常求助

Puppeteer部署与定时爬取问题解决方案

Heroku部署后API一分钟停止的排查与修复

Heroku上跑Puppeteer最容易踩资源和进程退出的坑,给你几个具体的修复方向:

  • 内存不足被强制杀进程:Heroku免费/基础层只有512MB内存,Chromium默认启动参数太吃资源,必须加优化参数压缩内存占用:
    const browser = await puppeteer.launch({
      args: [
        '--no-sandbox',
        '--disable-setuid-sandbox',
        '--disable-dev-shm-usage', // 解决容器内临时空间不足问题
        '--disable-accelerated-2d-canvas',
        '--no-first-run',
        '--no-zygote',
        '--single-process',
        '--disable-gpu'
      ],
      headless: 'new' // 新版无头模式比旧版轻量太多
    });
    
  • 进程未正常退出:就算关闭了页面和标签,也要确保browser.close()被执行,用try/finally包裹避免报错时跳过关闭操作:
    let browser;
    try {
      browser = await puppeteer.launch(/* 上述优化参数 */);
      const page = await browser.newPage();
      // 你的爬取逻辑
    } catch (err) {
      console.error('爬取出错:', err);
    } finally {
      if (browser) await browser.close();
    }
    
  • 看日志找根因:用heroku logs --tail查看实时日志,明确是内存溢出、超时还是其他错误,比瞎猜有用多了。

定时爬取实现(无需数据库,实时返回最新数据)

既然不想存数据库,那就定期爬取后把数据存在内存缓存里,API请求直接返回缓存的最新内容:

  • 用node-schedule做定时任务就行,轻量好上手:
    1. 先安装依赖:npm install node-schedule
    2. 核心代码示例:
      const schedule = require('node-schedule');
      let latestTableData = null; // 内存缓存最新表格数据
      
      // 封装爬取函数
      async function crawlTable() {
        let browser;
        try {
          browser = await puppeteer.launch(/* 优化参数 */);
          const page = await browser.newPage();
          await page.goto('你的目标网页', { waitUntil: 'networkidle2' }); // 等页面加载稳定
      
          // 提取表格数据,根据你的页面结构调整
          latestTableData = await page.evaluate(() => {
            const rows = Array.from(document.querySelectorAll('table tr'));
            return rows.slice(1).map(row => {
              const cols = row.querySelectorAll('td');
              return {
                // 按你的表格列定义字段
                name: cols[0].textContent.trim(),
                value: cols[1].textContent.trim()
              };
            });
          });
        } catch (err) {
          console.error('定时爬取失败:', err);
        } finally {
          if (browser) await browser.close();
        }
      }
      
      // 设置定时规则:比如每5分钟爬一次(可自行调整)
      schedule.scheduleJob('*/5 * * * *', async () => {
        console.log('开始定时更新数据...');
        await crawlTable();
      });
      
      // API接口直接返回缓存数据
      app.get('/api/table-data', (req, res) => {
        if (!latestTableData) {
          return res.status(503).json({ msg: '数据还没加载好,稍等几秒试试' });
        }
        res.json(latestTableData);
      });
      
      // 启动服务前先爬一次初始化数据
      crawlTable().then(() => {
        app.listen(process.env.PORT || 3000, () => {
          console.log('API服务启动成功');
        });
      });
      
  • 注意:Heroku免费层30分钟没请求会休眠,定时任务也会停。如果要24小时运行,要么升级到付费层,要么用外部工具每隔20分钟调用一次你的API,把容器唤醒。

puppeteer-clustering无法启动的替代方案

这个库对环境要求太高,Heroku容器里容易出问题。如果只是要实现并发爬取,自己用Promise.all控制并发数就行,简单还靠谱:

// 控制同时爬2个页面,避免资源耗尽
const targetUrls = ['url1', 'url2', 'url3'];
const maxConcurrency = 2;

async function batchCrawl() {
  const results = [];
  for (let i = 0; i < targetUrls.length; i += maxConcurrency) {
    const batch = targetUrls.slice(i, i + maxConcurrency);
    const batchResults = await Promise.all(
      batch.map(async url => {
        let browser;
        try {
          browser = await puppeteer.launch(/* 优化参数 */);
          const page = await browser.newPage();
          await page.goto(url);
          // 处理当前页面的数据
          return { url, data: /* 爬取结果 */ };
        } finally {
          if (browser) await browser.close();
        }
      })
    );
    results.push(...batchResults);
  }
  return results;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 04:47:28