Se você está escrevendo um parser, integrando uma API externa ou automatizando a coleta de dados em Ruby — cedo ou tarde você encontrará bloqueios por IP. Wildberries, Ozon, Instagram e dezenas de outros serviços cortam solicitações de um único endereço já após algumas centenas de acessos. A solução é uma — proxies. Neste artigo, vamos explorar como conectar proxies a um projeto Ruby através do padrão Net::HTTP e do popular cliente HTTP Faraday, configurar a rotação de IP e tratar erros corretamente — tanto em uma aplicação Rails quanto em scripts simples.
Por que um desenvolvedor Ruby precisa de proxies: cenários principais
Ruby é amplamente utilizado para tarefas onde proxies são indispensáveis. Antes de passar para o código, é importante entender em quais situações eles são necessários — isso ajudará a escolher o tipo e a arquitetura corretos da solução.
Parsing e coleta de dados. Este é o cenário mais comum. Você escreve um script em Ruby que navega pelas páginas do Wildberries, Ozon ou Avito, coletando preços e características dos produtos. Sem proxies, tal script será bloqueado por IP em poucos minutos. Os sites detectam centenas de solicitações de um único endereço e ativam automaticamente a proteção — captcha, ban temporário ou recusa total de serviço.
Integração com APIs dependentes de geolocalização. Alguns serviços externos retornam conteúdo diferente dependendo do país da solicitação. Se sua aplicação Rails faz chamadas a tal API, proxies permitem simular solicitações da região desejada. Por exemplo, verificar os resultados dos motores de busca para diferentes países ou obter preços relevantes para um mercado específico.
Testes e QA. Desenvolvedores usam proxies para testar o comportamento da aplicação em solicitações de diferentes endereços IP e países. Isso é especialmente relevante para serviços com geobloqueio ou limitação de taxa por IP.
Automação de marketing. Scripts Ruby são frequentemente usados para monitorar campanhas publicitárias, verificar posições em resultados de busca ou rastrear a atividade de concorrentes. Aqui, proxies são necessários para distribuir a carga e contornar restrições.
Trabalho com várias contas. Se seu script gerencia várias contas em uma única plataforma, cada conta deve operar através de um IP separado — caso contrário, a plataforma facilmente detectará a conexão e bloqueará tudo de uma vez.
Qual tipo de proxy escolher para tarefas em Ruby
Nem todos os proxies são igualmente adequados para diferentes tarefas. A escolha do tipo impacta diretamente no sucesso do seu script. Vamos analisar as principais opções em relação ao desenvolvimento Ruby.
| Tipo de proxy | Melhores tarefas em Ruby | Velocidade | Contorno de proteções |
|---|---|---|---|
| Proxies de data center | Parsing sem proteção rigorosa, solicitações de API, testes | Alta | Média |
| Proxies residenciais | Parsing de sites protegidos, trabalho com redes sociais, marketplaces | Média | Alta |
| Proxies móveis | Facebook, Instagram, TikTok API, trabalho com contas | Média | Máxima |
Proxies de data center — a opção mais rápida e barata. Adequados para parsing de sites sem proteção agressiva, solicitações de API em massa e testes de carga. No entanto, grandes plataformas (Facebook, Google, Cloudflare) facilmente os reconhecem pelo ASN do data center.
Proxies residenciais utilizam IPs de usuários domésticos reais. Para scripts Ruby que fazem parsing do Wildberries, Ozon ou trabalham com sites protegidos — esta é a escolha ideal. Os sites veem um usuário comum, e não um servidor.
Proxies móveis operam através de IPs de operadores móveis (4G/5G). Esses são os endereços mais confiáveis do ponto de vista das plataformas — um IP móvel pode ser utilizado por centenas de usuários reais simultaneamente, portanto, bloqueios são extremamente raros. Se seu script Ruby trabalha com redes sociais ou plataformas publicitárias — proxies móveis reduzirão o risco de bloqueios ao mínimo.
Proxies em Net::HTTP: configuração básica
Net::HTTP — a biblioteca padrão do Ruby para solicitações HTTP, incluída na distribuição padrão da linguagem. Ela suporta proxies "out of the box" através de um método especial Net::HTTP::Proxy.
A maneira mais simples é criar uma classe de proxy e usá-la em vez do Net::HTTP normal:
require 'net/http'
require 'uri'
# Dados do proxy
proxy_host = 'proxy.example.com'
proxy_port = 8080
# Criando a classe de proxy
proxy_class = Net::HTTP::Proxy(proxy_host, proxy_port)
# Executando a solicitação através do proxy
uri = URI('https://httpbin.org/ip')
proxy_class.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
request = Net::HTTP::Get.new(uri)
response = http.request(request)
puts response.body
end
O método Net::HTTP::Proxy aceita quatro parâmetros: host do proxy, porta, nome de usuário e senha. Os dois primeiros são obrigatórios, os últimos dois são opcionais para proxies sem autenticação.
Uma alternativa é passar os parâmetros do proxy diretamente para o método Net::HTTP.new:
require 'net/http'
uri = URI('https://httpbin.org/ip')
http = Net::HTTP.new(
uri.host,
uri.port,
'proxy.example.com', # proxy_addr
8080, # proxy_port
nil, # proxy_user (nil se sem autenticação)
nil # proxy_pass
)
http.use_ssl = true
response = http.get(uri.path)
puts response.body
Essa abordagem é conveniente quando você precisa reutilizar o mesmo objeto HTTP para várias solicitações a um único host. Observe que os parâmetros do proxy são passados como o terceiro e quarto argumento, e não através de um método separado.
Autenticação e HTTPS através do Net::HTTP
A maioria dos proxies comerciais requer autenticação — login e senha. Além disso, ao trabalhar com sites HTTPS, uma configuração adicional de SSL é necessária. Vamos analisar ambos os casos.
Proxies com login e senha:
require 'net/http'
require 'uri'
proxy_host = 'proxy.example.com'
proxy_port = 8080
proxy_user = 'your_username'
proxy_pass = 'your_password'
# Criando a classe de proxy com autenticação
proxy_class = Net::HTTP::Proxy(proxy_host, proxy_port, proxy_user, proxy_pass)
uri = URI('https://httpbin.org/ip')
proxy_class.start(uri.host, uri.port, use_ssl: true) do |http|
# Desativando a verificação de SSL (apenas para testes!)
# http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new(uri)
request['User-Agent'] = 'Mozilla/5.0 (compatible; MyBot/1.0)'
response = http.request(request)
puts "IP: #{response.body}"
puts "Status: #{response.code}"
end
Um ponto importante: ao usar proxies HTTPS (túnel através do CONNECT) Net::HTTP estabelece automaticamente uma conexão SSL com o servidor de destino através do túnel proxy. Isso funciona corretamente para a maioria dos servidores proxy.
Configuração através de variáveis de ambiente. Uma boa prática é não hardcodar os dados do proxy no código, mas lê-los a partir de variáveis de ambiente:
require 'net/http'
require 'uri'
# Lendo as configurações do proxy do ENV
proxy_uri = URI(ENV.fetch('HTTP_PROXY', 'http://proxy.example.com:8080'))
proxy_class = Net::HTTP::Proxy(
proxy_uri.host,
proxy_uri.port,
proxy_uri.user,
proxy_uri.password
)
target_uri = URI('https://httpbin.org/ip')
proxy_class.start(target_uri.host, target_uri.port, use_ssl: true) do |http|
response = http.get(target_uri.path)
puts response.body
end
A variável de ambiente pode ser definida no formato HTTP_PROXY=http://user:[email protected]:8080. Essa abordagem permite mudar o proxy sem alterar o código — basta atualizar a variável no arquivo .env ou nas configurações do servidor.
Conectando proxies através do Faraday
Faraday é um dos clientes HTTP mais populares no ecossistema Ruby. Ele é usado em muitas aplicações Rails devido à sua abordagem conveniente de middleware e suporte a diferentes adaptadores (Net::HTTP, HTTParty, Typhoeus e outros). A configuração de proxies no Faraday é um pouco diferente dependendo do adaptador utilizado.
Configuração básica de proxies no Faraday:
require 'faraday'
# Criando uma conexão Faraday com proxy
conn = Faraday.new(url: 'https://httpbin.org') do |faraday|
faraday.proxy = {
uri: 'http://proxy.example.com:8080',
user: 'your_username',
password: 'your_password'
}
faraday.headers['User-Agent'] = 'Mozilla/5.0 (compatible; MyBot/1.0)'
faraday.adapter Faraday.default_adapter
end
response = conn.get('/ip')
puts response.body
puts "Status: #{response.status}"
O parâmetro faraday.proxy aceita um hash com as chaves uri, user e password. Você também pode passar uma string URI diretamente, se o proxy não exigir autenticação.
Faraday com adaptador Net::HTTP e configurações avançadas:
require 'faraday'
conn = Faraday.new(url: 'https://httpbin.org') do |faraday|
faraday.proxy = 'http://user:[email protected]:8080'
# Configurações de timeout
faraday.options.timeout = 30
faraday.options.open_timeout = 10
# Middleware para logging (útil durante a depuração)
faraday.response :logger
# Retry em caso de erros de conexão
faraday.request :retry, max: 3, interval: 1
# Adaptador
faraday.adapter :net_http
end
begin
response = conn.get('/ip')
puts response.body
rescue Faraday::ConnectionFailed => e
puts "Erro de conexão: #{e.message}"
rescue Faraday::TimeoutError => e
puts "Timeout: #{e.message}"
end
O middleware :retry é especialmente útil ao trabalhar com proxies — se um endereço estiver temporariamente indisponível, o Faraday tentará automaticamente repetir a solicitação. Isso é crítico para o funcionamento confiável do script em produção.
Faraday com adaptador Typhoeus (para solicitações paralelas):
# Gemfile: gem 'typhoeus'
require 'faraday'
require 'typhoeus/adapters/faraday'
conn = Faraday.new(url: 'https://httpbin.org') do |faraday|
faraday.proxy = 'http://user:[email protected]:8080'
faraday.adapter :typhoeus
end
# Solicitações paralelas através do proxy
responses = []
conn.in_parallel do
responses << conn.get('/ip')
responses << conn.get('/headers')
responses << conn.get('/user-agent')
end
responses.each { |r| puts r.body }
Rotação de proxies: troca automática de IP
Um único endereço proxy, mesmo residencial, cedo ou tarde encontrará limitações durante um parsing intensivo. A solução é a rotação: troca automática de IP após cada solicitação ou em intervalos determinados. Vamos implementar um simples rotator para scripts Ruby.
Um rotator simples baseado em um array de proxies:
require 'net/http'
require 'uri'
class ProxyRotator
def initialize(proxies)
@proxies = proxies
@index = 0
@mutex = Mutex.new
end
def next_proxy
@mutex.synchronize do
proxy = @proxies[@index % @proxies.length]
@index += 1
proxy
end
end
def random_proxy
@proxies.sample
end
end
# Lista de proxies no formato [host, port, user, pass]
proxies = [
['proxy1.example.com', 8080, 'user1', 'pass1'],
['proxy2.example.com', 8080, 'user2', 'pass2'],
['proxy3.example.com', 8080, 'user3', 'pass3'],
]
rotator = ProxyRotator.new(proxies)
# Fazendo parsing de uma lista de URLs através de diferentes proxies
urls = [
'https://httpbin.org/ip',
'https://httpbin.org/headers',
'https://httpbin.org/user-agent'
]
urls.each do |url|
proxy = rotator.next_proxy
proxy_class = Net::HTTP::Proxy(proxy[0], proxy[1], proxy[2], proxy[3])
uri = URI(url)
begin
proxy_class.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.read_timeout = 15
response = http.get(uri.path)
puts "#{url} -> Status: #{response.code}, Proxy: #{proxy[0]}"
end
rescue => e
puts "Erro com o proxy #{proxy[0]}: #{e.message}"
# Passa automaticamente para o próximo proxy
end
sleep(rand(0.5..2.0)) # Atraso aleatório entre as solicitações
end
O atraso aleatório entre as solicitações (sleep(rand(0.5..2.0))) é um elemento importante. O padrão de solicitações com intervalos fixos é facilmente detectado por sistemas anti-bot. Atrasos aleatórios imitam o comportamento de um usuário real.
Rotação através do Faraday com middleware:
require 'faraday'
class ProxyMiddleware < Faraday::Middleware
PROXIES = [
'http://user1:[email protected]:8080',
'http://user2:[email protected]:8080',
'http://user3:[email protected]:8080',
].freeze
def call(env)
# Escolhendo um proxy aleatório para cada solicitação
proxy_uri = URI(PROXIES.sample)
env[:request][:proxy] = {
uri: proxy_uri,
user: proxy_uri.user,
password: proxy_uri.password
}
@app.call(env)
end
end
# Registrando o middleware
Faraday::Middleware.register_middleware proxy_rotator: ProxyMiddleware
conn = Faraday.new(url: 'https://httpbin.org') do |faraday|
faraday.use :proxy_rotator
faraday.adapter :net_http
end
5.times do
response = conn.get('/ip')
puts response.body
end
Integração de proxies em uma aplicação Rails
Em uma aplicação Rails, proxies são frequentemente necessários para solicitações HTTP externas: integração com APIs, tarefas de parsing em segundo plano ou webhooks. Vamos explorar alguns padrões práticos.
Padrão 1: Objeto de serviço com Faraday. Criamos uma classe de serviço básica que faz todas as solicitações HTTP externas através de proxies:
# app/services/http_client.rb
class HttpClient
def self.connection(base_url)
Faraday.new(url: base_url) do |faraday|
# Proxies a partir de variáveis de ambiente Rails
if Rails.env.production? && ENV['PROXY_URL'].present?
faraday.proxy = ENV['PROXY_URL']
end
faraday.headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
faraday.options.timeout = 30
faraday.options.open_timeout = 10
faraday.request :retry, max: 3, interval: 2,
exceptions: [Faraday::ConnectionFailed, Faraday::TimeoutError]
faraday.response :raise_error
faraday.adapter :net_http
end
end
end
# Uso em um controlador ou serviço:
# client = HttpClient.connection('https://api.example.com')
# response = client.get('/products', { category: 'electronics' })
Padrão 2: Configuração através de credenciais do Rails. Para armazenar dados do proxy, utilize credenciais criptografadas do Rails:
# config/credentials.yml.enc (editado através de rails credentials:edit)
# proxy:
# host: proxy.example.com
# port: 8080
# username: your_user
# password: your_pass
# app/services/proxy_service.rb
class ProxyService
def self.faraday_proxy_config
creds = Rails.application.credentials.proxy
return nil unless creds
{
uri: "http://#{creds[:host]}:#{creds[:port]}",
user: creds[:username],
password: creds[:password]
}
end
def self.net_http_proxy
creds = Rails.application.credentials.proxy
return Net::HTTP::Proxy(nil, nil) unless creds
Net::HTTP::Proxy(
creds[:host],
creds[:port],
creds[:username],
creds[:password]
)
end
end
Padrão 3: Tarefas em segundo plano com Sidekiq. Se o parsing ou as solicitações de API são executadas em trabalhadores em segundo plano, a configuração do proxy permanece a mesma, mas é importante considerar a concorrência:
# app/workers/price_parser_worker.rb
class PriceParserWorker
include Sidekiq::Worker
sidekiq_options retry: 3, queue: :parsers
def perform(product_url)
# Criando uma nova conexão para cada trabalhador
proxy = ProxyService.net_http_proxy
uri = URI(product_url)
proxy.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.read_timeout = 20
request = Net::HTTP::Get.new(uri)
request['User-Agent'] = random_user_agent
response = http.request(request)
if response.code == '200'
parse_and_save(response.body, product_url)
else
Rails.logger.warn "Status inesperado #{response.code} para #{product_url}"
end
end
rescue Net::OpenTimeout, Net::ReadTimeout => e
Rails.logger.error "Timeout para #{product_url}: #{e.message}"
raise # Sidekiq repetirá a tarefa
end
private
def random_user_agent
agents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36'
]
agents.sample
end
def parse_and_save(html, url)
# Lógica de parsing...
end
end
Tratamento de erros e timeouts
Trabalhar através de proxies adiciona uma camada adicional que pode gerar erros específicos. Um tratamento de exceções adequado é a diferença entre um script que falha em 5 minutos e um script que funciona por horas sem intervenção.
Principais tipos de erros ao trabalhar com proxies em Ruby:
| Exceção | Causa | O que fazer |
|---|---|---|
Net::OpenTimeout |
Proxy não responde | Trocar proxy, repetir |
Net::ReadTimeout |
Proxy lento ou site não responde | Aumentar timeout ou trocar proxy |
Errno::ECONNREFUSED |
Servidor proxy rejeitou a conexão | Verificar dados do proxy |
OpenSSL::SSL::SSLError |
Problema com SSL através do proxy | Verificar configurações de SSL |
| HTTP 407 | Login/senha do proxy incorretos | Verificar credenciais |
| HTTP 403/429 | IP bloqueado pelo site | Trocar proxy, adicionar pausa |
Tratamento abrangente de erros com troca automática de proxies:
require 'net/http'
require 'uri'
class RobustHttpClient
MAX_RETRIES = 3
RETRIABLE_ERRORS = [
Net::OpenTimeout,
Net::ReadTimeout,
Errno::ECONNREFUSED,
Errno::ECONNRESET,
OpenSSL::SSL::SSLError
].freeze
def initialize(proxies)
@proxies = proxies.dup
@failed_proxies = []
end
def get(url, retries: MAX_RETRIES)
uri = URI(url)
attempt = 0
begin
attempt += 1
proxy = current_proxy
proxy_class = Net::HTTP::Proxy(*proxy)
proxy_class.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.open_timeout = 10
http.read_timeout = 20
request = Net::HTTP::Get.new(uri)
request['User-Agent'] = 'Mozilla/5.0 (compatible; RubyBot/1.0)'
response = http.request(request)
case response.code.to_i
when 200..299
response
when 407
raise "Falha na autenticação do proxy para #{proxy[0]}"
when 403, 429
rotate_proxy!
raise "IP bloqueado (#{response.code}), trocando proxy"
else
raise "Status HTTP inesperado: #{response.code}"
end
end
rescue *RETRIABLE_ERRORS => e
Rails.logger.warn "Erro de conexão (tentativa #{attempt}/#{retries}): #{e.message}"
rotate_proxy!
retry if attempt < retries
raise "Falhou após #{retries} tentativas: #{e.message}"
rescue RuntimeError => e
Rails.logger.warn "Erro HTTP (tentativa #{attempt}/#{retries}): #{e.message}"
retry if attempt < retries
raise
end
end
private
def current_proxy
@proxies.first || raise("Sem proxies disponíveis!")
end
def rotate_proxy!
failed = @proxies.shift
@failed_proxies << failed
Rails.logger.info "Proxy trocado. Restantes: #{@proxies.length}"
end
end
Proxies SOCKS5 em Ruby: configuração através do socksify
O padrão Net::HTTP não suporta SOCKS5 nativamente — apenas proxies HTTP/HTTPS. Para trabalhar com SOCKS5, é necessária uma biblioteca adicional. A opção mais popular é a gem socksify.
Por que SOCKS5 pode ser preferível a proxies HTTP? O protocolo SOCKS5 opera em um nível mais baixo — ele proxy qualquer tráfego TCP, e não apenas HTTP. Além disso, SOCKS5 suporta autenticação e transmite menos metadados sobre a solicitação, tornando-o menos visível para sistemas de detecção de bots.
# Gemfile
# gem 'socksify'
require 'socksify'
require 'socksify/http'
require 'net/http'
require 'uri'
# Configuração global do proxy SOCKS5
TCPSocket::socks_server = 'proxy.example.com'
TCPSocket::socks_port = 1080
# Opcional: autenticação (SOCKS5 username/password)
# TCPSocket::socks_username = 'your_user'
# TCPSocket::socks_password = 'your_pass'
# Agora todas as solicitações Net::HTTP vão através do SOCKS5
uri = URI('https://httpbin.org/ip')
response = Net::HTTP.get_response(uri)
puts response.body
Observe que ao usar socksify, a configuração é aplicada globalmente a todas as conexões TCP no processo. Se você precisar de uma configuração mais flexível — use Net::HTTP.SOCKSProxy da mesma gem:
require 'socksify/http'
# Criando uma classe de proxy SOCKS5 (semelhante ao Net::HTTP::Proxy para HTTP)
socks_proxy = Net::HTTP.SOCKSProxy('proxy.example.com', 1080)
uri = URI('https://httpbin.org/ip')
socks_proxy.start(uri.host, uri.port, use_ssl: true) do |http|
response = http.get(uri.path)
puts response.body
end
Para Faraday com SOCKS5, será necessário um adaptador que suporte esse protocolo. Uma boa opção é faraday-net_http em conjunto com socksify, ou usar o adaptador baseado em curl faraday-typhoeus, que suporta nativamente SOCKS5 através do libcurl.
💡 Dica sobre escolha de protocolo
Para a maioria das tarefas de parsing e trabalho com APIs, proxies HTTP/HTTPS são suficientes — eles são mais fáceis de configurar e suportados nativamente. SOCKS5 deve ser usado quando você precisa proxy não apenas tráfego HTTP, ou quando proxies HTTP são facilmente detectados pelo serviço de destino. Proxies móveis e residenciais estão disponíveis em ambos os protocolos.
Conclusão e recomendações
Configurar proxies em Ruby não requer bibliotecas complexas — o padrão Net::HTTP suporta proxies HTTP "out of the box", e o Faraday torna a integração ainda mais conveniente devido à sua arquitetura de middleware. Principais conclusões deste guia:
- Para Net::HTTP, use
Net::HTTP::Proxy— este é um método embutido e confiável. - Para Faraday, o parâmetro
faraday.proxyaceita uma string URI ou um hash com credenciais. - Armazene dados do proxy em variáveis de ambiente ou credenciais do Rails — não hardcode no código.
- Implemente rotação de proxies e tratamento de erros para um funcionamento confiável em produção.
- Adicione atrasos aleatórios e rotação de User-Agent para imitar o comportamento humano.
- Para SOCKS5, use a gem
socksify.
A escolha do tipo de proxy depende da tarefa. Se você está escrevendo um parser para Wildberries ou Ozon — proxies residenciais proporcionarão o melhor equilíbrio entre velocidade e contorno de proteções: IPs reais de usuários domésticos raramente entram em listas de bloqueio. Para tarefas de alta carga com um grande volume de solicitações a fontes menos protegidas, proxies de data center são mais rápidos e mais baratos por solicitação. Se seu script Ruby trabalha com redes sociais ou APIs publicitárias — considere proxies móveis: seus endereços IP têm o nível máximo de confiança na maioria das plataformas.
```