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

在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 13:04:56