在Vercel部署Next.js项目时Puppeteer无法运行的解决方案咨询
解决Vercel上Next.js项目中Puppeteer缺少executablePath的问题
问题背景
Next.js项目部署到Vercel后,Puppeteer无法正常运行,报错提示缺少executablePath。尝试使用chrome-aws-lambda但因版本过旧,与当前环境(Next.js 13.4.4 + Puppeteer 20.5.0)不兼容,问题未解决。
相关代码
import puppeteer from 'puppeteer'; import * as cheerio from 'cheerio' export const searchPlayer = async (query , page) => { const url = `https://www.transfermarkt.com/schnellsuche/ergebnis/schnellsuche?ajax=yw0&query=${query}&Spieler_page=${page}&Trainer_page=${page}&Verein_page=${page}&Schiedsrichter_page=${page}` let nextPage = -1; try { const browser = await puppeteer.launch({ headless: 'new', }); const page = await browser.newPage(); await page.setCookie({ name: 'consentUUID', value: 'c819fae5-8610-400f-8769-41fea1b22bda_19', domain: 'www.transfermarkt.com', }); await page.goto(url, { waitUntil: 'domcontentloaded', }); await page.setUserAgent( "5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36" ); try { const paginationElement = await page.$('.tm-pagination__list-item--active + li'); if (paginationElement) { const nextNumber = await page.evaluate((element) => element.textContent, paginationElement); nextPage = nextNumber; } } catch (error) { nextPage = -1; } // Wait for the desired content to be rendered on the page const getTable = await page.waitForXPath('/html/body/div[2]/main/div[1]/div/div/div[1]/div/table/tbody/tr[1]', { timeout: 5000 }).catch(() => null); if (!getTable) { await browser.close(); return []; } const trElements = await page.$x('/html/body/div[2]/main/div[1]/div/div/div[1]/div/table/tbody/tr'); // const search = []; const tableRows = await Promise.all(trElements.map(async (tr) => { const rowData = await page.evaluate((row) => { const tds = Array.from(row.querySelectorAll('td')); const data = { href: tds[2]?.querySelector('a')?.href || '', name: tds[2]?.textContent || '', position: tds[4]?.textContent || '', club: tds[3]?.textContent || '', age: tds[6]?.textContent || '', marketValue: tds[8]?.textContent || '', agency: tds[9]?.textContent || '' }; return data; }, tr); return rowData; })); await browser.close(); return { "next": nextPage == "" ? "-1" : nextPage, "result": tableRows }; } catch (error) { console.error('Error:', error); return []; } };
原项目依赖
"dependencies": { "@emotion/react": "^11.11.0", "@emotion/styled": "^11.11.0", "@mui/icons-material": "^5.11.16", "@mui/lab": "^5.0.0-alpha.132", "@mui/material": "^5.13.3", "axios": "^1.4.0", "cheerio": "^1.0.0-rc.12", "eslint": "8.41.0", "eslint-config-next": "13.4.4", "http-proxy-middleware": "^2.0.6", "next": "13.4.4", "puppeteer": "^20.5.0", "react": "18.2.0", "react-dom": "18.2.0" },
解决方案
1. 替换Puppeteer包为puppeteer-core
puppeteer自带完整Chromium,但Vercel环境已预装兼容的Chrome,使用puppeteer-core更轻量,且能手动指定浏览器路径。
执行命令:
npm uninstall puppeteer && npm install puppeteer-core@20.5.0
修改后的依赖中,puppeteer替换为:
"puppeteer-core": "^20.5.0"
2. 适配Vercel环境的浏览器启动配置
Vercel提供process.env.CHROME_EXECUTABLE_PATH环境变量,直接指向预装的Chrome路径。同时添加无服务器环境必需的启动参数,避免沙箱权限问题。
修改代码中的puppeteer.launch配置:
const browser = await puppeteer.launch({ headless: 'new', executablePath: process.env.CHROME_EXECUTABLE_PATH || '/usr/bin/chromium-browser', // 兼容本地开发环境的 fallback args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-accelerated-2d-canvas', '--no-first-run', '--no-zygote', '--single-process' ] });
3. 调整UserAgent设置顺序
将setUserAgent移至page.goto之前,确保请求时正确携带UA:
await page.setUserAgent( "5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36" ); await page.goto(url, { waitUntil: 'domcontentloaded', });
4. 延长Vercel函数执行时间(可选)
Puppeteer操作页面耗时可能超过Vercel默认10秒限制,在项目根目录创建vercel.json配置:
{ "functions": { "app/api/**/*.js": { // 根据你的API路由路径调整 "maxDuration": 30 } } }
修改后的完整代码
import puppeteer from 'puppeteer-core'; import * as cheerio from 'cheerio' export const searchPlayer = async (query , page) => { const url = `https://www.transfermarkt.com/schnellsuche/ergebnis/schnellsuche?ajax=yw0&query=${query}&Spieler_page=${page}&Trainer_page=${page}&Verein_page=${page}&Schiedsrichter_page=${page}` let nextPage = -1; try { const browser = await puppeteer.launch({ headless: 'new', executablePath: process.env.CHROME_EXECUTABLE_PATH || '/usr/bin/chromium-browser', args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-accelerated-2d-canvas', '--no-first-run', '--no-zygote', '--single-process' ] }); const page = await browser.newPage(); await page.setCookie({ name: 'consentUUID', value: 'c819fae5-8610-400f-8769-41fea1b22bda_19', domain: 'www.transfermarkt.com', }); await page.setUserAgent( "5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36" ); await page.goto(url, { waitUntil: 'domcontentloaded', }); try { const paginationElement = await page.$('.tm-pagination__list-item--active + li'); if (paginationElement) { const nextNumber = await page.evaluate((element) => element.textContent, paginationElement); nextPage = nextNumber; } } catch (error) { nextPage = -1; } // Wait for the desired content to be rendered on the page const getTable = await page.waitForXPath('/html/body/div[2]/main/div[1]/div/div/div[1]/div/table/tbody/tr[1]', { timeout: 5000 }).catch(() => null); if (!getTable) { await browser.close(); return []; } const trElements = await page.$x('/html/body/div[2]/main/div[1]/div/div/div[1]/div/table/tbody/tr'); const tableRows = await Promise.all(trElements.map(async (tr) => { const rowData = await page.evaluate((row) => { const tds = Array.from(row.querySelectorAll('td')); const data = { href: tds[2]?.querySelector('a')?.href || '', name: tds[2]?.textContent || '', position: tds[4]?.textContent || '', club: tds[3]?.textContent || '', age: tds[6]?.textContent || '', marketValue: tds[8]?.textContent || '', agency: tds[9]?.textContent || '' }; return data; }, tr); return rowData; })); await browser.close(); return { "next": nextPage == "" ? "-1" : nextPage, "result": tableRows }; } catch (error) { console.error('Error:', error); return []; } };
内容的提问来源于stack exchange,提问作者danielmax123
相关产品推荐
相关产品推荐

