Se você já tentou coletar dados de um site e recebeu um HTTP 403 após a décima requisição, sabe a frustração. Usar proxies em R é a diferença entre um script que roda uma vez e um pipeline de dados que sustenta centenas de requisições por hora. Este guia mostra como configurar httr2 e rvest com proxies residenciais, geo-targeting e sessões sticky — tudo com código executável e armadilhas de produção.
Por que usar proxies em R é essencial para coleta de dados
Quando você faz uma requisição HTTP a partir do R, o servidor de destino vê o IP da sua máquina ou do seu provedor de internet. Sites com proteção anti-bot (Cloudflare, Akamai, DataDome) classificam esse IP em milissegundos: se for um IP de datacenter conhecido (AWS, DigitalOcean, OVH), a requisição frequentemente é bloqueada antes mesmo de chegar ao conteúdo. Segundo a Cloudflare, mais de 40% do tráfego web em 2023 foi classificado como automatizado ou malicioso — o que significa que filtros anti-bot estão cada vez mais agressivos.
Proxies residenciais resolvem esse problema roteando seu tráfego por IPs atribuídos a provedores reais de internet (ISPs). O servidor alvo enxerga um visitante residencial comum, não um scraper em um datacenter. Para fontes com restrição geográfica — um e-commerce que mostra preços diferentes por país, ou um portal governamental acessível apenas de dentro da UE — o geo-targeting no nome de usuário do proxy garante que o IP de saída venha da localização correta.
O pacote httr2, sucessor moderno do httr, oferece a função req_proxy() que torna a configuração trivial. Combinado com rvest para parsing HTML, você tem um stack elegante e tipado para coleta de dados em R. Veja mais casos de uso em web scraping com proxies.
O stack moderno: httr2 + rvest + proxies
Instalação e dependências
Antes de começar, instale os pacotes necessários:
install.packages(c("httr2", "rvest", "purrr", "dplyr", "tibble", "chromote"))
O httr2 gerencia requisições HTTP com uma API funcional encadeável. O rvest faz parsing de HTML/XML. O purrr fornece map() para iteração funcional. O chromote controla um navegador headless para páginas JavaScript.
Requisição básica com proxy via httr2
O padrão fundamental é: criar um request, aplicar req_proxy(), executar com req_perform(). Veja o exemplo:
library(httr2)
library(rvest)
# Credenciais ProxyHat
proxy_user <- "user-country-US"
proxy_pass <- "sua_senha_aqui"
# Construir e executar requisição
resp <- request("https://httpbin.org/ip") |>
req_proxy("http://gate.proxyhat.com:8080", proxy_user, proxy_pass) |>
req_user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") |>
req_perform()
# Verificar IP de saída
resp |> resp_body_json()
# $origin
# [1] "185.230.x.x" # IP residencial dos EUA
A função req_proxy() aceita a URL do gateway, usuário e senha. O gateway gate.proxyhat.com na porta 8080 é o ponto de entrada HTTP. Todo o tráfego subsequente passa pelo IP residencial atribuído.
Parsing HTML com rvest
Depois de obter a resposta, use resp_body_string() e read_html() do rvest para extrair conteúdo:
library(httr2)
library(rvest)
fetch_page <- function(url, user, pass) {
resp <- request(url) |>
req_proxy("http://gate.proxyhat.com:8080", user, pass) |>
req_user_agent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36") |>
req_headers("Accept-Language" = "en-US,en;q=0.9") |>
req_retry(max_tries = 3, backoff = ~ 2^\.x) |>
req_perform()
resp |>
resp_body_string() |>
read_html()
}
# Extrair títulos de uma página
html <- fetch_page(
"https://books.toscrape.com/catalogue/page-1.html",
"user-country-GB-city-london",
"sua_senha_aqui"
)
titles <- html |>
html_elements("h3 a") |>
html_text2()
print(titles[1:5])
Aqui já vemos dois recursos importantes: req_retry() com backoff exponencial para falhas transitórias, e o geo-targeting user-country-GB-city-london no nome de usuário, que faz o gateway atribuir um IP residencial em Londres.
Geo-targeting e sessões sticky no nome de usuário
O ProxyHat codifica parâmetros de roteamento diretamente no nome de usuário. Isso evita cabeçalhos customizados e funciona com qualquer cliente HTTP. As flags são separadas por hífen:
| Flag | Exemplo | Descrição |
|---|---|---|
country-XX | user-country-DE | IP residencial na Alemanha |
city-name | user-country-US-city-newyork | IP em cidade específica |
session-ID | user-session-abc123 | Sessão sticky (mesmo IP) |
Combine flags livremente: user-country-FR-city-paris-session-cart7 mantém o mesmo IP parisiense durante toda a sessão de checkout — essencial para fluxos multi-etapa onde o IP precisa ser consistente.
Sessões sticky vs rotação por requisição
Para scraping de páginas independentes (cada URL é uma página de produto, por exemplo), rotação por requisição é ideal: cada chamada recebe um IP novo, distribuindo o risco. Para fluxos que exigem continuidade (login, navegação por categorias, carrinho de compras), sessões sticky mantêm o mesmo IP:
library(httr2)
# Rotação por requisição (IP novo a cada call)
fetch_rotating <- function(url) {
request(url) |>
req_proxy("http://gate.proxyhat.com:8080",
"user-country-US",
"sua_senha_aqui") |>
req_perform()
}
# Sessão sticky (mesmo IP em todas as calls)
sticky_user <- paste0("user-country-US-session-",
digest::digest(Sys.time(), algo = "md5"))
fetch_sticky <- function(url) {
request(url) |>
req_proxy("http://gate.proxyhat.com:8080",
sticky_user,
"sua_senha_aqui") |>
req_perform()
}
SOCKS5 na porta 1080
Para casos onde o HTTP CONNECT não é suficiente — túneis UDP, ou ambientes que exigem SOCKS — o ProxyHat oferece SOCKS5 na porta 1080:
library(httr2)
resp <- request("https://httpbin.org/ip") |>
req_proxy("socks5://gate.proxyhat.com:1080",
"user-country-JP",
"sua_senha_aqui") |>
req_perform()
resp |> resp_body_json()
O esquema socks5:// no primeiro argumento de req_proxy() é suficiente para o httr2 negociar o túnel apropriadamente.
Proxies residenciais vs datacenter: quando usar cada um
A escolha entre residencial e datacenter depende do alvo. A tabela abaixo resume as diferenças:
| Característica | Residencial | Datacenter |
|---|---|---|
| Origem do IP | ISPs reais | Provedores de cloud |
| Detecção por anti-bot | Baixa | Alta |
| Latência típica | 200–800ms | 50–150ms |
| Custo por GB | Maior | Menor |
| Ideal para | SERP, e-commerce, social | APIs públicas, dados abertos |
Para SERP tracking e monitoramento de preços em e-commerce, residenciais são praticamente obrigatórios. Para APIs documentadas que apenas limitam por rate, datacenter é mais eficiente. Veja nossa página de localizações de proxy para cobertura detalhada.
Exemplo prático: tabela paginada em data frame tidy
Vamos construir um exemplo completo: raspar uma tabela paginada, rotacionando sessões por página, com retries e throttle. O alvo é books.toscrape.com, um site de teste mantido pela comunidade de scraping.
library(httr2)
library(rvest)
library(purrr)
library(dplyr)
library(tibble)
# Configuração do proxy
PROXY_HOST <- "http://gate.proxyhat.com:8080"
PROXY_PASS <- "sua_senha_aqui"
# Throttle global: máximo 2 requisições por segundo
throttle <- req_throttle(2 / 1)
# Função para raspar uma página
scrape_page <- function(page_num) {
url <- sprintf("https://books.toscrape.com/catalogue/page-%d.html", page_num)
# Sessão única por página (rotação de IP)
session_id <- paste0("sess-p", page_num)
proxy_user <- paste0("user-country-GB-session-", session_id)
tryCatch({
html <- request(url) |>
req_proxy(PROXY_HOST, proxy_user, PROXY_PASS) |>
req_user_agent(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
) |>
req_headers("Accept-Language" = "en-GB,en;q=0.9") |>
req_retry(max_tries = 4, backoff = ~ 2^\.x) |>
throttle() |>
req_perform() |>
resp_body_string() |>
read_html()
# Extrair tabela de livros
df <- html |>
html_elements("article.product_pod") |>
map_dfr(function(article) {
tibble(
title = article |>
html_element("h3 a") |>
html_attr("title"),
price = article |>
html_element(".price_color") |>
html_text2() |>
gsub("[£A-Z]", "", x = _) |>
as.numeric(),
rating = article |>
html_element(".star-rating") |>
html_attr("class") |>
strsplit(" ") |> unlist() |> tail(1),
availability = article |>
html_element(".instock") |>
html_text2() |>
trimws()
)
})
message(sprintf("Página %d: %d livros extraídos", page_num, nrow(df)))
df
}, error = function(e) {
warning(sprintf("Falha na página %d: %s", page_num, conditionMessage(e)))
tibble()
})
}
# Raspar 10 páginas com iteração funcional
all_books <- 1:10 |>
map_dfr(scrape_page)
# Resultado tidy
print(all_books)
# # A tibble: 200 × 4
# title price rating availability
# <chr> <dbl> <chr> <chr>
# 1 A Light in the Attic 51.77 Three In stock
# 2 Tipping the Velvet 53.74 One In stock
# ...
Observe os elementos de resiliência:
req_retry(max_tries = 4, backoff = ~ 2^\.x): até 4 tentativas com backoff exponencial (1s, 2s, 4s, 8s).req_throttle(2 / 1): máximo 2 requisições por segundo, evitando sobrecarregar o alvo.tryCatch(): captura erros por página, retornando umtibblevazio em vez de abortar toda a coleta.- Sessão única por página: cada página usa um IP diferente, distribuindo o risco de bloqueio.
Com 10 páginas a 2 req/s, a coleta completa leva aproximadamente 5 segundos — bem dentro de limites razoáveis. Para volumes maiores, aumente req_throttle proporcionalmente e considere concorrência com furrr::future_map_dfr().
Páginas JavaScript com read_html_live() e chromote
Muitos sites modernos renderizam conteúdo via JavaScript — React, Vue, SPAs. O rvest oferece read_html_live(), que usa o chromote para controlar um Chrome headless. Para passar o proxy pelo navegador, configure a flag --proxy-server:
library(rvest)
library(chromote)
# Configurar Chrome headless com proxy ProxyHat
proxy_url <- "gate.proxyhat.com:8080"
# Criar sessão do chromote com proxy
b <- ChromoteSession$new(
chromote = Chromote$new(
browser = Chrome$new(
args = c(
"--headless",
"--disable-gpu",
"--no-sandbox",
paste0("--proxy-server=http://", proxy_url)
)
)
)
)
# Navegar para a página
b$Page$navigate("https://quotes.toscrape.com/js/")
b$Page$loadEventFired()
# Obter HTML renderizado
html_content <- b$Runtime$evaluate(
"document.documentElement.outerHTML"
)$result$value
html <- read_html(html_content)
quotes <- html |>
html_elements(".quote .text") |>
html_text2()
print(quotes[1:3])
b$close()
Nota: O
read_html_live()do rvest ainda não expõe configuração de proxy diretamente. A abordagem acima usachromotecom a flag--proxy-serverpara o mesmo efeito. Para autenticação de proxy no Chrome, use uma extensão ou o formatouser:pass@gate.proxyhat.com:8080na flag.
Para autenticação no proxy via Chrome headless, uma alternativa é usar um proxy local que injeta credenciais:
# Alternativa: usar httr2 para páginas que não precisam de JS
# e chromote apenas quando necessário
library(httr2)
library(rvest)
# Para páginas estáticas (90% dos casos), httr2 + rvest é suficiente
# e muito mais rápido que um navegador headless
fetch_with_headers <- function(url) {
request(url) |>
req_proxy("http://gate.proxyhat.com:8080",
"user-country-US",
"sua_senha_aqui") |>
req_user_agent(
"Mozilla/5.0 (X11; Linux x86_64; rv:121.0) Gecko/20100101 Firefox/121.0"
) |>
req_headers(
"Accept" = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language" = "en-US,en;q=0.5",
"Accept-Encoding" = "gzip, deflate, br",
"DNT" = "1",
"Connection" = "keep-alive",
"Upgrade-Insecure-Requests" = "1"
) |>
req_retry(max_tries = 3, backoff = ~ 2^\.x) |>
req_perform() |>
resp_body_string() |>
read_html()
}
Boas práticas e armadilhas de produção
1. Sempre defina um User Agent realista
O user agent padrão do httr2 revela que você está usando R/curl. Use um browser real:
ua <- "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
request(url) |>
req_user_agent(ua) |>
# ...
2. Respeite robots.txt e ToS
Antes de raspar, verifique o robots.txt do site. O pacote robotstxt no R facilita isso:
# install.packages("robotstxt")
library(robotstxt)
paths_allowed("https://books.toscrape.com")
# [1] TRUE
Se o site oferece uma API oficial, prefira-a. APIs são mais estáveis, mais rápidas e não violam ToS. O W3C e a FTC publicam orientações sobre coleta ética de dados.
3. GDPR e dados pessoais
Se você coleta dados de sujeitos na UE, o GDPR se aplica. Dados pessoais (nome, email, IP) exigem base legal. Dados públicos agregados (preços, estatísticas) geralmente são seguros, mas consulte um advogado para casos de borda. A Comissão Europeia detalha as obrigações.
4. Logging e observabilidade
Em produção, registre cada requisição com timestamp, status code e tempo de resposta:
library(httr2)
# Callback de logging com req_perform()
log_request <- function(req) {
start <- Sys.time()
resp <- req_perform(req)
elapsed <- as.numeric(difftime(Sys.time(), start, units = "secs"))
message(sprintf(
"[%s] %s -> %d (%.2fs)",
format(Sys.time(), "%Y-%m-%d %H:%M:%S"),
req$url,
resp_status_code(resp),
elapsed
))
resp
}
# Uso
resp <- request("https://httpbin.org/ip") |>
req_proxy("http://gate.proxyhat.com:8080",
"user-country-DE",
"sua_senha_aqui") |>
log_request()
5. Circuit breaker para falhas em cascata
Se 5 requisições consecutivas falham com HTTP 403, pause o coletor por 60 segundos antes de tentar novamente:
circuit_breaker <- function(max_failures = 5, cooldown = 60) {
failures <- 0
last_failure <- NULL
function(resp) {
status <- resp_status_code(resp)
if (status == 403 || status == 429) {
failures <<- failures + 1
last_failure <<- Sys.time()
if (failures >= max_failures) {
message(sprintf("Circuit breaker acionado. Pausando %ds...", cooldown))
Sys.sleep(cooldown)
failures <<- 0
}
stop(sprintf("HTTP %d recebido", status))
} else if (status < 400) {
failures <<- 0
}
resp
}
}
# Uso em pipeline
breaker <- circuit_breaker()
resp <- request("https://exemplo.com") |>
req_proxy("http://gate.proxyhat.com:8080",
"user-country-US",
"sua_senha_aqui") |>
req_perform() |>
breaker()
ProxyHat SDK para pipelines mistos (Python/Node/R)
Se sua equipe mantém pipelines em múltiplas linguagens — R para análise, Python para ETL, Node.js para APIs — o ProxyHat usa o mesmo padrão de gateway em todas. A configuração gate.proxyhat.com:8080 com flags no nome de usuário é idêntica em qualquer cliente HTTP. Consulte a documentação oficial do ProxyHat para exemplos em Python e Node.js.
Para preços e planos, veja a página de preços do ProxyHat. Para casos de uso específicos de SERP, consulte SERP tracking com proxies.
Key Takeaways
- Use
req_proxy()do httr2 para rotear requisições HTTP pelo gatewaygate.proxyhat.com:8080— é a forma mais limpa de configurar proxy em R. - Geo-targeting vai no nome de usuário:
user-country-GB-city-londonatribui um IP residencial em Londres sem cabeçalhos extras. - Sessões sticky com
user-session-IDmantêm o mesmo IP para fluxos multi-etapa; rotação por requisição para páginas independentes. - Combine
req_retry()ereq_throttle()para resiliência: backoff exponencial em falhas e rate limiting para não sobrecarregar o alvo. - Proxies residenciais superam datacenter para fontes com anti-bot, mas têm latência maior (200–800ms vs 50–150ms).
- Para páginas JS, use
chromotecom--proxy-server; para 90% dos casos, httr2 + rvest é suficiente e mais rápido. - Respeite robots.txt, ToS e GDPR: colete apenas dados públicos legítimos e prefira APIs oficiais quando disponíveis.
FAQ
Como usar proxies em R com httr2?
Use a função req_proxy() do httr2 passando a URL do gateway (http://gate.proxyhat.com:8080), usuário e senha. Encadeie com request() e req_perform() para rotear todo o tráfego HTTP pelo proxy. Você também pode adicionar flags de geo-targeting no nome de usuário, como user-country-US.
Qual tipo de proxy funciona melhor para web scraping em R?
Proxies residenciais são os mais eficazes para fontes que bloqueiam IPs de datacenter, pois usam IPs de provedores reais de internet. Para fontes menos restritivas (APIs públicas, dados abertos), proxies de datacenter oferecem menor latência e custo. Proxies móveis são ideais para alvos com proteção anti-bot agressiva, como redes sociais.
Como evitar bloqueios ao raspar dados com proxies em R?
Combine rotação de sessões sticky, throttling com req_throttle(), retries com req_retry(), user agents realistas e respeito ao robots.txt. Distribua requisições em horários diferentes e use geo-targeting apropriado para a fonte. Implemente um circuit breaker para pausar automaticamente após falhas consecutivas.
Como configurar geo-targeting em proxies no R?
Incorpore flags de geo-targeting no nome de usuário, como user-country-GB-city-london. O gateway gate.proxyhat.com interpreta essas flags e atribui um IP residencial na localização especificada, essencial para conteúdo com restrição geográfica como preços regionais e portais governamentais.






