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

API批量数据抓取先遇内存问题,现出现connect ETIMEDOUT错误求助

解决大规模API抓取的内存溢出与连接超时问题

看起来你在抓取60多万页的API数据时遇到了两个典型问题:内存耗尽和connect ETIMEDOUT,这都是因为一次性发起了过高并发的请求导致的。咱们一步步来解决:

问题根源分析

  • 内存问题:async.map默认会同时发起所有60多万个请求,每个请求的响应数据都会存在内存里,直到全部请求完成才写入文件,内存自然扛不住。
  • ETIMEDOUT错误:短时间内发起数万级请求,会耗尽本地TCP端口,同时目标服务器也会因为请求频率过高拒绝连接,导致超时。

针对性解决方案

1. 控制请求并发数

用async.mapLimit替代async.map,限制同时发起的请求数量(比如10-20,根据服务器承受能力调整),避免瞬间压垮服务器和本地资源:

const async = require('async');
const request = require('request');
const jsonfile = require('jsonfile');

const file = '/temp/data.json';
const urls = [];
for (let i = 1; i <= 608469; i++) {
  const url = `https://api.demo.com/v2.1/people?user_key=auth_key&page=${i}&sort_order=created_at%20DESC`;
  urls.push(url);
}

function httpGet(url, callback) {
  const options = { url, json: true };
  request(options, function(err, res, body) {
    // 针对超时错误增加一次重试
    if (err && err.code === 'ETIMEDOUT') {
      return request(options, callback);
    }
    callback(err, body);
  });
}

// 限制并发数为15,可根据实际情况调整
async.mapLimit(urls, 15, httpGet, function (err, res) {
  if (err) return console.error('抓取失败:', err);
  jsonfile.writeFileSync(file, res);
  console.log('数据已全部写入文件');
});

2. 优化内存占用:流式写入/分批写入

如果数据量极大,即使限制并发,全部存在内存里还是会爆,这时候可以边请求边写入文件,或者分批写入:

分批写入示例

const async = require('async');
const request = require('request');
const jsonfile = require('jsonfile');
const fs = require('fs');

const file = '/temp/data.json';
const urls = [];
const batchSize = 1000; // 每1000条数据写入一次
let allData = [];

for (let i = 1; i <= 608469; i++) {
  const url = `https://api.demo.com/v2.1/people?user_key=auth_key&page=${i}&sort_order=created_at%20DESC`;
  urls.push(url);
}

// 先清空文件并写入数组开头
fs.writeFileSync(file, '[');

function httpGet(url, callback) {
  const options = { url, json: true };
  request(options, function(err, res, body) {
    if (err && err.code === 'ETIMEDOUT') {
      return request(options, callback);
    }
    if (!err) {
      allData.push(body);
      // 达到批次大小就写入文件
      if (allData.length >= batchSize) {
        const dataStr = allData.map(item => JSON.stringify(item)).join(',');
        fs.appendFileSync(file, dataStr + ',');
        allData = []; // 清空当前批次释放内存
      }
    }
    callback(err, body);
  });
}

async.mapLimit(urls, 15, httpGet, function (err) {
  if (err) return console.error('抓取失败:', err);
  // 写入剩余未达批次的数据
  if (allData.length > 0) {
    const dataStr = allData.map(item => JSON.stringify(item)).join(',');
    fs.appendFileSync(file, dataStr);
  }
  // 写入数组结尾完成JSON格式
  fs.appendFileSync(file, ']');
  console.log('数据已全部写入文件');
});

3. 额外优化建议

  • 增强重试机制:可以用async.retry封装请求,针对超时、5xx等可恢复错误自动重试多次,进一步提高抓取成功率。
  • 清理无用依赖:你代码里同时引入了node-fetch和request但只用到了request,可以移除无用依赖减少冗余。
  • 监控内存状态:在代码中加入console.log(process.memoryUsage()),实时查看内存占用,方便调整并发数和批次大小。
  • 考虑API速率限制:如果目标API有明确的请求频率限制,可在请求间隔加入短暂延迟(比如结合setTimeout),避免触发限流。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:12:37