Proxys in R verwenden: Warum und wie
Wenn Sie in R Daten aus dem Web sammeln, stoßen Sie früher oder später auf IP-basierte Sperren, Rate-Limits oder Geo-Walls. Proxys in R sind dann nicht optional, sondern Voraussetzung für zuverlässige Extraktion. Die moderne Kombination aus httr2 für HTTP-Anfragen und rvest für HTML-Parsing bietet eine saubere, pipeline-fähige API, in die sich Proxy-Konfigurationen elegant integrieren lassen.
In diesem Leitfaden zeigen wir, wie Sie R proxy-Verbindungen mit req_proxy() aufbauen, Geo-Targeting und Sticky Sessions im Benutzernamen kodieren, paginierte Tabellen resilient scrapen und sogar JavaScript-gerenderte Seiten über denselben Proxy laden. Alle Beispiele nutzen ProxyHat unter gate.proxyhat.com:8080 (HTTP) bzw. :1080 (SOCKS5).
Technischer Kontext: Warum Residential Proxys statt Datacenter-IPs
Die meisten Anti-Bot-Systeme klassifizieren IP-Adressen nach ASN-Typ. Datacenter-IPs stammen aus Bloecken von Cloud- oder Hosting-Providern (z. B. AWS, Hetzner, OVH) und werden von Systemen wie Cloudflare oder PerimeterX mit hoher Wahrscheinlichkeit als automatisiert eingestuft. Residential-IPs gehören echten ISPs und sind deutlich schwerer zu unterscheiden von menschlichem Traffic. Laut einer Datadome-Analyse werden über 40 % aller automatisierten Anfragen an geschützte Websites blockiert, bevor sie eine HTML-Antwort erreichen – der Großteil davon basiert auf IP-Reputation allein.
Wenn eine Website Geo-Beschränkungen durchsetzt (z. B. nur Inhalte für GB-Nutzer sichtbar), hilft nur ein Proxy mit Geo-Targeting. Mit ProxyHat kodieren Sie Land und Stadt direkt im Benutzernamen:
# Username-Pattern für Geo-Targeting
# Format: user-country-{CC}-city-{city}
user <- 'user-country-GB-city-london'
pass <- 'ihr_passwort'
proxy_url <- paste0('http://', user, ':', pass, '@gate.proxyhat.com:8080')
Für Sticky Sessions, die über mehrere Anfragen dieselbe Exit-IP halten, fügen Sie eine Session-ID hinzu:
# Sticky Session für 10 Minuten (Standard-TTL)
user <- 'user-country-DE-session-abc123'
pass <- 'ihr_passwort'
# Diese IP bleibt für aufeinanderfolgende Requests konstant
Die moderne R-Stack: httr2 request() und req_proxy()
httr2 ist der Nachfolger von httr und baut auf einer Pipeline-Architektur: Sie erstellen ein request-Objekt, fügen schrittweise Header, Retry-Logik und Proxy-Konfiguration hinzu und führen es dann aus. Die Funktion req_proxy() akzeptiert Host, Port sowie optionale Zugangsdaten.
library(httr2)
library(rvest)
# Basis-Request mit Proxy
req <- request('https://httpbin.org/ip') %>%
req_proxy('gate.proxyhat.com', 8080,
username = 'user-country-DE',
password = 'ihr_passwort') %>%
req_user_agent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36') %>%
req_timeout(30)
resp <- req %>% req_perform()
body <- resp_body_string(resp)
cat(body)
# {"origin": "91.x.x.x"} -- Ihre Residential-Exit-IP
Das req_proxy()-Statement setzt den Proxy-Authorization-Header automatisch (Basic Auth, Base64-kodiert). Sie müssen sich nicht manuell um Header-Formatierung kümmern. Für SOCKS5 wechseln Sie lediglich den Port:
# SOCKS5 auf Port 1080
req_socks <- request('https://httpbin.org/ip') %>%
req_proxy('gate.proxyhat.com', 1080,
username = 'user-country-DE',
password = 'ihr_passwort')
# httr2 erkennt das Protokoll nicht automatisch;
# für SOCKS5 verwenden Sie die URL-Form in curl:
req_socks <- request('https://httpbin.org/ip') %>%
req_options(proxy = 'socks5://user-country-DE:ihr_passwort@gate.proxyhat.com:1080')
Vergleich: Residential vs Datacenter vs Mobile
| Eigenschaft | Residential | Datacenter | Mobile |
|---|---|---|---|
| IP-Reputation | Hoch (echter ISP) | Niedrig (Hosting-ASN) | Sehr hoch (Mobilfunk) |
| Blockrate bei geschützten Seiten | ~5–15 % | ~40–70 % | <5 % |
| Latenz | 200–800 ms | 50–150 ms | 500–2000 ms |
| Preis (ungefähr) | Mittel | Niedrig | Hoch |
| Einsatzzweck | Breites Scraping, Geo-Targeting | Bulk, unkritische Quellen | Mobile-First-Websites |
Paginierte Tabelle scrapen mit purrr und rvest
Das folgende Beispiel scrapt eine paginierte Tabelle (z. B. ein öffentliches Verzeichnis), rotiert pro Seite durch eine neue Session-ID und kombiniert req_retry() mit req_throttle() für Resilienz. Das Ziel ist ein tibble mit sauberen Spaltennamen.
library(httr2)
library(rvest)
library(purrr)
library(dplyr)
library(stringr)
base_url <- 'https://example.com/table?page='
fetch_page <- function(page_num) {
session_id <- paste0('sess-page-', page_num, '-', as.integer(Sys.time()))
url <- paste0(base_url, page_num)
req <- request(url) %>%
req_proxy('gate.proxyhat.com', 8080,
username = paste0('user-country-DE-session-', session_id),
password = 'ihr_passwort') %>%
req_user_agent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36') %>%
req_headers('Accept-Language' = 'de-DE,de;q=0.9') %>%
req_retry(max_tries = 3, max_seconds = 60,
retry_on = c(403, 429, 500, 502, 503)) %>%
req_throttle(2 / 1) # max 2 Anfragen pro Sekunde
resp <- tryCatch(
req_perform(req),
error = function(e) {
message('Fehler auf Seite ', page_num, ': ', conditionMessage(e))
return(NULL)
}
)
if (is.null(resp)) return(NULL)
html <- resp_body_string(resp) %>% read_html()
# Tabelle extrahieren
tbl <- html %>%
html_element('table.data-table') %>%
html_table()
# Spaltennamen bereinigen
names(tbl) <- names(tbl) %>%
str_to_lower() %>%
str_replace_all(' ', '_')
tbl
}
# 10 Seiten abrufen
pages <- 1:10
results <- map(pages, fetch_page)
combined <- bind_rows(results)
glimpse(combined)
Die req_throttle(2 / 1)-Anweisung limitiert auf 2 Requests pro Sekunde – eine bewährte Rate für mittelfrequentes Scraping. Die retry_on-Liste deckt die häufigsten transienten Fehler ab. Durch die pro-Seite generierte Session-ID rotiert die Exit-IP, ohne dass Sie den Proxy-Endpoint manuell wechseln müssen.
JavaScript-gerenderte Seiten mit read_html_live()
Viele moderne Seiten laden Inhalte erst per JavaScript nach. rvest bietet ab Version 1.0.0 die Funktion read_html_live(), die auf chromote basiert und einen echten Chrome-Browser headless steuert. Sie können den Proxy direkt an chromote übergeben:
library(rvest)
# Proxy für chromote/Chrome konfigurieren
# chromote liest die Umgebungsvariable CHROMOTE_CHROME_ARGS
Sys.setenv(CHROMOTE_CHROME_ARGS = paste0(
'--proxy-server=http://gate.proxyhat.com:8080'
))
# Für authentifizierte Proxys benötigt Chrome eine Erweiterung oder
# die curl-basierte Alternative über headless Chrome mit --proxy-auth
# Einfacher: Proxy-Auth in die URL kodieren über eine kleine Hilfsfunktion
page <- read_html_live('https://example.com/js-app',
timeout_ms = 30000)
# Warten bis Content geladen ist
Sys.sleep(3)
# Dynamisch geladene Elemente extrahieren
titles <- page %>%
html_elements('.dynamic-title') %>%
html_text2()
head(titles)
Für authentifizierte HTTP-Proxys mit Chrome-Browser ist der sauberste Weg die Nutzung von --proxy-auth über eine Chrome-Erweiterung oder der Wechsel auf polite-basierte Headless-Lösungen. In der Praxis kombinieren viele Teams read_html_live() für JS-Rendering mit einem vorgeschalteten Proxy-Gateway, das die Authentifizierung übernimmt.
Realistische Header und Anti-Fingerprinting
Ein Proxy allein reicht nicht, wenn Ihre Header-Fingerabdrücke auf Automatisierung hindeuten. Setzen Sie immer einen realistischen User-Agent, Accept-Language und Referer:
req <- request('https://example.com') %>%
req_proxy('gate.proxyhat.com', 8080,
username = 'user-country-GB-city-london',
password = 'ihr_passwort') %>%
req_user_agent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36') %>%
req_headers(
'Accept' = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language' = 'en-GB,en;q=0.9',
'Accept-Encoding' = 'gzip, deflate, br',
'Referer' = 'https://www.google.com/',
'Sec-Fetch-Dest' = 'document',
'Sec-Fetch-Mode' = 'navigate',
'Sec-Fetch-Site' = 'cross-site',
'Upgrade-Insecure-Requests' = '1'
) %>%
req_timeout(25) %>%
req_retry(max_tries = 4, retry_on = c(403, 429, 503))
Die Mozilla Developer Network-Dokumentation zu HTTP-Headern bietet eine vollständige Referenz. Achten Sie darauf, dass Accept-Language zum Geo-Targeting passt: Wenn Sie über eine GB-IP kommen, sollte die Sprache en-GB sein, nicht de-DE.
Häufige Fehler und Edge Cases
- Vergessene Proxy-Authentifizierung:
req_proxy()ohneusername/passwordsendet keine Credentials – Sie erhalten 407 Proxy Authentication Required. - Zu aggressive Raten: Ohne
req_throttle()feuern Sie Anfragen so schnell wie R sie absenden kann. 100+ Requests/s aus einer IP sind ein sofortiges Flag. - Kein Retry bei 429: Viele Sites senden 429 mit einem
Retry-After-Header.req_retry()wertet diesen automatisch aus. - Encoding-Probleme:
html_table()liefert manchmallist-Spalten bei verschachtelten Zellen. Verwenden Siemutate(across(everything(), as.character))als Sicherheitsnetz. - Session-ID-Kollision: Wenn mehrere Worker dieselbe Session-ID verwenden, teilen sie dieselbe Exit-IP. Generieren Sie IDs pro Worker oder pro Request.
- SSL-Zertifikatsfehler bei SOCKS5: Verwenden Sie
req_options(ssl_verifypeer = 1)und stellen Sie sicher, dasslibcurlmit aktueller CA-Bundle kompiliert ist.
Ethik und Compliance
Web Scraping ist legal, wenn Sie öffentliche Daten sammeln und die Nutzungsbedingungen sowie robots.txt respektieren. Für EU-Datensubjekte gilt die DSGVO: Personenbezogene Daten dürfen nicht ohne Rechtsgrundlage verarbeitet werden. Prüfen Sie immer:
- Gibt es eine offizielle API? Bevorzugen Sie diese gegenüber HTML-Scraping.
- Was erlaubt
robots.txt? Respektieren SieDisallow-Anweisungen. - Sind die Daten personenbezogen? Wenn ja, benötigen Sie eine Rechtsgrundlage (Art. 6 DSGVO).
- Haben die Nutzungsbedingungen Scraping-Verbote? Einige sind durchsetzbar, andere nicht – konsultieren Sie bei Unsicherheit einen Anwalt.
Für SERP-Tracking und Price-Monitoring sind Residential Proxys die Standardwahl. ProxyHat bietet weltweit verteilte Exit-IPs und flexible Preismodelle. Für gemischte Pipelines (R + Python/Node) können Sie denselben Gateway verwenden – das ProxyHat SDK spiegelt dasselbe Username-Pattern in Python und Node.js wider.
Key Takeaways
- httr2 + req_proxy() ist der sauberste Weg, Proxys in R zu konfigurieren – ohne manuelle Header-Kodierung.
- Geo-Targeting und Sticky Sessions werden über den Username kodiert (
user-country-GB-city-london-session-xyz).- Residential Proxys reduzieren Blockraten von 40–70 % (Datacenter) auf 5–15 % bei geschützten Quellen.
- req_retry() + req_throttle() sind Pflicht für produktive Scraping-Pipelines – kombinieren Sie beide.
- read_html_live() via chromote für JS-gerenderte Seiten; übergeben Sie den Proxy als Chrome-Flag.
- Ethik zuerst: Bevorzugen Sie APIs, respektieren Sie robots.txt und DSGVO.
Weitere Anleitungen finden Sie in unseren Use-Cases für Web Scraping und SERP Tracking.






