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

Node.js中Puppeteer导航超时错误求助及代码排查

Puppeteer爬取导航超时问题解决

问题分析

出现TimeoutError: Navigation timeout of 30000 ms exceeded通常是因为页面加载耗时超过默认30秒限制,可能是目标网站资源加载量大、存在反爬限制,或是代码中的导航/等待逻辑不够健壮导致。

解决方案

1. 调整导航超时时间

在page.goto时显式设置更长的超时时间,或临时禁用超时(仅应急使用):

// 设置60秒超时
await page.goto(url, { timeout: 60000 });
// 禁用超时(不推荐长期使用)
await page.goto(url, { timeout: 0 });

2. 拦截非必要资源加载

禁止加载图片、样式、字体等非核心资源,大幅减少页面加载时间:

await page.setRequestInterception(true);
page.on('request', (req) => {
  const blockedTypes = ['image', 'stylesheet', 'font'];
  if (blockedTypes.includes(req.resourceType())) {
    req.abort();
  } else {
    req.continue();
  }
});

3. 优化等待逻辑

  • 给waitForSelector设置合理超时,避免无限等待
  • 导航时指定waitUntil: 'domcontentloaded',确保核心DOM加载完成即可:
// 等待目标元素,设置30秒超时
await page.waitForSelector('div#model-brief-specifications', { timeout: 30000 });
// 导航时等待DOM加载完成
await page.goto(deviceLink, { waitUntil: 'domcontentloaded' });

4. 增加导航重试机制

对超时的页面进行重试,避免单次失败导致程序中断:

async function gotoWithRetry(page, url, maxRetries = 3) {
  let retries = 0;
  while (retries < maxRetries) {
    try {
      await page.goto(url, { timeout: 60000, waitUntil: 'domcontentloaded' });
      return;
    } catch (err) {
      retries++;
      console.log(`导航失败,重试第${retries}次: ${url}`);
      if (retries === maxRetries) throw err;
      await new Promise(resolve => setTimeout(resolve, 2000)); // 重试前等待2秒
    }
  }
}

5. 修复CSV写入的异步问题

原代码中csvWriter.writeRecords是异步操作但未等待,可能导致数据写入混乱,需用await确保完成:

// 无需手动写表头,csv-writer会自动处理
await csvWriter.writeRecords([deviceDetails]);
console.log('数据已追加到CSV');

注意:初始化createCsvWriter时,append: false会自动写入表头;后续运行改为append: true可避免覆盖已有数据。

6. 优化元素提取逻辑

原代码多次调用page.evaluate遍历DOM,效率低下,改为一次evaluate提取所有数据:

const deviceDetails = await page.evaluate((keysToExtract) => {
  const details = {};
  const specItems = document.querySelectorAll('div#model-brief-specifications b');
  specItems.forEach(item => {
    const key = item.textContent.trim();
    if (keysToExtract.includes(key)) {
      details[key] = item.nextSibling?.textContent.trim() || '';
    }
  });
  // 给未找到的字段设置空字符串
  keysToExtract.forEach(key => {
    if (!details.hasOwnProperty(key)) {
      details[key] = '';
    }
  });
  return details;
}, keysToExtract);

修正后的完整代码

const puppeteer = require('puppeteer');
const createCsvWriter = require('csv-writer').createObjectCsvWriter;

(async () => {
  const keysToExtract = [
    'Dimensions', 'Weight', 'SoC', 'CPU', 'Cores', 'GPU', 'RAM',
    'Storage', 'Memory cards', 'Display', 'Battery', 'OS', 'Camera',
    'SIM card', 'Wi-Fi', 'USB', 'Bluetooth', 'Positioning'
  ];

  const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox'] });
  const page = await browser.newPage();
  
  // 使用普通浏览器UA,避免被反爬识别
  const userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36';
  await page.setUserAgent(userAgent);

  // 拦截非必要资源
  await page.setRequestInterception(true);
  page.on('request', (req) => {
    const blockedTypes = ['image', 'stylesheet', 'font'];
    if (blockedTypes.includes(req.resourceType())) {
      req.abort();
    } else {
      req.continue();
    }
  });

  // 带重试的导航函数
  async function gotoWithRetry(page, url, maxRetries = 3) {
    let retries = 0;
    while (retries < maxRetries) {
      try {
        await page.goto(url, { timeout: 60000, waitUntil: 'domcontentloaded' });
        console.log('成功导航到:', url);
        return;
      } catch (err) {
        retries++;
        console.log(`导航超时,重试第${retries}次: ${url}`);
        if (retries === maxRetries) {
          console.log(`多次导航失败,跳过: ${url}`);
          throw err;
        }
        await new Promise(resolve => setTimeout(resolve, 2000));
      }
    }
  }

  // 初始化CSV写入器
  const csvWriter = createCsvWriter({
    path: 'device_details.csv',
    header: keysToExtract.map(key => ({ id: key, title: key })),
    append: false // 首次运行自动写表头,后续改为true避免覆盖
  });

  try {
    // 导航到品牌列表页
    await gotoWithRetry(page, 'https://www.devicespecifications.com/en/brand-more');
    const brandLinks = await page.$$eval('.brand-listing-container-news a', links => links.map(a => a.href));

    for (const brandLink of brandLinks) {
      try {
        await gotoWithRetry(page, brandLink);
        const deviceLinks = await page.$$eval('div.model-listing-container-80 h3 a', links => links.map(a => a.href));
        
        for (const deviceLink of deviceLinks) {
          try {
            await gotoWithRetry(page, deviceLink);
            // 等待规格容器加载
            await page.waitForSelector('div#model-brief-specifications', { timeout: 30000 });

            // 一次性提取所有设备信息
            const deviceDetails = await page.evaluate((keys) => {
              const details = {};
              const specItems = document.querySelectorAll('div#model-brief-specifications b');
              specItems.forEach(item => {
                const key = item.textContent.trim();
                if (keys.includes(key)) {
                  details[key] = item.nextSibling?.textContent.trim() || '';
                }
              });
              keys.forEach(key => {
                if (!details[key]) details[key] = '';
              });
              return details;
            }, keysToExtract);

            console.log('提取到设备信息:', deviceDetails);
            // 写入CSV
            await csvWriter.writeRecords([deviceDetails]);
            console.log('数据已写入CSV');

            // 增加请求间隔,避免触发反爬
            await new Promise(resolve => setTimeout(resolve, 1000));
          } catch (err) {
            console.log('处理设备页面失败:', deviceLink, err.message);
            continue;
          }
        }
      } catch (err) {
        console.log('处理品牌页面失败:', brandLink, err.message);
        continue;
      }
    }
  } catch (err) {
    console.log('主流程出错:', err.message);
  } finally {
    await browser.close();
    console.log('浏览器已关闭');
  }
})();

额外建议

  • 避免使用Googlebot的User-Agent,容易被反爬系统识别
  • 控制请求频率,增加页面跳转间隔,降低被封禁风险
  • 可尝试使用多页面实例并行处理,但需注意并发量不要过高

内容的提问来源于stack exchange,提问作者Lak

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 19:50:19