Em Ruby, Net::HTTP e Mechanize facilitam o uso de proxies ao permitirem a configuração direta de host, porta e credenciais de autenticação do proxy durante a inicialização do objeto ou no estabelecimento da conexão. Essas bibliotecas permitem que aplicações Ruby roteiem requisições de rede através de servidores proxy intermediários, atendendo casos de uso como rotação de IP, geo-targeting e contorno de rate limits.
Configuração de proxy no Net::HTTP
Net::HTTP é a biblioteca padrão do Ruby para fazer requisições HTTP. Ela oferece controle direto sobre os parâmetros de conexão, incluindo as configurações de proxy.
Parâmetros diretos de proxy
Para configurar um proxy em uma requisição Net::HTTP, informe host, porta, usuário e senha do proxy ao criar o objeto Net::HTTP ou ao iniciar a conexão.
require 'net/http'
require 'uri'
# Dados do proxy
proxy_host = 'your_proxy_host.com'
proxy_port = 8080
proxy_user = 'proxy_username'
proxy_pass = 'proxy_password'
# URL de destino
uri = URI('http://example.com/data')
# Método 1: informar os parâmetros do proxy em Net::HTTP.new
http = Net::HTTP.new(uri.host, uri.port, proxy_host, proxy_port, proxy_user, proxy_pass)
http.use_ssl = (uri.scheme == 'https') # Necessário para HTTPS
request = Net::HTTP::Get.new(uri.request_uri)
begin
response = http.request(request)
puts "Method 1 Response Status: #{response.code}"
# puts response.body
rescue Net::HTTPClientException => e
puts "HTTP Error: #{e.message}"
rescue Net::ReadTimeout, Net::OpenTimeout => e
puts "Timeout Error: #{e.message}"
rescue StandardError => e
puts "An error occurred: #{e.message}"
ensure
http.finish if http.started? # Garante que a conexão seja fechada
end
# Método 2: informar os parâmetros do proxy no bloco Net::HTTP.start
uri_https = URI('https://httpbin.org/ip') # Endpoint simples para verificar o IP
Net::HTTP.start(uri_https.host, uri_https.port,
proxy_host, proxy_port, proxy_user, proxy_pass,
use_ssl: uri_https.scheme == 'https') do |http_with_proxy|
request_https = Net::HTTP::Get.new(uri_https.request_uri)
response_https = http_with_proxy.request(request_https)
puts "Method 2 Response Status: #{response_https.code}"
puts "External IP via proxy: #{response_https.body}" # Deve mostrar o IP do proxy
rescue Net::HTTPClientException => e
puts "HTTP Error: #{e.message}"
rescue Net::ReadTimeout, Net::OpenTimeout => e
puts "Timeout Error: #{e.message}"
rescue StandardError => e
puts "An error occurred: #{e.message}"
end
Variáveis de ambiente
Net::HTTP consegue detectar automaticamente as configurações de proxy a partir de variáveis de ambiente. Essa abordagem serve para configurar o proxy em todo o sistema ou em toda a aplicação sem alterar o código.
http_proxy: para requisições HTTP (ex.:http://user:[email protected]:8080)https_proxy: para requisições HTTPS (ex.:https://user:[email protected]:8080)no_proxy: lista separada por vírgulas de hostnames que devem ignorar o proxy.
require 'net/http'
require 'uri'
# Define as variáveis de ambiente (exemplo; normalmente feito fora do script)
# ENV['http_proxy'] = 'http://proxy_username:proxy_password@your_proxy_host.com:8080'
# ENV['https_proxy'] = 'http://proxy_username:proxy_password@your_proxy_host.com:8080' # Obs.: https_proxy também pode ser um proxy http
# ENV['no_proxy'] = 'localhost,127.0.0.1'
uri = URI('http://example.com/data')
http = Net::HTTP.new(uri.host, uri.port) # Sem parâmetros explícitos de proxy
request = Net::HTTP::Get.new(uri.request_uri)
# Se http_proxy/https_proxy estiverem definidas, Net::HTTP vai usá-las.
begin
response = http.request(request)
puts "Env Var Response Status: #{response.code}"
rescue StandardError => e
puts "An error occurred: #{e.message}"
end
# Limpe as variáveis de ambiente depois do uso, se definidas via código
# ENV.delete('http_proxy')
# ENV.delete('https_proxy')
Configuração de proxy no Mechanize
Mechanize é uma gem Ruby que simplifica o web scraping emulando um navegador. Ela é construída sobre Net::HTTP e oferece uma API de nível mais alto para lidar com proxies.
Parâmetros diretos de proxy
O Mechanize permite definir os dados do proxy durante a inicialização do agente ou chamando o método set_proxy.
require 'mechanize'
# Dados do proxy
proxy_host = 'your_proxy_host.com'
proxy_port = 8080
proxy_user = 'proxy_username'
proxy_pass = 'proxy_password'
# Método 1: inicializar o Mechanize com os parâmetros do proxy
agent = Mechanize.new do |a|
a.set_proxy(proxy_host, proxy_port, proxy_user, proxy_pass)
a.user_agent_alias = 'Mac Safari' # Recomendado para web scraping
end
begin
page = agent.get('http://example.com/')
puts "Method 1 Mechanize Page Title: #{page.title}"
rescue Mechanize::ResponseCodeError => e
puts "Mechanize HTTP Error: #{e.response_code} - #{e.page.uri}"
rescue Mechanize::Error => e
puts "Mechanize Error: #{e.message}"
rescue StandardError => e
puts "An error occurred: #{e.message}"
end
# Método 2: passar os parâmetros do proxy direto para Mechanize.new (mais simples para uso pontual)
agent_direct = Mechanize.new(proxy_addr: proxy_host,
proxy_port: proxy_port,
proxy_user: proxy_user,
proxy_pass: proxy_pass)
agent_direct.user_agent_alias = 'Linux Firefox'
begin
page_direct = agent_direct.get('https://httpbin.org/ip')
puts "Method 2 Mechanize External IP via proxy: #{page_direct.body}"
rescue Mechanize::ResponseCodeError => e
puts "Mechanize HTTP Error: #{e.response_code} - #{e.page.uri}"
rescue Mechanize::Error => e
puts "Mechanize Error: #{e.message}"
rescue StandardError => e
puts "An error occurred: #{e.message}"
end
Rotação de proxies com Mechanize
Em cenários que exigem troca frequente de IP, como coleta de dados em larga escala, a rotação de proxies é essencial. O método set_proxy do Mechanize pode ser chamado várias vezes para trocar o proxy ao longo do ciclo de vida do agente.
require 'mechanize'
proxies = [
{ host: 'proxy1.example.com', port: 8080, user: 'user1', pass: 'pass1' },
{ host: 'proxy2.example.com', port: 8080, user: 'user2', pass: 'pass2' },
# ... mais proxies
]
agent = Mechanize.new
agent.user_agent_alias = 'Windows Chrome'
proxies.each_with_index do |p, i|
puts "Using proxy #{i+1}: #{p[:host]}"
agent.set_proxy(p[:host], p[:port], p[:user], p[:pass])
begin
page = agent.get('https://httpbin.org/ip')
puts "Current IP: #{page.body.strip}"
sleep(2) # Pausa para não sobrecarregar o alvo
rescue Mechanize::ResponseCodeError => e
puts "Error with proxy #{p[:host]}: #{e.response_code}"
rescue Mechanize::Error, StandardError => e
puts "Connection error with proxy #{p[:host]}: #{e.message}"
end
end
Comparação: Net::HTTP vs. Mechanize para proxies
| Característica | Net::HTTP | Mechanize |
|---|---|---|
| Nível de abstração | Cliente HTTP de baixo nível. Controle direto do socket. | Biblioteca de web scraping de alto nível. Emula um navegador. |
| Configuração de proxy | Argumentos do construtor ou Net::HTTP.start. |
Método set_proxy ou opções de Mechanize.new. |
| Facilidade de uso | Mais verboso em tarefas complexas. | Mais simples para navegar em sites e enviar formulários. |
| Recursos automáticos | Nada além do HTTP básico. | Gerenciamento de cookies, redirecionamentos, interpretação de JavaScript (limitada), gestão de user-agent. |
| Tratamento de erros | Exceções de Net::HTTP (ex.: Net::OpenTimeout). |
Mechanize::Error, Mechanize::ResponseCodeError. |
| Melhor para | Chamadas de API simples, requisições HTTP/S específicas. | Web scraping, automação de navegador, navegação complexa. |
| Dependência | Biblioteca padrão. | Gem (mechanize). |
Tipos de proxy e limitações
Net::HTTP (e, por extensão, o Mechanize) suporta principalmente proxies dos tipos HTTP e HTTPS. Esses proxies encaminham requisições HTTP/HTTPS.
- Proxies HTTP: usados para tráfego HTTP não criptografado.
- Proxies HTTPS (CONNECT): usados para tráfego HTTPS criptografado. O
Net::HTTPestabelece um túnelCONNECTatravés do proxy até o host de destino. - Proxies SOCKS:
Net::HTTPnão suporta proxies SOCKS (SOCKS4, SOCKS5) de forma nativa. Para usar proxies SOCKS em Ruby, é necessária uma gem externa comosocks-ruby. Essa gem se integra aoNet::HTTPsobrescrevendo a criação do socket.
# Exemplo de uso de proxy SOCKS com socks-ruby (requer a gem 'socks-ruby')
# gem install socks-ruby
require 'net/http'
require 'socks-ruby'
require 'uri'
socks_proxy_host = 'your_socks_proxy.com'
socks_proxy_port = 1080
socks_proxy_user = 'socks_user'
socks_proxy_pass = 'socks_pass'
uri = URI('https://httpbin.org/ip')
# Sobrescreve a criação do socket do Net::HTTP
Net::HTTP.class_eval do
def connect
if proxy_address
# Lógica existente de proxy HTTP/HTTPS
super
elsif ENV['SOCKS_PROXY'] # Variável de ambiente personalizada para SOCKS
socks_uri = URI(ENV['SOCKS_PROXY'])
socks_socket = Socks::HTTP.new(socks_uri.host, socks_uri.port,
socks_uri.user, socks_uri.password)
@socket = socks_socket.connect(@address, @port)
@socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
if use_ssl?
@socket = Net::HTTP::SSL_SOCKET_CLASS.new(@socket, read_timeout: @read_timeout)
@socket.sync_close = true
@socket.connect
end
else
super
end
end
end
# Define uma variável de ambiente personalizada para o proxy SOCKS
ENV['SOCKS_PROXY'] = "socks5://#{socks_proxy_user}:#{socks_proxy_pass}@#{socks_proxy_host}:#{socks_proxy_port}"
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true # Essencial para HTTPS
http.verify_mode = OpenSSL::SSL::VERIFY_PEER # Recomendado por segurança
request = Net::HTTP::Get.new(uri.request_uri)
begin
response = http.request(request)
puts "SOCKS Proxy Response Status: #{response.code}"
puts "External IP via SOCKS proxy: #{response.body}"
rescue StandardError => e
puts "SOCKS Proxy Error: #{e.message}"
end
ENV.delete('SOCKS_PROXY') # Limpeza
Tratamento de erros e timeouts
Ao usar proxies, problemas de rede, configurações incorretas do proxy ou falhas do servidor proxy são comuns. Um tratamento de erros robusto é fundamental.
Erros comuns
Net::OpenTimeout: a conexão com o proxy ou com o servidor de destino expirou antes de ser estabelecida.Net::ReadTimeout: o servidor (proxy ou destino) não enviou dados dentro do tempo limite especificado.Errno::ECONNREFUSED: o servidor proxy recusou ativamente a conexão.Net::HTTPBadResponse: o servidor proxy ou o destino retornou uma resposta HTTP inválida.Mechanize::ResponseCodeError: erro específico do Mechanize quando o servidor de destino retorna um código de status HTTP que não seja 2xx (ex.: 403 Forbidden, 404 Not Found, 500 Internal Server Error).
Configuração de timeouts
Definir timeouts adequados evita que os scripts fiquem travados indefinidamente.
require 'net/http'
require 'uri'
uri = URI('http://slow-api.example.com')
proxy_host = 'your_proxy_host.com'
proxy_port = 8080
http = Net::HTTP.new(uri.host, uri.port, proxy_host, proxy_port)
http.open_timeout = 5 # Timeout para estabelecer a conexão (segundos)
http.read_timeout = 10 # Timeout para leitura de dados da conexão (segundos)
http.use_ssl = (uri.scheme == 'https')
request = Net::HTTP::Get.new(uri.request_uri)
begin
response = http.request(request)
puts "Response Status: #{response.code}"
rescue Net::OpenTimeout
puts "Connection to proxy or target timed out."
rescue Net::ReadTimeout
puts "Reading data from proxy or target timed out."
rescue Errno::ECONNREFUSED
puts "Connection refused by proxy or target server."
rescue StandardError => e
puts "An unexpected error occurred: #{e.message}"
end
# O Mechanize também suporta timeouts
# agent = Mechanize.new
# agent.set_proxy(proxy_host, proxy_port)
# agent.open_timeout = 5
# agent.read_timeout = 10
Boas práticas
- Strings de User-Agent: ao fazer scraping, sempre defina um cabeçalho
User-Agentrealista. Muitos sites bloqueiam requisições sem ele ou com valores genéricos. Ouser_agent_aliasdo Mechanize simplifica isso. - Cabeçalhos Referer: em alguns sites, um cabeçalho
Refererválido é necessário para imitar o comportamento de um navegador legítimo. - Gerenciamento de cookies: o Mechanize trata cookies automaticamente, o que é essencial para manter sessões através de um proxy. Com
Net::HTTP, o gerenciamento manual de cookies é necessário. - Tratamento de erros e retentativas: implemente lógica de retry com backoff exponencial para erros de rede transitórios ou problemas do proxy.
- Verificação de saúde dos proxies: antes de usar um proxy, considere um teste rápido contra um endpoint conhecido (ex.:
https://api.ipify.org) para verificar seu funcionamento e o endereço IP. - Gerenciamento de recursos: garanta que as conexões
Net::HTTPsejam fechadas corretamente, principalmente quando não estiver usando o blocostart, para evitar vazamento de recursos. O Mechanize gerencia as conexões internamente.
