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做定时任务就行,轻量好上手:- 先安装依赖:
npm install node-schedule - 核心代码示例:
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
相关产品推荐
相关产品推荐

