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

使用Puppeteer逐个爬取多URL并定时重复时的两类错误排查求助

嘿,我来帮你搞定这两个问题,咱们逐个拆解:

问题1:循环内函数引用外部变量的警告

这个警告是ESLint这类代码检查工具抛出的,核心原因是你在for循环内部重复定义了page.evaluate()里的箭头函数——虽然当前这个函数没直接引用循环变量,但循环里反复声明函数确实容易引发闭包语义混淆(比如后续如果不小心在函数里用到url这类外部变量,就可能出现意料之外的问题)。

修复方案:
把page.evaluate()里的数据提取逻辑抽成一个独立函数,放在循环外面,既避免了循环内重复声明函数,代码也更清晰:

// 抽离数据提取函数,放在循环外
const extractVideoData = () => {
  return [
    JSON.stringify(document.querySelector('#text > a')?.innerText || ''),
    JSON.stringify(document.querySelector('#container > h1')?.innerText || ''),
    JSON.stringify(document.querySelector('.view-count')?.innerText || ''),
    JSON.stringify(document.querySelector('#owner-sub-count')?.innerText || '')
  ];
};

之后在循环里直接调用这个函数即可:

const data = await page.evaluate(extractVideoData);

另外我还加了?.可选链操作符和空值兜底,避免因为元素找不到导致爬取中断。


问题2:Navigation timeout 导航超时错误

这个错误的常见原因有几个,咱们针对性解决:

  1. 重复等待导航:你先调用page.goto(url),又紧接着调用page.waitForNavigation()——这会导致代码等待两次导航,很容易超时。其实page.goto()本身支持通过waitUntil参数指定等待条件,不需要额外调用waitForNavigation。
  2. 反爬机制:YouTube会检测无头浏览器,可能限制页面加载,咱们可以设置模拟真实浏览器的User-Agent,降低被识别的概率。
  3. 超时时间不足:部分YouTube页面加载较慢,默认30秒超时不够用,可以延长超时时间。
  4. 单个URL错误影响全局:之前的代码如果某一个URL超时,会直接终止整个爬取任务,需要给单个URL添加独立的错误捕获。

修复方案:

  • 移除多余的page.waitForNavigation(),把等待条件整合到page.goto中;
  • 设置自定义User-Agent,模拟真实浏览器;
  • 延长page.goto和waitForSelector的超时时间;
  • 给单个URL添加错误捕获,确保一个页面报错不影响其他URL的爬取。

完整修复后的代码
const puppeteer = require("puppeteer");
const urls = [
  'https://www.youtube.com/watch?v=cw9FIeHbdB8',
  'https://www.youtube.com/watch?v=imy1px59abE',
  'https://www.youtube.com/watch?v=dQw4w9WgXcQ'
];

// 抽离数据提取函数,避免循环内声明函数的警告
const extractVideoData = () => {
  return [
    JSON.stringify(document.querySelector('#text > a')?.innerText || ''),
    JSON.stringify(document.querySelector('#container > h1')?.innerText || ''),
    JSON.stringify(document.querySelector('.view-count')?.innerText || ''),
    JSON.stringify(document.querySelector('#owner-sub-count')?.innerText || '')
  ];
};

const scrape = async() => {
  let browser, page;
  try {
    browser = await puppeteer.launch({ 
      headless: "new", // 使用新版无头模式,更接近真实浏览器
      args: ['--no-sandbox', '--disable-setuid-sandbox'] // 避免沙箱权限问题(根据运行环境调整)
    });
    page = await browser.newPage();
    
    // 设置模拟真实浏览器的User-Agent,规避反爬检测
    await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36');

    // 使用for...of循环,比传统for循环更简洁,也能避免闭包潜在问题
    for (const url of urls) { 
      try {
        // 整合导航等待逻辑,延长超时到60秒
        await page.goto(url, { 
          waitUntil: 'networkidle2', 
          timeout: 60000 
        });
        
        // 等待目标元素可见,超时设为60秒
        await page.waitForSelector('.view-count', { visible: true, timeout: 60000 });
        
        const data = await page.evaluate(extractVideoData);
        const [channel, title, views, subs] = data.map(item => JSON.parse(item));
        
        console.log({ channel, title, views, subs });
      } catch (urlErr) {
        // 单个URL报错不终止全局任务,打印错误后继续爬取下一个
        console.error(`爬取${url}失败:`, urlErr.message);
      }
    }
  } catch(err) {
    console.error('全局爬取错误:', err.message);
  } finally {
    if (browser) {
      await browser.close();
    }
    // 注意:setTimeout不需要await,它是异步非Promise API
    setTimeout(scrape, 60000); // 一轮爬取完成后1分钟启动下一轮
  }
};

scrape();

额外小提示
  • 新版Puppeteer推荐使用headless: "new",比旧版headless: true更接近真实浏览器,能降低被反爬的概率;
  • 如果还是遇到反爬限制,可以尝试添加更多模拟设置,比如设置viewport尺寸、启用Cookie等(Puppeteer默认已启用大部分常用设置);
  • 如果你需要更稳定的爬取,可以考虑给每个URL的爬取添加随机延迟,避免请求频率过高触发限制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 06:57:32