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

Debian 11下Puppeteer爬取Odds Portal时Chunk加载失败问题

问题背景

使用Puppeteer爬取Odds Portal赛事数据时,Windows 10开发环境完全正常,但部署到Debian 11服务器以无头Chromium运行时,约75%概率出现页面加载失败,伴随两类错误:

  1. 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'
}
  1. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 10:07:33