Next.js 13本地可爬取网页,部署到Vercel后抓取失败求助
问题分析与解决方案
核心问题排查
JSON解析错误根源
你的catch块中返回NextResponse.json(console.error(error)),但console.error()返回的是undefined,这会导致接口返回无效JSON(甚至空内容),客户端解析时就会抛出SyntaxError: Unexpected token 'A'这类错误。Vercel部署后的反爬拦截
Costco有反爬机制,Vercel的服务器IP大概率被识别为爬虫,导致fetch请求超时(对应504网关超时)或直接被拒绝(500内部错误),这是本地正常但部署失败的主要原因。路径参数处理不严谨
用req.url.split("scrape_costco/")[1]获取商品编号的方式不可靠,部署后Vercel的URL结构可能带有额外路径或查询参数,导致拆分失败。
分步修复方案
1. 修复错误返回逻辑
把catch块的代码改成正确返回结构化错误信息:
catch (error) { console.error(error); return NextResponse.json( { error: error.message || "Internal Server Error" }, { status: error.message === "Product not found" ? 404 : 500 } ); }
这样客户端能收到标准JSON格式的错误信息,不会出现解析失败。
2. 绕过Costco反爬机制
给fetch请求添加浏览器请求头,模拟正常用户访问:
const res = await fetch( `https://www.costco.com/CatalogSearch?dept=All&keyword=${itemNumber}`, { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Referer": "https://www.costco.com/", }, // 可选:设置缓存策略,避免重复请求触发反爬 cache: "no-store" } );
如果添加头后还是被拦截,可以尝试:
- 启用Vercel的Edge Functions(比Serverless Functions更难被识别)
- 使用第三方代理服务转发请求(注意合规性)
3. 规范路径参数获取
改用Next.js 13 App Router的动态路由,创建app/api/scrape_costco/[itemNumber]/route.js文件,直接从params获取商品编号:
import { NextResponse } from "next/server"; import { JSDOM } from "jsdom"; export async function GET(req, { params }) { const { itemNumber } = params; console.log("Item #: ", itemNumber); try { console.log("scraping costco"); const res = await fetch( `https://www.costco.com/CatalogSearch?dept=All&keyword=${itemNumber}`, { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Referer": "https://www.costco.com/", }, cache: "no-store" } ); if (!res.ok) throw new Error(`Costco request failed: ${res.status}`); const html = await res.text(); const dom = new JSDOM(html); const document = dom.window.document; const productName = document.querySelector(".description a")?.textContent; if (!productName) throw new Error("Product not found"); return NextResponse.json(productName.trim()); } catch (error) { console.error(error); return NextResponse.json( { error: error.message || "Internal Server Error" }, { status: error.message === "Product not found" ? 404 : 500 } ); } }
这种方式更可靠,避免URL拆分带来的潜在问题。
4. 额外检查项
- 确认
jsdom已添加到package.json的依赖中(不是devDependencies),部署时Vercel会安装生产依赖 - 查看Vercel函数日志(Dashboard > Functions > 对应函数),获取更详细的错误信息,比如
fetch的具体错误原因
内容的提问来源于stack exchange,提问作者Martin Vera
相关产品推荐
相关产品推荐

