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

Node.js部分Promise返回undefined问题排查与优化咨询

解决Node.js + Axios + Cheerio爬取彩虹六号干员时部分operatorStats为undefined的问题

问题根源分析

出现部分operatorStats为undefined的核心原因有两个:

  1. 请求失败未处理:批量请求Ubisoft页面时,部分请求被反爬拦截或网络异常,进入scrapeSpecificOperatorStats的catch块,但该块仅打印错误未返回任何值,导致operatorStats为undefined。
  2. Cheerio集合处理异常:Cheerio的map方法返回的是Cheerio对象而非标准数组,Promise.all处理时可能出现异常,导致部分Promise未正确执行。
  3. (次要)部分干员页面的HTML结构可能存在差异,原选择器span.operator__header__roles无法匹配到元素,但这种情况会返回{specialty: ""}而非undefined。

具体解决方案

1. 修复catch块返回逻辑

确保请求失败时返回默认值,避免operatorStats为undefined:

const scrapeSpecificOperatorStats = async (link) => {
  try {
    // 添加浏览器请求头,降低被拦截概率
    const response = await axios.get(link, {
      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'
      }
    });
    const $ = cheerio.load(response.data);
    // 改用更通用的选择器,兼容不同标签的情况
    const specialty = $(".operator__header__roles").text().trim();
    return { specialty: specialty || "未获取到角色信息" };
  } catch (error) {
    console.error(`请求干员页面失败 ${link}:`, error.message);
    // 返回默认值,保证operatorStats不为undefined
    return { specialty: "请求失败" };
  }
};

2. 修正Cheerio集合转Promise数组的方式

将Cheerio集合转换为标准数组后再生成Promise,确保Promise.all能正确处理:

// 替换原statsPromises生成代码
const statsPromises = Array.from(statsElement).map(async (element) => {
  const $el = $(element);
  const name = $el.find("span").text().trim();
  const imageUrl = $el.find("img").attr("src");
  const linkUrl = "https://www.ubisoft.com" + $el.attr("href");
  const operatorStats = await scrapeSpecificOperatorStats(linkUrl);
  return { name, imageUrl, linkUrl, operatorStats };
});

3. 添加请求节流避免反爬

使用p-limit限制并发请求数,降低被网站拦截的概率:

  1. 安装依赖:
npm install p-limit
  1. 修改代码:
const pLimit = require('p-limit');
// 限制同时发起3个请求,可根据情况调整
const limit = pLimit(3);

// 生成带节流的Promise数组
const statsPromises = Array.from(statsElement).map(element => {
  return limit(async () => {
    const $el = $(element);
    const name = $el.find("span").text().trim();
    const imageUrl = $el.find("img").attr("src");
    const linkUrl = "https://www.ubisoft.com" + $el.attr("href");
    const operatorStats = await scrapeSpecificOperatorStats(linkUrl);
    return { name, imageUrl, linkUrl, operatorStats };
  });
});

完整修正后的代码

const axios = require("axios");
const cheerio = require("cheerio");
const pLimit = require('p-limit');

const url = "https://www.ubisoft.com/en-us/game/rainbow-six/siege/game-info/operators";
const limit = pLimit(3);

const scrapeSpecificOperatorStats = async (link) => {
  try {
    const response = await axios.get(link, {
      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'
      }
    });
    const $ = cheerio.load(response.data);
    const specialty = $(".operator__header__roles").text().trim();
    return { specialty: specialty || "未获取到角色信息" };
  } catch (error) {
    console.error(`请求干员页面失败 ${link}:`, error.message);
    return { specialty: "请求失败" };
  }
};

const scrapeOperatorStats = async (url) => {
  try {
    const response = await axios.get(url, {
      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'
      }
    });
    const $ = cheerio.load(response.data);
    const statsElement = $(".oplist__card");

    const statsPromises = Array.from(statsElement).map(element => {
      return limit(async () => {
        const $el = $(element);
        const name = $el.find("span").text().trim();
        const imageUrl = $el.find("img").attr("src");
        const linkUrl = "https://www.ubisoft.com" + $el.attr("href");
        const operatorStats = await scrapeSpecificOperatorStats(linkUrl);
        return { name, imageUrl, linkUrl, operatorStats };
      });
    });

    return Promise.all(statsPromises);
  } catch (error) {
    console.error("请求干员列表页面失败:", error.message);
  }
};

scrapeOperatorStats(url)
  .then((results) => console.log(results))
  .catch((error) => console.error(error));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 10:54:54