无需 KYC —— 无需身份证、自拍、文件证明。支持加密货币支付:BTC, USDT, USDC, ETH, SOL, LTC
语言: 简体中文
开始使用

文档 · Node.js 22+ · ES 模块

Node.js 中使用旋转住宅代理:undici、axios、got 和 SOCKS5

Node 内置 fetch 默认忽略代理设置,除非传入调度器。这些方法库分别适用,加上无依赖的单主机限制器。

最后更新:

快速回答

如何在 Node.js 里使用旋转代理?

使用 fetch 时创建 undici ProxyAgent 用于 http://gw.crawlrelay.com:7777 并作为 dispatcher 传入。axios 或 got 传入代理代理如 https-proxy-agent 或 hpagent,并在 axios 里设置 proxy: false。CrawlRelay 住宅代理通过用户名读取国家和会话,来自 US$0.50/GB。支持加密货币支付,无需 KYC。

Node.js 配置速览

fetch / undicinew ProxyAgent({ uri, token }),然后 fetch(url, { dispatcher })
axioshttpsAgent: new HttpsProxyAgent(url) 和 proxy: false
got从 hpagent 获取 agent: { https: new HttpsProxyAgent({ proxy }) }
SOCKS5使用端口 7778 的 socks5h:// URL 获取 socks-proxy-agent
每次请求使用新 IP每个请求使用新的代理或新的会话 ID;池化连接保持其出口
模块格式示例为 ES 模块(.mjs 或 "type": "module"),支持顶层 await

如何在 Node 的 fetch 中使用代理?

Node 的全局 fetch 基于 undici,但默认不支持读取 HTTP_PROXY 或 HTTPS_PROXY。安装 undici 并传入 ProxyAgent 作为调度器。token 字段通过 Proxy-Authorization 头部发送凭据,因此密码不会出现在可能被记录的 URL 中。

undici ProxyAgent 配合 fetch
import { ProxyAgent, fetch } from "undici";

const credentials = Buffer.from("USERNAME-country-us:PASSWORD").toString("base64");
const dispatcher = new ProxyAgent({
  uri: "http://gw.crawlrelay.com:7777",
  token: `Basic ${credentials}`,
});

const res = await fetch("https://api.ipify.org?format=json", {
  dispatcher,
  signal: AbortSignal.timeout(30000),
});
console.log(res.status, await res.json());
await dispatcher.close();

如何在 axios 中使用代理?

不要依赖 axios 自带的 proxy 选项用于 HTTPS 站点:它会直接发送请求到代理而不建立 CONNECT 隧道,很多代理会拒绝。需禁用该选项(proxy: false),并传入一个可以打开正确隧道的代理。

axios + https-proxy-agent
import axios from "axios";
import { HttpsProxyAgent } from "https-proxy-agent";

const agent = new HttpsProxyAgent("http://USERNAME-country-de:[email protected]:7777");
const client = axios.create({ httpsAgent: agent, httpAgent: agent, proxy: false, timeout: 30000 });

const { data } = await client.get("https://api.ipify.org?format=json");
console.log(data.ip);

如何在 got 中使用代理?

got 每种协议只接受一个代理。hpagent 遵循 Node 的 http.Agent:默认关闭 keep-alive,除非传入 keepAlive: true。想让每个请求使用新连接,也就是通常用新出口时,关闭它或按下面设置的 keepAlive: false。

got + hpagent
import got from "got";
import { HttpsProxyAgent } from "hpagent";

const proxy = "http://USERNAME-country-fr:[email protected]:7777";
const body = await got("https://api.ipify.org", {
  agent: { https: new HttpsProxyAgent({ proxy, keepAlive: false }) },
  timeout: { request: 30000 },
  retry: { limit: 2, statusCodes: [429, 502, 503, 504] },
}).text();
console.log(body);

如何在 Node.js 中使用 SOCKS5?

使用带有 socks5h:// 协议的 socks-proxy-agent ,这样主机名会在出口解析。它兼容 Node 的 http 和 https 模块,也支持接受代理的任何库。

socks-proxy-agent 配合 node:https
import https from "node:https";
import { SocksProxyAgent } from "socks-proxy-agent";

const agent = new SocksProxyAgent("socks5h://USERNAME-country-gb:[email protected]:7778");

const req = https.get("https://api.ipify.org", { agent, timeout: 30000 }, (res) => {
  let body = "";
  res.on("data", (chunk) => (body += chunk));
  res.on("end", () => console.log(res.statusCode, body));
});
req.on("timeout", () => req.destroy(new Error("timeout after 30 s")));
req.on("error", (err) => console.error(err.code, err.message));

如何在不借助库的情况下限制每个主机的并发数?

Promise.all 在 10,000 个 URL 上会同时打开 10,000 个请求。对每个主机设置小并发限制可以让每个网站保持合理负载,同时整个爬取任务仍是并行的。以下版本无依赖,兼容任意客户端。

每主机并发限制 + undici
import { ProxyAgent, fetch } from "undici";

const PER_HOST = 4;
const active = new Map();
const waiting = new Map();
const dispatcher = new ProxyAgent("http://USERNAME-country-us:[email protected]:7777");

async function withHostSlot(url, task) {
  const host = new URL(url).hostname;
  while ((active.get(host) ?? 0) >= PER_HOST) {
    await new Promise((resolve) => {
      if (!waiting.has(host)) waiting.set(host, []);
      waiting.get(host).push(resolve);
    });
  }
  active.set(host, (active.get(host) ?? 0) + 1);
  try {
    return await task();
  } finally {
    active.set(host, active.get(host) - 1);
    waiting.get(host)?.shift()?.();
  }
}

const urls = ["https://example.com/", "https://example.org/", "https://example.net/"];
const results = await Promise.all(
  urls.map((url) =>
    withHostSlot(url, async () => {
      const res = await fetch(url, { dispatcher, signal: AbortSignal.timeout(30000) });
      return [url, res.status, (await res.arrayBuffer()).byteLength];
    }).catch((err) => [url, err.name, 0])
  )
);
console.table(results);
await dispatcher.close();

为什么我会遇到重复 IP 或 ECONNRESET?

代理池化连接,连接保持其打开时的出口。要实现每次请求新 IP,需要每次请求创建新代理并在用后关闭,或者在代理中关闭 keep-alive,或者在每个请求的用户名中放入唯一-session-<id>。

ECONNRESET 和 socket hang up 通常来自重用已被远端关闭的池化连接,或出口掉线。GET 请求遇到此类错误时,重试一次新连接。连接重置页面 有详细介绍。

Node.js 与代理:常见问题

Node.js 的 fetch 支持 HTTP_PROXY 吗?

当前大多数项目常用版本默认不支持:全局 fetch 会忽略代理环境变量,除非传入调度器。使用上述的 undici ProxyAgent,或者如果希望进程内每个请求都通过代理,则用 undici 的 setGlobalDispatcher() 设置全局调度器。

为什么 axios 代理 HTTPS 站点时会失败?

axios 内建的 proxy 选项把 HTTPS 请求以许多代理拒绝的方式发送给代理,未建立 CONNECT 隧道。需设置 proxy: false 并传入 HttpsProxyAgent 作为 httpsAgent。请求才会通过正确隧道,TLS 保持在你的代码和目标站点之间。

如何用一个 Node.js 进程发送多个国家的请求?

为每个国家创建一个代理,每个代理使用不同用户名如 USERNAME-country-de 或 USERNAME-country-jp,每个请求选用不同代理。代理成本低,分开存储连接池,从而分开出口。

在 Node.js 中我该设置哪个超时?

务必设置超时:fetch 用 AbortSignal.timeout(30000),axios 用 timeout: 30000,got 用 timeout: { request: 30000 }。HTML 页面用 30 秒足够;慢目标和移动出口可达 60 秒,且建议遇超时在新连接上重试一次,而非等待更久。

连接你的 Node.js 爬虫

US$5.94 适用于 10 GB,US$0.50/GB 来自 50 GB。

从 US$0.50/GB 开始 · 无需 KYC

开始使用