Voltar ao blogue
Guias
Mihnea-Octavian ManolacheLast updated on May 1, 202613 min read

Como usar um proxy no Node-Fetch: Um Guia Prático

Como usar um proxy no Node-Fetch: Um Guia Prático
Resumo: O Node-Fetch não possui uma opção de proxy integrada, pelo que é necessário configurar um agente HTTP, HTTPS ou SOCKS5 na solicitação através da sua agent opção. Este guia explica passo a passo como utilizar um proxy no Node-Fetch de ponta a ponta: proxies HTTP e HTTPS autenticados, SOCKS5, rotação, tentativas de repetição, casos extremos de TLS, resolução de problemas e a rota undici moderna para o fetch nativo do Node 18+.

Se já alguma vez ficou a olhar para um erro 403 de um alvo que costumava rastrear sem problemas, já sabe porque é que este artigo existe. Aprender a usar um proxy no Node-Fetch é a diferença entre um script que funciona no seu portátil e um que sobrevive na CI num IP diferente, num país diferente, contra uma pilha anti-bot real. A boa notícia: a forma de usar um proxy no Node-Fetch resume-se a uma pequena superfície de API, e o resto é apenas a ligação operacional.

O Node-Fetch é um cliente HTTP popular para Node.js que traz o window.fetch para o servidor. É pequeno, assíncrono e agradável de usar, mas intencionalmente não inclui uma proxy opção. Em vez disso, expõe um agent slot, e você liga um agente de proxy externo a ele. Essa única escolha de design é o mecanismo por trás de todas as receitas abaixo.

Este guia é neutro em relação aos fornecedores e prioriza o código. Irá configurar um proxy HTTP/HTTPS, enviar o seu primeiro pedido através do proxy, adicionar credenciais com segurança, mudar para SOCKS5, alternar entre um conjunto de servidores, adicionar tempos de espera e tentativas de repetição, e verificar se o tráfego está realmente a passar pelo proxy. Também abordaremos a alternativa para Node 18+ utilizando o ProxyAgent, além de uma matriz de resolução de problemas para os erros que irá encontrar no primeiro dia.

Como usar um proxy no Node-Fetch: por que precisa de um agente

O Node-Fetch não possui um parâmetro de proxy integrado. Para encaminhar pedidos através de um proxy, deve passar um Node.js http.Agent (ou https.Agent) para a agent opção em cada fetch() chamada. Os pacotes da comunidade https-proxy-agent e socks-proxy-agent implementam essa interface e encaminham o seu tráfego através do proxy que lhes indicar.

Conclusão: o único conhecimento específico do Node-Fetch de que precisa é que a agent opção existe e aceita qualquer agente compatível. Tudo o resto — HTTP, HTTPS, SOCKS5, autenticação, peculiaridades do TLS, rotação — reside na camada do agente. Isso mantém o seu código de busca idêntico entre os fornecedores e permite-lhe trocar o transporte sem reescrever a lógica de negócio.

Configuração do projeto e dependências

Precisa de um Node.js LTS recente. O node-fetch README indica uma versão mínima exata que tem variado ao longo das versões, por isso verifique-a no node-fetch antes de fixar a sua matriz de CI. Qualquer versão da linha LTS atual é normalmente segura.

Escolha uma node-fetch versão principal intencionalmente. A versão 3 é apenas ESM, o que significa que define "type": "module" no seu package.json (ou use .mjs ficheiros) e carregue-a com import. A versão 2 ainda funciona em projetos CommonJS e é intercambiável para fins de proxy. As duas versões têm um comportamento praticamente idêntico, pelo que a v2 é uma escolha perfeitamente razoável se a sua base de código ainda não estiver em ESM.

Instale o agente proxy juntamente com o Node-Fetch:

# CommonJS friendly
npm install node-fetch@2 https-proxy-agent

# ESM (requires "type": "module" in package.json)
npm install node-fetch https-proxy-agent

Enviando a primeira solicitação através de um proxy HTTP

Assim que os pacotes estiverem instalados, o procedimento é mecânico: crie um URL de proxy, passe-o para HttpsProxyAgente passe esse agente para fetch(). A mesma classe de agente funciona quer o seu destino seja http:// ou https://, porque ela encaminha o HTTPS através do proxy com CONNECT.

// proxy-fetch.js (CommonJS, node-fetch v2)
const fetch = require('node-fetch');
const { HttpsProxyAgent } = require('https-proxy-agent');

async function main() {
  const proxyUrl = `http://${process.env.PROXY_HOST}:${process.env.PROXY_PORT}`;
  const agent = new HttpsProxyAgent(proxyUrl);

  const res = await fetch('https://ifconfig.me/all.json', { agent });
  const body = await res.json();
  console.log('Outbound IP:', body.ip_addr);
}

main().catch(console.error);

Alguns detalhes que são discretamente importantes quando lançar isto:

  • Use uma importação desestruturada ({ HttpsProxyAgent }) para https-proxy-agent v6 e versões superiores. A forma padrão da exportação mudou entre versões principais, e a importação incorreta é uma causa comum de undefined is not a constructor.
  • Reutilize o agente em todas as solicitações que tenham como destino o mesmo proxy. Criar um novo agente por chamada funciona, mas perde-se o pool de conexões e paga-se um handshake TLS todas as vezes.
  • Aceda primeiro a um ponto final de eco de IP conhecido (ifconfig.me, ident.me, ou ipinfo.io/json). Se não conseguir ver o IP do proxy a ser devolvido, não avance; nada mais funcionará até que esse caso base funcione.

Autenticação num servidor proxy

A maioria dos proxies pagos requer credenciais. A convenção é colocá-las na URL com o http://USERNAME:PASSWORD@HOST:PORT formato, que https-proxy-agent analisa por si:

const user = encodeURIComponent(process.env.PROXY_USER);
const pass = encodeURIComponent(process.env.PROXY_PASS);
const proxyUrl = `http://${user}:${pass}@${process.env.PROXY_HOST}:${process.env.PROXY_PORT}`;
const agent = new HttpsProxyAgent(proxyUrl);

Há duas coisas que causam problemas aqui. Primeiro, codificar credenciais nos ficheiros fonte faz com que estas sejam divulgadas através do histórico do Git; mantenha-as em variáveis de ambiente (ou num gestor de segredos) e insira-as em tempo de execução. Segundo, caracteres especiais nas palavras-passe (@, :, /, #) corrompem a análise da URL silenciosamente, e você receberá um 407 Proxy Authentication Required em vez de um erro de análise. Envolver o nome de utilizador e a palavra-passe em encodeURIComponent() elimina toda essa classe de erros.

Usar proxies SOCKS5 com o Node-Fetch

Quando o seu fornecedor lhe der um ponto de extremidade SOCKS5, troque o agente. O Node-Fetch não se importa com o protocolo; ele apenas chama o agente que você lhe fornecer. Instale socks-proxy-agent:

npm install socks-proxy-agent
const fetch = require('node-fetch');
const { SocksProxyAgent } = require('socks-proxy-agent');

// socks5h:// resolves DNS through the proxy; socks5:// resolves locally.
const proxyUrl = `socks5h://${process.env.PROXY_USER}:${process.env.PROXY_PASS}` +
                 `@${process.env.PROXY_HOST}:${process.env.PROXY_PORT}`;
const agent = new SocksProxyAgent(proxyUrl);

fetch('https://ifconfig.me/all.json', { agent })
  .then(r => r.json())
  .then(console.log);

Dê preferência ao socks5h:// esquema ao fazer scraping, porque ele encaminha a resolução de DNS através do proxy. O esquema socks5:// resolve o nome do host na sua máquina, o que revela o seu DNS real e anula parcialmente o objetivo de encaminhar através do proxy.

Trabalhar com destinos HTTPS e certificados autoassinados

Alguns produtos de proxy, especialmente os serviços do tipo «desbloqueador da web» de interceção, apresentam o seu próprio certificado TLS ao seu cliente e assinam novamente a resposta do servidor. O Node rejeitará esse handshake por padrão com UNABLE_TO_VERIFY_LEAF_SIGNATURE ou SELF_SIGNED_CERT_IN_CHAIN.

A solução mais fácil é definir process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'. Não faça isso em produção. Isso desativa a verificação de certificados para todos os pedidos de saída no processo, incluindo aqueles que você realmente deseja que sejam verificados.

Limite-a, em vez disso, ao próprio agente:

const agent = new HttpsProxyAgent(proxyUrl, { rejectUnauthorized: false });

Isso mantém intacta a postura TLS do resto da sua aplicação. O nome exato da opção do construtor e a forma como se propaga variam entre as https-proxy-agent versões principais, por isso verifique novamente a página do pacote no npm quando atualizar e evite completamente este sinalizador se puder fixar um pacote de CA em vez disso.

Proxies rotativos para scraping resiliente

Muitos sites utilizam limitação de taxa baseada em IP e deteção de bots, pelo que um único IP de proxy será limitado ou banido no momento em que escalar. A rotação através de um conjunto distribui a carga e faz com que cada pedido pareça vir de um utilizador diferente. Existem dois padrões que vale a pena conhecer sobre como usar um proxy no Node-Fetch em escala: uma escolha aleatória por solicitação e uma rotação determinística em round-robin. Ambos se baseiam na mesma ideia de um agente por proxy que você já conhece.

Escolher um proxy aleatório em cada pedido

Quando a única coisa com que se preocupa é a dispersão, aceda aleatoriamente ao conjunto e instancie um novo agente por chamada:

const proxies = process.env.PROXY_POOL.split(','); // host:port,host:port,...

function randomAgent() {
  const pick = proxies[Math.floor(Math.random() * proxies.length)];
  return new HttpsProxyAgent(`http://${pick}`);
}

await fetch(targetUrl, { agent: randomAgent() });

Se o mesmo proxy atender a várias solicitações consecutivas, armazene seu agente em cache em um Map indexado pela URL do proxy, para que possa reutilizar a ligação.

Iterar sequencialmente por uma lista de proxies

Para uma depuração reproduzível ou um comportamento simples de round-robin, percorra a lista com um contador. A rotação sequencial também facilita a atribuição de métricas de sucesso por proxy, o que é importante quando se começa a retirar proxies inativos:

let i = 0;
for (const url of urls) {
  const agent = new HttpsProxyAgent(`http://${proxies[i % proxies.length]}`);
  await fetch(url, { agent });
  i++;
}

Adicionar novas tentativas, tempos de espera e tratamento de erros

O tráfego de produção através de proxies públicos falha de formas interessantes: bloqueios, sockets semiabertos, ECONNRESETe repentinas tempestades de 5xx. Uma configuração robusta combina um tempo limite por pedido, um ciclo de tentativas limitado com recuo e um disjuntor que retira um proxy após falhas repetidas. (Mais sobre a estratégia de rotação no nosso guia aprofundado sobre a rotação de proxies para web scraping.)

async function fetchWithProxy(url, getAgent, opts = {}) {
  const { tries = 3, timeoutMs = 10_000 } = opts;
  let lastErr;

  for (let attempt = 1; attempt <= tries; attempt++) {
    const ctrl = new AbortController();
    const t = setTimeout(() => ctrl.abort(), timeoutMs);
    try {
      const res = await fetch(url, { agent: getAgent(), signal: ctrl.signal });
      if (res.ok) return res;
      if (res.status === 407 || res.status === 403) throw new Error(`status ${res.status}`);
    } catch (err) {
      lastErr = err;
      const backoff = 2 ** attempt * 250 + Math.random() * 250;
      await new Promise(r => setTimeout(r, backoff));
    } finally {
      clearTimeout(t);
    }
  }
  throw lastErr ?? new Error('exhausted retries');
}

Registe o número de falhas por URL de proxy num pequeno objeto e, quando um ultrapassar um limite (três falhas num minuto, por exemplo), retire-o do conjunto até que um período de arrefecimento termine. Essa regra única é a parte que a maioria dos tutoriais ignora quando explicam como usar um proxy no Node-Fetch, e é o que impede que um punhado de IPs inativos contamine todas as tentativas de repetição. Combine-a com umAbortController, para que um proxy bloqueado nunca trave o seu worker para sempre.

Verificar se o proxy está realmente a ser utilizado

Nunca confie num proxy que não tenha verificado. O teste mais simples é um diff: aceda duas vezes a um endpoint de eco de IP/geo, uma vez com o agente e outra sem, e compare. O IP e o país devem mudar.

const direct = await fetch('https://ipinfo.io/json').then(r => r.json());
const proxied = await fetch('https://ipinfo.io/json', { agent }).then(r => r.json());
console.log('direct  :', direct.ip, direct.country);
console.log('proxied :', proxied.ip, proxied.country);

Se as duas linhas corresponderem, o seu agente está a ser ignorado, geralmente devido a um erro de forma de importação ou a uma chave de opção mal escrita. Adicione esta verificação à sonda de arranque do seu scraper para que uma implementação silenciosamente direta falhe de forma evidente.

Fetch nativo no Node 18+ vs Node-Fetch

O Node 18 inclui um fetch alimentado pelo undici, e muitas equipas abandonaram o node-fetch pacote. O problema: o fetch não aceita a agent opção, pelo que https-proxy-agent não se integrará diretamente. O equivalente nativo é o ProxyAgent, definido como o despachador global:

import { ProxyAgent, setGlobalDispatcher } from 'undici';

setGlobalDispatcher(new ProxyAgent(process.env.PROXY_URL));

const res = await fetch('https://ifconfig.me/all.json');
console.log(await res.json());

A API do proxy undici mudou ao longo das versões (autenticação na URL, cabeçalhos personalizados, despachadores no âmbito da solicitação), por isso verifique a documentação atual do undici antes de definir qualquer coisa. O modelo mental ainda se alinha perfeitamente à forma de usar um proxy no Node-Fetch, apenas na camada do despachador em vez de por solicitação.

Resolução de erros comuns do proxy do Node-Fetch

A maioria dos node-fetch erros de proxy podem ser resumidos numa pequena matriz:

Sintoma

Causa provável

Solução

ECONNREFUSED

Host ou porta de proxy incorreta, ou o proxy está inativo.

Telnet/nc host:porta; mude para outro proxy.

ETIMEDOUT / pedido pendente

Sem AbortController prazo, ou o proxy está a descartar pacotes silenciosamente.

Envolva cada busca com um tempo limite por solicitação e tente novamente num proxy diferente.

407 Proxy Authentication Required

Credenciais em falta ou URL corrompido.

Codifique o nome de utilizador/palavra-passe na URL; verifique se as variáveis de ambiente estão carregadas.

SELF_SIGNED_CERT_IN_CHAIN

O proxy apresenta o seu próprio certificado TLS.

Defina rejectUnauthorized: false no agente (com âmbito), não na variável de ambiente global.

Cannot find module 'node-fetch'

v3 ESM importado de um ficheiro CommonJS.

Adicione "type": "module" ou fazer o downgrade para node-fetch@2.

HttpsProxyAgent is not a constructor

Formato de importação incorreto para v6+.

Utilize const { HttpsProxyAgent } = require('https-proxy-agent').

Quando um alvo continua a falhar em vários proxies em bom estado, o gargalo é normalmente a identificação anti-bot, em vez da camada de proxy. Nessa altura, por mais que se refine a forma de utilizar um proxy no Node-Fetch, isso não ajudará; é necessária uma camada de pedidos que também lide com a identificação.

Principais conclusões e próximos passos

O mecanismo por trás de como usar um proxy no Node-Fetch é uma opção: agent. Escolha a classe de agente correta (HttpsProxyAgent para HTTP/HTTPS, SocksProxyAgent para SOCKS5), forneça-lhe um URL devidamente codificado e o resto é rotação, tentativas e verificação. A partir daqui, os próximos passos naturais são a implementação da lógica de rotação em camadas e a comparação de clientes HTTP alternativos para o Node.js.

Pontos-chave

  • O Node-Fetch não tem proxy opção; o suporte é implementado passando um Agent (de https-proxy-agent ou socks-proxy-agent) para o agent campo em fetch().
  • Escolha a especialização deliberadamente: node-fetch@3 é exclusivo do ESM e requer "type": "module", enquanto node-fetch@2 é a opção compatível com CommonJS com o mesmo comportamento de proxy.
  • Codifique sempre as credenciais em URL e leia-as a partir de variáveis de ambiente. Um 407 Proxy Authentication Required é mais frequentemente um erro de análise do que uma falha de autenticação real.
  • O código de proxy Node-Fetch de nível de produção combina AbortController tempos de espera, recuo exponencial e descarte de um proxy inativo após algumas falhas, não apenas um try/catch em torno de fetch.
  • No Node 18+ com fetch, a agent opção não funciona; use o ProxyAgent plus setGlobalDispatcher e verifique novamente a interface da API em relação à documentação atual do undici.

Perguntas frequentes

O Node-Fetch v3 suporta proxies nativamente?

Não. Nem a v2 nem a v3 do Node-Fetch implementam uma proxy opção. O mecanismo de proxy é idêntico em ambas as versões principais: instale https-proxy-agent (ou socks-proxy-agent), crie um agente a partir do URL do seu proxy e passe-o fetch() através do agent parâmetro. A única diferença entre a v2 e a v3 é o sistema de módulos: ESM na v3 versus CommonJS na v2.

Posso reutilizar o mesmo HttpsProxyAgent em várias chamadas fetch?

Sim, e deve fazê-lo. Reutilizar um agente por cada par (proxyUrl, host de destino) permite que o conjunto de sockets subjacente mantenha as ligações ativas, o que elimina um handshake TLS de cada pedido e reduz significativamente a latência sob carga. Só deve criar um novo agente quando a própria URL do proxy mudar, por exemplo, durante a rotação. Armazene-os em cache num Map chave de URL de proxy para manter tanto a reutilização como a rotação.

Qual é a diferença entre o HttpProxyAgent e o HttpsProxyAgent, e quando preciso de cada um?

HttpProxyAgent tuneis http:// através de um proxy sem CONNECT. HttpsProxyAgent enviar um HTTP CONNECT para o proxy e, em seguida, executa TLS para o destino, que é o que precisa para qualquer https:// URL. Na prática, HttpsProxyAgent abrange ambos os protocolos com segurança e é a recomendação padrão. Recorra a HttpProxyAgent apenas quando tiver um motivo específico e se destinar a pontos finais exclusivamente HTTP.

Por que é que a minha solicitação Node-Fetch via proxy retorna 407 Proxy Authentication Required?

Um 407 significa que o proxy não aceitou as suas credenciais, mas a causa geralmente está a montante do próprio proxy. A mais comum é um caractere especial na palavra-passe que interrompe a análise da URL. Coloque ambos os campos entre encodeURIComponent e recarregue-os a partir de variáveis de ambiente. Depois disso, verifique novamente o formato de credenciais que o seu provedor espera (alguns exigem um token de sessão, código de país ou prefixo de sessão no nome de utilizador).

Como posso utilizar um proxy com o fetch integrado no Node.js 18+ em vez do node-fetch?

O fetch no Node 18+ usa o undici, que ignora a agent opção. Instale undici, crie um ProxyAgent a partir do URL do seu proxy e registe-o com setGlobalDispatcher. Depois disso, todas as fetch() é encaminhada através do proxy sem necessidade de alterações adicionais no código. A API do undici evoluiu ao longo das versões, por isso verifique os nomes atuais das opções na documentação do undici antes de fixar.

Conclusão

Depois de internalizar a agent opção, todas as receitas deste guia são pequenas variações da mesma ideia. HTTP, HTTPS e SOCKS5 partilham uma assinatura de busca; a autenticação é uma codificação de URL feita corretamente; a rotação, as tentativas de repetição e a omissão de proxies inativos residem num invólucro fino em torno de fetch(); e a rota do Node 18+ undici é o mesmo modelo mental traduzido para um despachante global.

Não subestime a camada operacional. Proxies gratuitos são inúteis em produção: baixa reputação de IP, alto tempo de inatividade e comportamento TLS imprevisível vão consumir o seu orçamento de erros. Um conjunto limpo de endereços residenciais ou de datacenter, juntamente com o invólucro de timeout e repetição de tentativas mencionado anteriormente, é o que transforma um snippet num scraper implementável.

Se preferir não gerir a infraestrutura de proxies por conta própria, a API Scraper da WebScrapingAPI lida com a rotação de proxies, a mitigação anti-bot e as tentativas de repetição por trás de um único ponto de extremidade, para que possa manter o seu node-fetch código e trocar a camada de pedidos. A partir daqui, os próximos passos naturais são a estratégia de rotação em camadas e a escolha do cliente HTTP Node.js certo para a sua carga de trabalho, ambos abordados em profundidade nos nossos guias complementares.

Sobre o autor
Mihnea-Octavian Manolache, Desenvolvedor Full Stack @ WebScrapingAPI
Mihnea-Octavian ManolacheDesenvolvedor Full Stack

Mihnea-Octavian Manolache é engenheiro Full Stack e DevOps na WebScrapingAPI, onde desenvolve funcionalidades do produto e mantém a infraestrutura que garante o bom funcionamento da plataforma.

Comece a construir

Pronto para expandir a sua recolha de dados?

Junte-se a mais de 2.000 empresas que utilizam a WebScrapingAPI para extrair dados da Web à escala empresarial, sem quaisquer custos de infraestrutura.