Node.js体育比分爬虫本地正常,部署Heroku后报错求助
Heroku部署Puppeteer体育爬虫报错:"Cannot read property 'close' of undefined"
我来帮你搞定这个问题!你的错误主要来自两个地方:catch块中未判断browser是否初始化就调用close(),以及Heroku环境下Puppeteer的启动配置不兼容。
错误原因分析
- 空值调用问题:当
puppeteer.launch()在Heroku上失败时(比如缺少浏览器依赖),browser变量还没被赋值就进入了catch块,这时候执行await browser.close()自然会抛出"Cannot read property 'close' of undefined"的错误。 - Heroku环境差异:本地环境已经预装了Chromium/Chrome的完整依赖,但Heroku是极简容器环境,默认没有这些组件,导致
launch()直接失败,触发了catch块的错误逻辑。
解决方案
1. 修复catch块的空值判断
首先修改scraper.js的catch逻辑,确保只有browser存在时才调用close(),同时把错误抛出去方便上层调试:
const puppeteer = require('puppeteer'); const scrapeLiveScores = async () => { let browser; // 改用let,方便后续判断 try{ browser = await puppeteer.launch({ headless: true, // 先加上Heroku需要的启动参数 args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-gpu', '--window-size=1920x1080' ] }); const page = await browser.newPage(); await page.goto('https://www.flashscore.com.au/basketball/usa/nba/'); await page.waitForSelector('div.event__score.event__score--home'); const scores = await page.evaluate(() => { // 你的原有evaluate逻辑保持不变 var basePath = 'div.leagues--live > div > div.event__match'; var homeScore = document.querySelectorAll('div.leagues--live > div > div.event__match > div.event__score--home'); var awayScore = document.querySelectorAll('div.leagues--live > div > div.event__match > div.event__score--away'); var homeTeam = document.querySelectorAll('div.leagues--live > div > div.event__match > div.event__participant.event__participant--home'); var awayTeam = document.querySelectorAll('div.leagues--live > div > div.event__match > div.event__participant.event__participant--away'); var stages = document.querySelectorAll('div.leagues--live > div > div.event__match > div.event__stage > div'); var scoresArray = []; for(var i = 0; i < homeScore.length; i++){ if(stages[i] != null){ scoresArray[i] = { homeTeam: homeTeam[i].innerText.trim(), homeScore: homeScore[i].innerText.trim(), awayTeam: awayTeam[i].innerText.trim(), awayScore: awayScore[i].innerText.trim(), stage: stages[i].innerText.trim() }; }else{ scoresArray[i] = { homeTeam: homeTeam[i].innerText.trim(), homeScore: homeScore[i].innerText.trim(), awayTeam: awayTeam[i].innerText.trim(), awayScore: awayScore[i].innerText.trim(), stage: "-" }; } } return scoresArray; }); console.log(scores); await browser.close(); return scores; }catch(err){ // 先判断browser是否存在再关闭 if (browser) { await browser.close(); } // 抛出错误,让上层能捕获到具体问题 throw err; } } module.exports.scrapeLiveScores = scrapeLiveScores;
2. 配置Heroku的Puppeteer Buildpack
Heroku默认没有Puppeteer需要的浏览器依赖,需要添加专门的buildpack来安装:
- 打开Heroku控制台,进入你的应用 → Settings → Buildpacks
- 点击
Add buildpack,输入https://github.com/jontewks/puppeteer-heroku-buildpack.git并保存 - 确保
heroku/nodejsbuildpack也在列表中(如果没有就添加)
3. 优化index.js的Promise逻辑(可选但推荐)
你的index.js里的Promise写法有点冗余,改成async/await更简洁,还能捕获具体错误:
const express = require('express'); const scraper = require('./util/scraper'); const app = express(); app.get('/scores', async (req, res) => { try { const data = await scraper.scrapeLiveScores(); res.json(data); // 用res.json自动处理JSON序列化 } catch (err) { console.error("Scores scrape failed:", err); res.status(500).send("Failed to fetch live scores"); } }); app.set( 'port', ( process.env.PORT || 5000 )); app.listen( app.get( 'port' ), function() { console.log( 'Node server is running on port ' + app.get( 'port' )); }); module.exports = app;
为什么本地运行正常?
本地环境通常已经安装了Chromium/Chrome浏览器,Puppeteer可以直接找到依赖启动;而Heroku是轻量级容器,没有预装这些组件,必须通过buildpack补充,同时需要关闭sandbox等安全限制才能在容器中运行。
内容的提问来源于stack exchange,提问作者hhhenry54
相关产品推荐
相关产品推荐

