Usare proxy in R con httr2 e rvest: guida pratica al web scraping resiliente

Una guida code-first per configurare proxy residenziali in R con httr2 (req_proxy), rvest e chromote. Include geo-targeting, sessioni sticky, rotazione IP, retry e compliance GDPR.

Using Proxies in R: A Code-First Guide with httr2 and rvest
In questo articolo

Se collezioni dati pubblici in R e improvvisamente le tue richieste restituiscono HTTP 403 o pagine vuote, il problema quasi mai è il codice: è l'IP. In questa guida pratica vediamo usare proxy in R con la stack moderna httr2 + rvest, configurando residential proxy rotanti, geo-targeting e sessioni sticky per scraping affidabile.

Perché serve un R proxy per il web scraping

I siti moderni usano bot detection basata su reputazione IP, TLS fingerprinting e rate limiting. Gli IP datacenter — quelli tipicamente usati dai provider cloud — hanno una reputazione bassissima perché associati a traffico automatizzato. Un residential proxy, invece, instrada le richieste attraverso IP assegnati a dispositivi consumer reali (ISP, mobile carrier), rendendo il tuo traffico indistinguibile da quello di un utente legittimo.

Secondo MDN, lo User-Agent da solo non basta più: i sistemi anti-bot come Cloudflare e Datadome combinano IP reputation, header order e comportamento. Per dati geo-restritti (es. listini regionali, SERP localizzate) un IP residenziale nella giusta nazione è spesso l'unico modo per ottenere la versione corretta della pagina.

Residential vs datacenter vs mobile: confronto rapido

TipoReputazione IPCostoLatency tipicaCasi d'uso
DatacenterBassaBasso ($0.5–2/GB)50–200 msAPI pubbliche, test, fetch non bloccati
ResidentialAltaMedio ($3–12/GB)200–800 msScraping SERP, e-commerce, geo-walled
MobileAltissimaAlto ($10–30/GB)500–2000 msSocial, app API, siti molto aggressivi

La stack moderna: httr2 + rvest

httr2 è il successore moderno di httr, con un'API pipeline basata su request() e metodi concatenabili. rvest resta lo standard per parsing HTML in R. La funzione chiave per i proxy è req_proxy(), che imposta proxy HTTP con autenticazione.

Ecco un esempio minimale di httr2 req_proxy con ProxyHat:

library(httr2)
library(rvest)

# ProxyHat residential HTTP proxy
proxy_url <- "http://gate.proxyhat.com:8080"
proxy_user <- "user-country-IT"
proxy_pass <- "la_tua_password"

req <- request("https://httpbin.org/ip") |>
  req_proxy(proxy_url, username = proxy_user, password = proxy_pass) |>
  req_user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") |>
  req_retry(max_tries = 3, max_seconds = 30)

resp <- req |> req_perform()
body <- resp |> resp_body_json()
print(body$origin)

Se l'IP restituito corrisponde a un ISP italiano, il geo-targeting funziona. req_retry() gestisce automaticamente i transient failures (5xx e connection errors).

Geo-targeting e sessioni sticky nel username

ProxyHat codifica geo-targeting e sessioni direttamente nel username, usando un formato con trattini. Questo vale sia per HTTP (porta 8080) sia per SOCKS5 (porta 1080).

# Geo-targeting: Regno Unito, Londra
proxy_user <- "user-country-GB-city-london"

# Sessione sticky: stesso IP per tutte le richieste di una sessione
proxy_user <- "user-session-abc123"

# Combinazione: geo + sessione
proxy_user <- "user-country-DE-city-berlin-session-r42"

Le sessioni sticky sono fondamentali quando il target usa CSRF token o carrelli che dipendono dall'IP. Per il paginated scraping, invece, spesso conviene ruotare la sessione ad ogni pagina per distribuire il carico e ridurre il rischio di rate limit.

SOCKS5 su porta 1080

Per scenari dove l'HTTP CONNECT non è sufficiente (es. alcuni firewall aziendali o target che ispezionano il TLS del proxy), ProxyHat espone SOCKS5 su gate.proxyhat.com:1080. httr2 supporta SOCKS5 via req_proxy() con URL socks5://:

req <- request("https://httpbin.org/ip") |>
  req_proxy("socks5://gate.proxyhat.com:1080",
           username = "user-country-FR",
           password = "la_tua_password") |>
  req_user_agent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)")

resp <- req |> req_perform()

Verifica la disponibilità SOCKS5 nel tuo piano ProxyHat sulla pagina pricing.

Esempio pratico: tabella paginata in tidy data frame

Vediamo un caso realistico: estrarre una tabella paginata (50 pagine, 20 righe ciascuna) ruotando sessione ad ogni pagina, con throttle e retry. Usiamo purrr::map_dfr per accumulare i risultati in un unico data frame.

library(httr2)
library(rvest)
library(purrr)
library(dplyr)
library(logger)

log_info("Avvio scraping paginato con proxy rotanti")

base_url <- "https://example.com/table?page="
proxy_pass <- Sys.getenv("PROXYHAT_PASS")

fetch_page <- function(page) {
  session_id <- paste0("sess-page-", page, "-", sample(1000:9999, 1))
  proxy_user <- paste0("user-country-IT-session-", session_id)

  req <- request(paste0(base_url, page)) |>
    req_proxy("http://gate.proxyhat.com:8080",
             username = proxy_user, password = proxy_pass) |>
    req_user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36") |>
    req_headers("Accept-Language" = "it-IT,it;q=0.9,en;q=0.8") |>
    req_retry(max_tries = 4, max_seconds = 60,
              backoff = ~ rnorm(1, mean = 2, sd = 0.5) * .x) |>
    req_throttle(2 / 60)  # max 2 richieste al minuto

  resp <- tryCatch(
    req_perform(req),
    error = function(e) {
      log_warn("Pagina {page} fallita: {conditionMessage(e)}")
      NULL
    }
  )

  if (is.null(resp)) return(NULL)

  html <- resp |> resp_body_html()
  tbl <- html |>
    html_element("table.data-table") |>
    html_table()

  if (is.null(tbl) || nrow(tbl) == 0) {
    log_warn("Nessuna tabella alla pagina {page}")
    return(NULL)
  }

  tbl$page <- page
  tbl
}

results <- map_dfr(1:50, fetch_page)
log_info("Righe totali estratte: {nrow(results)}")

Note di produzione:

  • req_throttle(2/60) limita a 2 richieste/minuto — adatto per target aggressivi. Per SERP scraping, 1 req/3–5 secondi è un buon default.
  • backoff con jitter esponenziale evita thundering herd quando più worker riprovano contemporaneamente.
  • Salva i risultati incrementalmente (es. write_csv ogni 10 pagine) per non perdere tutto in caso di crash.
  • Usa logger per tracciare success rate e latenza per pagina.

Pagine JS-rendered con read_html_live()

Se il target renderizza i dati via JavaScript (SPA, React, Vue), read_html() non basta. rvest >= 1.0.4 offre read_html_live(), che usa chromote (Chrome headless via DevTools Protocol).

Per instradare Chrome attraverso ProxyHat, passiamo --proxy-server alla sessione chromote:

library(rvest)
library(chromote)

# Avvia Chrome con proxy HTTP ProxyHat
# L'autenticazione avviene via header Proxy-Authorization gestito da Chrome
proxy_user <- "user-country-ES-session-js7"
proxy_pass <- Sys.getenv("PROXYHAT_PASS")
proxy_auth <- jsonlite::base64_encode(paste0(proxy_user, ":", proxy_pass))

b <- ChromoteSession$new(
  browser = ChromoteBrowser$new(
    args = c(
      "--proxy-server=http://gate.proxyhat.com:8080",
      "--disable-blink-features=AutomationControlled",
      "--no-sandbox"
    )
  )
)

# Inietta header Proxy-Authorization via CDP Network domain
b$Network$setExtraHTTPHeaders(headers = list(
  "Proxy-Authorization" = paste("Basic", proxy_auth)
))

page <- read_html_live("https://example.com/spa-table",
                     chrome = b,
                     wait = "table tbody tr")

tbl <- page |>
  html_elements("table tbody tr") |>
  map_dfr(~ {
    cells <- html_elements(.x, "td")
    tibble(
      nome = html_text(cells[[1]]),
      valore = html_text(cells[[2]])
    )
  })

print(head(tbl))
b$close()
Nota: read_html_live() è più lento (2–5s per pagina) e consuma più risorse. Usalo solo quando read_html() non restituisce i dati. Per pipeline Python/Node che condividono la stessa logica di rotazione, il ProxyHat SDK usa lo stesso pattern di username con flag country, city, session.

Header realistici e anti-fingerprinting

Oltre al proxy, gli header HTTP devono sembrare umani. Alcune regole pratiche:

  • Usa User-Agent coerenti con Accept-Language e sec-ch-ua (Chrome moderno).
  • Mantieni l'ordine degli header come nei browser reali (httr2 lo gestisce, ma evita header custom sospetti).
  • Non inviare X-Requested-With se non lo fa il browser legittimo.
  • Per pagine protette, aggiungi Referer coerente con il flusso di navigazione.
req <- request("https://shop.example.com/products") |>
  req_proxy("http://gate.proxyhat.com:8080",
           username = "user-country-IT-city-milan",
           password = proxy_pass) |>
  req_user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36") |>
  req_headers(
    "Accept" = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
    "Accept-Language" = "it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7",
    "Accept-Encoding" = "gzip, deflate, br",
    "Sec-Fetch-Dest" = "document",
    "Sec-Fetch-Mode" = "navigate",
    "Sec-Fetch-Site" = "none",
    "Upgrade-Insecure-Requests" = "1"
  )

Etica, compliance e best practice

Lo scraping non è illegale di per sé, ma ha confini legali precisi. Alcuni principi:

  • Preferisci API ufficiali. Se il target offre un'API (anche a pagamento), spesso è più affidabile e legalmente più sicura del scraping. Vedi la pagina su SERP tracking per approcci strutturati.
  • Rispetta robots.txt. Anche se non vincolante in tutte le giurisdizioni, ignorarlo è un segnale negativo in caso di disputa. La W3C robots.txt community mantiene la specifica de facto.
  • GDPR per dati personali. Se raccogli dati di persone nell'UE (nome, email, profili social), il GDPR si applica. Serve base giuridica (consenso, interesse legittimo) e rispetto dei diritti dell'interessato. La Commissione Europea fornisce orientamenti ufficiali.
  • Leggi i ToS. Molti siti vietano esplicitamente lo scraping; la violabilità dipende dalla giurisdizione (in USA, hiQ Labs v. LinkedIn ha parzialmente legitimizzato lo scraping di dati pubblici, ma la giurisprudenza è fluida).
  • Rate limiting responsabile. Non saturare il target: req_throttle() con 1–2 req/sec è raramente problematico.

Per approfondire i casi d'uso legittimi, consulta la pagina web scraping e l'elenco delle locazioni proxy disponibili.

Key takeaways

  • httr2 + rvest è la stack moderna per il web scraping in R; req_proxy() è il modo idiomatico per configurare proxy HTTP/SOCKS5.
  • I residential proxy sono necessari per target bloccati o geo-walled; i datacenter IP vengono filtrati dalla maggior parte dei sistemi anti-bot moderni.
  • Geo-targeting e sessioni sticky si codificano nel username ProxyHat (user-country-XX-city-yy-session-zz), non nell'URL.
  • Per paginazione, ruota la sessione ad ogni pagina con purrr::map, req_retry() e req_throttle() per resilienza.
  • Per pagine JS-rendered, usa read_html_live() con chromote e --proxy-server.
  • Rispetta robots.txt, GDPR e ToS; preferisci API ufficiali quando disponibili.

FAQ

Cos'è usare proxy in R?

Significa instradare le richieste HTTP/SOCKS5 generate da codice R (tipicamente con httr2, httr o curl) attraverso un server proxy che nasconde l'IP originario e presenta un IP diverso al target. In R, la funzione idiomatica è httr2::req_proxy(), che accetta URL, username e password per l'autenticazione.

Perché i proxy importano per il web scraping in R?

Perché i siti moderni bloccano IP datacenter e rate-limitano per IP. Senza proxy, dopo poche decine di richieste ricevi 403/429. I residential proxy distribuiscono il traffico su IP consumer reali, riducendo il tasso di blocco dal 80–100% a sotto il 5% su target moderatamente protetti.

Quale tipo di proxy funziona meglio per rvest proxy?

Dipende dal target. Per SERP e e-commerce con protezione moderata, i residential proxy offrono il miglior rapporto costo/affidabilità ($3–12/GB). Per social e app molto aggressive, i mobile proxy hanno reputazione massima ma costano $10–30/GB. I datacenter vanno bene solo per API pubbliche senza anti-bot.

Come evitare blocchi usando proxy in R?

Combina quattro elementi: (1) residential proxy con rotazione per richiesta o sessione; (2) header realistici (req_user_agent(), Accept-Language, sec-ch-ua); (3) throttle con req_throttle() a 1–2 req/sec; (4) retry con backoff esponenziale via req_retry(). Evita pattern di accesso prevedibili (stesso User-Agent per migliaia di richieste, orari fissi).

Posso usare SOCKS5 con httr2?

Sì. req_proxy() accetta URL con schema socks5://. Su ProxyHat, SOCKS5 è disponibile su gate.proxyhat.com:1080, mentre HTTP usa la porta 8080. L'autenticazione (username con geo/session flags + password) è identica per entrambi i protocolli.

Domande frequenti

Cos'è usare proxy in R?

Significa instradare le richieste HTTP/SOCKS5 generate da codice R (tipicamente con httr2, httr o curl) attraverso un server proxy che nasconde l'IP originario e presenta un IP diverso al target. In R, la funzione idiomatica è httr2::req_proxy(), che accetta URL, username e password per l'autenticazione.

Perché i proxy importano per il web scraping in R?

Perché i siti moderni bloccano IP datacenter e rate-limitano per IP. Senza proxy, dopo poche decine di richieste ricevi 403/429. I residential proxy distribuiscono il traffico su IP consumer reali, riducendo il tasso di blocco dall'80–100% a sotto il 5% su target moderatamente protetti.

Quale tipo di proxy funziona meglio per rvest proxy?

Dipende dal target. Per SERP e e-commerce con protezione moderata, i residential proxy offrono il miglior rapporto costo/affidabilità ($3–12/GB). Per social e app molto aggressive, i mobile proxy hanno reputazione massima ma costano $10–30/GB. I datacenter vanno bene solo per API pubbliche senza anti-bot.

Come evitare blocchi usando proxy in R?

Combina quattro elementi: (1) residential proxy con rotazione per richiesta o sessione; (2) header realistici (req_user_agent(), Accept-Language, sec-ch-ua); (3) throttle con req_throttle() a 1–2 req/sec; (4) retry con backoff esponenziale via req_retry(). Evita pattern di accesso prevedibili (stesso User-Agent per migliaia di richieste, orari fissi).

Posso usare SOCKS5 con httr2?

Sì. req_proxy() accetta URL con schema socks5://. Su ProxyHat, SOCKS5 è disponibile su gate.proxyhat.com:1080, mentre HTTP usa la porta 8080. L'autenticazione (username con geo/session flags + password) è identica per entrambi i protocolli.

Verifica la tua configurazione proxy in pochi secondi

Verificatore di proxy gratuito — conferma che i tuoi IP siano veloci, anonimi e non bloccati.

Controlla i proxy gratis
← Torna al Blog