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

Node.js使用request和cheerio编写的爬虫执行中途卡住无报错如何解决?

问题原因
  • 无限制并发请求:脚本一次性发起大量HTTP请求,触发目标服务器限流规则,后续请求被挂起无响应,由于默认无超时配置,脚本会一直卡住不会抛出错误。
  • getData函数逻辑错误:所有请求共享同一个nitems数组存储结果,每个Promise都返回完整的nitems,最终处理后会出现数倍的冗余链接,进一步放大并发压力。
  • 缺少请求校验规则:仅判断了基础请求错误,未校验HTTP响应状态码(如429限流、403禁止访问等场景不会触发基础错误,代码会继续执行无效逻辑),也未设置超时时间,异常请求会无限等待。
  • 域名拼接错误:第二步、第三步要抓取的页面域名为egpostal.com,代码统一用了egyptcodebase.com的base链接拼接,会生成大量无效链接。
修复方案
  1. 封装通用请求方法,新增超时配置和HTTP状态码校验,异常请求直接抛出错误避免卡住。
  2. 修复getData逻辑,每个请求单独维护返回的链接数组,避免生成冗余重复链接。
  3. 增加并发请求控制,限制同时发起的请求数量,避免触发服务器限流。
  4. 修正不同域名的拼接规则,保证链接有效性。
修改后代码示例

首先需要安装并发控制依赖:npm install p-limit

const request = require('request');
const cheerio = require('cheerio');
// 限制并发数为5,可根据实际访问情况上下调整
const limit = require('p-limit')(5);

const firstPageLink = 'https://www.egyptcodebase.com/en/p/all';
const egyptBase = 'https://www.egyptcodebase.com/en/';
const egPostalBase = 'https://egpostal.com/en/';

// 通用请求封装,加超时和状态码校验
const fetchPage = (url) => {
  return new Promise((resolve, reject) => {
    request({
      url: url,
      timeout: 10000 // 10秒超时,可自行调整
    }, (error, response, html) => {
      if (error) return reject(new Error(`请求失败:${url},错误信息:${error.message}`));
      if (response.statusCode < 200 || response.statusCode >= 400) {
        return reject(new Error(`请求异常:${url},状态码:${response.statusCode}`));
      }
      resolve(cheerio.load(html));
    })
  })
}

// 抓取省份链接
const getProvinceLinks = async (link) => {
  const $ = await fetchPage(link);
  const items = [];
  $('.table tbody tr').each(function() {
    items.push(egyptBase + $(this).find("a[href]").attr("href"));
  });
  return items;
};

// 抓取办事处链接
const getOfficeLinks = async (provinceLinks) => {
  const promises = provinceLinks.map(url => limit(async () => {
    const $ = await fetchPage(url);
    const currentItems = [];
    $('.table tbody tr').each(function() {
      currentItems.push(egPostalBase + $(this).find("a[href]").attr("href"));
    });
    return currentItems;
  }))
  const result = await Promise.all(promises);
  return result.flat(1);
}

// 抓取详情页标题
const fetchTitles = async (officeLinks) => {
  const promises = officeLinks.map(url => limit(async () => {
    const $ = await fetchPage(url);
    const title = $(".home-title > h2").eq(0).text().trim();
    console.log({
      title: title,
      link: url
    });
    return title;
  }))
  return Promise.all(promises);
}

// 主函数
(async () => {
  try {
    const provinceLinks = await getProvinceLinks(firstPageLink);
    console.log(`共抓取到${provinceLinks.length}个省份链接`);
    const officeLinks = await getOfficeLinks(provinceLinks);
    console.log(`共抓取到${officeLinks.length}个办事处链接`);
    const titles = await fetchTitles(officeLinks);
    console.log('===== 抓取完成,所有标题如下 =====');
    titles.forEach(title => console.log(title));
  } catch (err) {
    console.error('执行出错:', err);
  }
})();

如果你不想安装额外依赖,可以用原生分批请求逻辑替换并发控制,每次处理固定数量的请求,等当前批次执行完成后再处理下一批即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 13:51:01