Debian 11下Puppeteer爬取Odds Portal时Chunk加载失败问题
问题背景
使用Puppeteer爬取Odds Portal赛事数据时,Windows 10开发环境完全正常,但部署到Debian 11服务器以无头Chromium运行时,约75%概率出现页面加载失败,伴随两类错误:
- ChunkLoadError:加载JS chunk失败(涉及多个不同编号的chunk)
{ message: 'Loading chunk 4402 failed.\n' + '(error: https://www.oddsportal.com/res/public/js/build/4402.be189ee4615a6b8d.js)', name: 'ChunkLoadError', type: 'error', request: 'https://www.oddsportal.com/res/public/js/build/4402.be189ee4615a6b8d.js' }
- Axios 520错误:请求
/ajax-max-odds/all/返回520状态码
{ config: { url: '/ajax-max-odds/all/', method: 'get', headers: { Accept: 'application/json, text/plain, */*', 'X-Requested-With': 'XMLHttpRequest', 'X-XSRF-TOKEN': 'eyJpdiI6IkEyZFdweitieTdReGpIQ01Uc2ozZVE9PSIsInZhbHVlIjoiM2VqZEhqM0FES3B0czZ0K2ZZTCtJc1VIa XVFc2NqMWJsRXYwOEJyaDhDTnBlTy84SkltVnBaNW5sRDdvdlNZV2RnQnJSdVpodEFVNFgzOGhSeWpnMVRKdmtvaVlKRWJGZyswc0JQYW9EY01EVG U4YmMyODMxNDIxMDlmNGZiODJjOWViYTRjMDM2OTdhMTZjNmE4NGFhNGE1ZjkzZGZlYTI3N2VhIiwidGFnIjoiIn0=' }, transformRequest: [ {} ], transformResponse: [ {} ], timeout: 0, adapter: {}, xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN', maxContentLength: -1, maxBodyLength: -1, validateStatus: {}, transitional: { silentJSONParsing: true, forcedJSONParsing: true, clarifyTimeoutError: false } }, request: {}, response: { data: '', status: 520, statusText: '', headers: { 'accept-ranges': 'bytes', age: '0', date: 'Thu, 02 Nov 2023 21:35:34 GMT', server: 'nginx', 'x-cache': 'uncached', 'x-deliver': 'Thu, 02 Nov 2023 21:35:34 GMT' }, config: { url: '/ajax-max-odds/all/', method: 'get', headers: [Object], transformRequest: [Array], transformResponse: [Array], timeout: 0, adapter: {}, xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN', maxContentLength: -1, maxBodyLength: -1, validateStatus: {}, transitional: [Object] }, request: {} }, isAxiosError: true, toJSON: {} }
服务器仅支持无头模式,Windows下无头运行正常;更换Playwright后问题仍存在(频率稍低),复现代码如下:
const browser = await puppeteer.launch({ headless: 'new', args: [ '--no-sandbox', '--disable-setuid-sandbox', '--incognito', '--disable-web-security' ], defaultViewport: { width: 1480, height: 800 } }); const page = await browser.newPage(); await page.goto(url); await page.waitForTimeout(300000);
疑问解答
1. 为何Windows环境正常,Debian环境异常?
- IP特征差异:Windows本地IP多为家庭民用网络,不在反爬系统重点监控范围内;Debian服务器IP属于数据中心集群IP,这类IP被反爬系统标记为高风险爬虫来源,容易触发限流或拦截规则。
- 浏览器指纹差异:Debian上的无头Chromium与Windows无头浏览器在UA字符串、系统标识、渲染引擎特性等指纹信息上存在细微差异,目标网站可通过这些特征识别服务器端爬虫行为,进而限制资源加载。
- 网络稳定性差异:服务器网络路由、带宽稳定性通常弱于本地民用网络,易导致JS chunk或AJAX请求超时失败,而本地网络环境更稳定,请求成功率更高。
2. 刷新页面有时正常有时异常,原因是什么?
- 概率性反爬拦截:目标网站采用动态阈值反爬策略,实时分析IP的请求频率、行为特征,仅当触发阈值时才会拦截,未触发则允许访问,导致结果随机。
- 网络波动影响:服务器网络偶尔出现波动,导致部分资源请求超时;网络状态良好时则能正常加载资源。
- 会话随机性验证:目标网站为每个会话分配不同的验证规则,部分会话被判定为可疑,部分通过验证,从而出现时好时坏的情况。
3. 是否有解决办法?
方法1:优化浏览器指纹,模拟真实用户环境
修改无头浏览器启动参数,让其更接近真实Chrome浏览器:
const browser = await puppeteer.launch({ headless: 'new', args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-blink-features=AutomationControlled', // 禁用自动化标识 '--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"', // 匹配Windows UA '--enable-features=NetworkService', '--disable-web-security', '--incognito', '--start-maximized' ], defaultViewport: null, // 使用真实窗口大小 ignoreDefaultArgs: ['--enable-automation'], // 移除自动化提示参数 });
也可搭配puppeteer-extra及puppeteer-extra-plugin-stealth插件进一步隐藏爬虫特征。
方法2:增加请求重试与随机延迟
页面加载失败时自动重试,同时加入随机延迟模拟用户行为:
async function loadPageWithRetry(page, url, maxRetries = 3) { for (let i = 0; i < maxRetries; i++) { try { await page.goto(url, { waitUntil: 'networkidle2', timeout: 60000 }); // 校验页面是否加载成功(示例:检查目标元素是否存在) await page.waitForSelector('.target-element', { timeout: 10000 }); return true; } catch (e) { console.log(`加载失败,重试第${i+1}次`); await page.reload({ waitUntil: 'networkidle2' }); await page.waitForTimeout(Math.random() * 3000 + 2000); // 随机延迟2-5秒 } } return false; }
方法3:使用代理IP切换
采用住宅代理等高质量IP,避免服务器IP被反爬系统拦截:
const browser = await puppeteer.launch({ headless: 'new', args: [ '--no-sandbox', '--disable-setuid-sandbox', '--proxy-server=http://your-proxy-ip:port', // 配置代理 // 其他参数... ], });
方法4:拦截非必要资源请求
禁用图片、CSS等非核心资源加载,减少请求数量,降低被拦截概率:
await page.setRequestInterception(true); page.on('request', (req) => { if (req.resourceType() === 'image' || req.resourceType() === 'stylesheet') { req.abort(); } else { req.continue(); } });
内容的提问来源于stack exchange,提问作者JackyLaMitaine
相关产品推荐
相关产品推荐

