Resposta rápida
Como usar proxy rotativo em Node.js?
Com fetch, crie um undici ProxyAgent para http://gw.crawlrelay.com:7777 e passe como dispatcher. Com axios ou got, passe um agente proxy como https-proxy-agent ou hpagent, e defina proxy: false no axios. CrawlRelay proxies residenciais leem país e sessão do nome de usuário, a partir de US$ 0,50/GB. Pago em cripto, sem KYC.
Resumo da configuração Node.js
| fetch / undici | new ProxyAgent({ uri, token }), depois fetch(url, { dispatcher }) |
|---|---|
| axios | httpsAgent: new HttpsProxyAgent(url) e proxy: false |
| got | agent: { https: new HttpsProxyAgent({ proxy }) } do hpagent |
| SOCKS5 | socks-proxy-agent com uma URL socks5h:// na porta 7778 |
| IP novo a cada requisição | Um agente novo ou um novo ID de sessão a cada requisição; conexões agrupadas mantêm sua saída |
| Formato de módulo | Exemplos são módulos ES (.mjs ou "type": "module") com await no nível superior |
Como uso um proxy com fetch do Node?
O fetch global do Node é baseado no undici mas não lê HTTP_PROXY ou HTTPS_PROXY por padrão. Instale undici e passe um ProxyAgent como dispatcher. O campo token envia as credenciais como um cabeçalho Proxy-Authorization, assim a senha nunca aparece numa URL que pode ser logada.
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();Como uso um proxy com axios?
Não confie na opção proxy do axios para sites HTTPS: ele envia a requisição ao proxy sem túnel CONNECT, que muitos proxies rejeitam. Desative isso com proxy: false e passe um agente que abra um túnel correto.
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);Como uso um proxy com got?
got usa um agente por protocolo. O hpagent segue o http.Agent do Node: keep-alive é desligado a menos que você passe keepAlive: true. Deixe desligado ou configure keepAlive: false como abaixo quando quiser cada requisição numa conexão nova, e assim normalmente numa saída nova.
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);Como uso SOCKS5 no Node.js?
Use socks-proxy-agent com o esquema socks5h:// para que o hostname seja resolvido na saída. Funciona com os módulos http e https do Node e com qualquer biblioteca que aceite um agente.
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));Como limito concorrência por host sem biblioteca?
Promise.all em mais de 10.000 URLs abre 10.000 requisições ao mesmo tempo. Um limitador pequeno por hostname mantém cada site num nível educado enquanto o crawl roda em paralelo. A versão abaixo não tem dependências e funciona com qualquer cliente acima.
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();Por que recebo o mesmo IP duas vezes, ou ECONNRESET?
Agentes agrupam conexões, e uma conexão agrupada mantém a saída com que foi aberta. Para um IP novo a cada requisição, crie um agente novo por requisição e feche depois, desligue keep-alive no agente, ou coloque um -session-<id> único no nome de usuário de cada requisição.
ECONNRESET e socket hang up geralmente vêm de reutilizar um socket agrupado que o outro lado já fechou, ou de uma saída que caiu. Refaça requisições GET uma vez numa conexão nova. A página de reset de conexão cobre os detalhes.
Node.js e proxies: perguntas
O fetch do Node.js suporta HTTP_PROXY?
Não por padrão nas versões que a maioria dos projetos usa hoje: o fetch global ignora variáveis de ambiente de proxy a menos que você passe um dispatcher. Use um undici ProxyAgent como mostrado acima, ou configure um dispatcher global com o setGlobalDispatcher() do undici se todas as requisições do processo devem usar o proxy.
Por que o axios falha com sites HTTPS atrás de um proxy?
A opção proxy embutida do axios envia requisições HTTPS ao proxy numa forma que muitos proxies recusam, em vez de abrir um túnel CONNECT. Configure proxy: false e passe um HttpsProxyAgent como httpsAgent. A requisição então passa por um túnel correto, e TLS fica entre seu código e o site.
Como envio diferentes países em um único processo Node.js?
Crie um agente por país, cada um com seu nome de usuário como USERNAME-country-de ou USERNAME-country-jp, e escolha o agente por requisição. Agentes são baratos, e mantê-los separados também mantém seus pools de conexão, e assim suas saídas, separados.
Qual timeout devo configurar no Node.js?
Sempre configure um: AbortSignal.timeout(30000) para fetch, timeout: 30000 para axios, timeout: { request: 30000 } para got. Trinta segundos serve para páginas HTML; dê até 60 segundos para alvos lentos e saídas móveis, e refaça uma vez em conexão nova ao invés de esperar mais.
Conecte seu crawler Node.js
US$ 5,94 para 10 GB, US$ 0,50/GB a partir de 50 GB.