Dlaczego korzystanie z proxy w R jest kluczowe dla web scrapingu
Korzystanie z proxy w R to fundament każdej solidnej strategii zbierania danych. Kiedy wysyłasz żądania HTTP bezpośrednio ze swojego adresu IP, strony docelowe mogą Cię zablokować, ograniczyć lub serwować fałszywe dane. Problem dotyczy zarówno analityków pobierających ceny z e-commerce, jak i badaczy scrapujących wyniki wyszukiwania. R jako język statystyczny nie posiada wbudowanego mechanizmu rotacji IP, dlatego musisz polegać na zewnętrznym serwerze proxy.
W tym przewodniku pokażemy, jak skonfigurować R proxy z nowoczesnym stosem httr2 i rvest, jak używać residential proxy do omijania blokad geograficznych oraz jak zbudować odporny pipeline pobierania danych z paginacją, retry i throttle. Wszystkie przykłady używają ProxyHat jako bramy proxy.
Techniczne tło: dlaczego strony blokują scrapery
Większość nowoczesnych stron stosuje warstwy ochrony: rate limiting na poziomie IP, nagłówki HTTP do fingerprintingu klienta, CAPTCHA i systemy anti-bot takie jak Cloudflare czy Datadome. Datacenter IP — adresy przypisane do serwerów hostingowych — są łatwo wykrywalne przez bazy ASN (Autonomous System Number). Według badań, ponad 40% ruchu z datacenter IP jest blokowane przez popularne strony e-commerce.
Residential proxy rozwiązują ten problem, ponieważ adresy IP pochodzą od rzeczywistych dostawców ISP i wyglądają jak naturalny ruch użytkowników. To kluczowa różnica, gdy scrapujesz źródła z ograniczeniami geograficznymi lub agresywnym anti-botem. Więcej o typach proxy znajdziesz na Wikipedii.
| Cecha | Datacenter Proxy | Residential Proxy | Mobile Proxy |
|---|---|---|---|
| Pochodzenie IP | Serwery hostingowe | Rzeczywiści ISP | Sieci komórkowe |
| Poziom blokad | d>Wysoki (40%+)Niski (<5%) | Bardzo niski | |
| Prędkość | d>Szybkie (<100ms) d>Średnie (200–500ms)Wolniejsze (300–800ms) | ||
| Koszt | d>Niski d>Średni d>Wysoki|||
| Geo-targeting | Ograniczony | Kraj + miasto | Kraj + operator |
Nowoczesny stos: httr2 i rvest
Pakiet httr2 to następca httr, oferujący potokowe API z request(), req_perform(), req_retry() i req_throttle(). Pakiet rvest upraszcza parsowanie HTML z funkcjami read_html(), html_elements() i html_table(). Razem tworzą wydajny zestaw do web scrapingu w R.
Podstawowe żądanie przez proxy
Funkcja req_proxy() przyjmuje URL proxy, nazwę użytkownika i hasło. Oto minimalny przykład:
library(httr2)
library(rvest)
# Konfiguracja proxy ProxyHat
proxy_url <- "http://gate.proxyhat.com:8080"
proxy_user <- "user-country-US"
proxy_pass <- "twoje_haslo"
# Budowa żądania z proxy i realistycznym User-Agent
req <- request("https://example.com/data") |>
req_proxy(proxy_url, proxy_user, proxy_pass) |>
req_user_agent(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
) |>
req_headers("Accept-Language" = "en-US,en;q=0.9")
resp <- req_perform(req)
# Parsowanie HTML z odpowiedzi
html <- resp_body_string(resp) |> read_html()
table_nodes <- html_elements(html, "table.data-table")
df <- html_table(table_nodes[[1]], trim = TRUE)
head(df)
Zauważ, że nazwa użytkownika zawiera flagę user-country-US — ProxyHat używa tego wzorca do geo-targetingu. Hasło jest oddzielone dwukropkiem, a całość kodowana w URL automatycznie przez httr2.
Geo-targeting i sticky sessions
ProxyHat pozwala na precyzyjne celowanie geograficzne oraz utrzymanie sesji (sticky session) w nazwie użytkownika:
# Geo-targeting: Wielka Brytania, Londyn
proxy_user_gb <- "user-country-GB-city-london"
# Sticky session — ten sam IP dla wielu żądań
proxy_user_sticky <- "user-country-GB-session-abc123"
# Rotacja per-żądanie — różne IP każde żądanie (domyślnie)
proxy_user_rotate <- "user-country-GB"
# SOCKS5 na porcie 1080
req_socks <- request("https://example.com") |>
req_proxy("socks5://gate.proxyhat.com:1080", proxy_user_sticky, proxy_pass)
Sticky sessions są kluczowe, gdy strona wymaga logowania lub utrzymania koszyka — rotacja IP w trakcie sesji powoduje wylogowanie. Używaj session- z unikalnym identyfikatorem (np. UUID), aby ProxyHat przypisał Ci stały IP na czas sesji.
Praktyczny przykład: paginacja z purrr i rotacją sesji
Pobierzmy tabelę z paginacją w postaci tidy data frame. Użyjemy purrr::map_dfr do iteracji po stronach, req_retry() do ponawiania nieudanych żądań i req_throttle() do ograniczenia tempa zapytań.
library(httr2)
library(rvest)
library(purrr)
library(dplyr)
# Bazowa funkcja pobierająca jedną stronę
fetch_page <- function(page_num, base_user = "user-country-US") {
# Rotacja sesji per strona — każda strona z innym IP
session_id <- paste0("page-", page_num, "-", format(Sys.time(), "%H%M%S"))
proxy_user <- paste0(base_user, "-session-", session_id)
url <- sprintf("https://example.com/products?page=%d", page_num)
req <- request(url) |>
req_proxy("http://gate.proxyhat.com:8080", proxy_user, "twoje_haslo") |>
req_user_agent(
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
) |>
req_headers(
"Accept" = "text/html,application/xhtml+xml",
"Accept-Language" = "en-US,en;q=0.9"
) |>
req_retry(
max_tries = 3,
backoff = ~ resp_is_rate_limited(.x) ~ 5
) |>
req_throttle(2 / 1) # max 2 żądania na sekundę
resp <- req_perform(req)
if (resp_status(resp) != 200) {
warning(sprintf("Strona %d zwróciła status %d", page_num, resp_status(resp)))
return(NULL)
}
html <- resp_body_string(resp) |> read_html()
tables <- html_elements(html, "table.products")
if (length(tables) == 0) return(NULL)
html_table(tables[[1]], trim = TRUE) |>
mutate(source_page = page_num)
}
# Pobieranie 50 stron z rotacją IP
pages <- 1:50
results <- map_dfr(pages, ~ fetch_page(.x))
# Podsumowanie
message(sprintf("Pobrano %d wierszy z %d stron", nrow(results), length(pages)))
Wzorzec req_retry() z backoff jest szczególnie ważny przy residential proxy — pojedynczy IP może chwilowo nie odpowiadać, ale retry automatycznie przełączy na nowy IP (dzięki rotacji w nazwie użytkownika). req_throttle(2 / 1) ogranicza do 2 żądań na sekundę, co jest bezpiecznym tempem dla większości stron.
Strony renderowane przez JavaScript
Wiele nowoczesnych stron wymaga wykonania JavaScript do załadowania treści. Pakiet rvest oferuje read_html_live(), który używa chromote (Chrome w tle) do renderowania. Możesz skonfigurować proxy dla chromote:
library(rvest)
library(chromote)
# Konfiguracja proxy dla chromote (Chrome)
# ProxyHat SOCKS5 — port 1080
proxy_user <- "user-country-DE-session-jsrender1"
proxy_pass <- "twoje_haslo"
# Ustaw zmienne środowiskowe Chrome
Sys.setenv(
CHROMOTE_HEADLESS = "true"
)
# Chromote obsługuje proxy przez argumenty Chrome
b <- ChromoteSession$new(
browser = Chromote$new(
args = c(
"--proxy-server=socks5://gate.proxyhat.com:1080",
"--proxy-auth=user:pass" # Chrome obsługuje to różnie
)
)
)
# Alternatywnie — użyj read_html_live() z opóźnieniem
# read_html_live() nie obsługuje proxy bezpośrednio,
# ale możesz ustawić proxy na poziomie systemu:
Sys.setenv(
http_proxy = "http://user-country-DE:twoje_haslo@gate.proxyhat.com:8080",
https_proxy = "http://user-country-DE:twoje_haslo@gate.proxyhat.com:8080"
)
html <- read_html_live("https://example.com/js-page", wait = 3)
data <- html |>
html_elements("div.result-item") |>
html_text(trim = TRUE)
Dla skomplikowanych stron JS, alternatywą jest ProxyHat SDK, który wspiera Python i Node.js — możesz wywołać skrypt Python z R przez processx::run() i przekazać wyniki jako JSON. To przydatne w hybrydowych pipeline'ach.
Realistyczne nagłówki i User-Agent
Jednym z najczęstszych błędów jest wysyłanie domyślnego User-Agent z httr2 (np. httr2/1.0.0), który natychmiast zdradza scraper. Zawsze ustawiaj realistyczny UA i komplet nagłówków:
req <- request(url) |>
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 (KHTML, like Gecko) "
"Chrome/120.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-US,en;q=0.9",
"Accept-Encoding" = "gzip, deflate, br",
"Connection" = "keep-alive",
"Upgrade-Insecure-Requests" = "1"
)
Najczęstsze błędy i przypadki brzegowe
1. Brak retry przy residential proxy
Residential IP mogą być chwilowo niedostępne (urządzenie offline). Bez req_retry() tracisz dane. Zawsze używaj minimum max_tries = 3 z backoff.
2. Zbyt agresywne tempo
Wysyłanie 100 żądań na sekundę przez residential proxy nadal wywoła blokady na poziomie strony. Używaj req_throttle() i dostosuj tempo do specyfiki strony — 1–3 żądania/s jest bezpieczne dla większości celów.
3. Ignorowanie kodowania znaków
Strony z różnych regionów mogą używać różnych kodowań. read_html() zazwyczaj radzi sobie automatycznie, ale przy ręcznym parsowaniu sprawdź nagłówek Content-Type:
resp <- req_perform(req)
encoding <- resp_headers(resp)[["Content-Type"]]
# Jeśli charset=iso-8859-2, konwertuj ręcznie:
body <- resp_body_string(resp)
if (grepl("iso-8859-2", encoding, ignore.case = TRUE)) {
body <- iconv(body, from = "ISO-8859-2", to = "UTF-8")
}
html <- read_html(body)
4. Brak obsługi timeout
Residential proxy mają wyższą latencję niż datacenter (200–500ms vs <100ms). Ustaw odpowiedni timeout:
req <- request(url) |>
req_proxy("http://gate.proxyhat.com:8080", proxy_user, proxy_pass) |>
req_options(timeout_ms = 30000) # 30 sekund
ProxyHat: konfiguracja i integracja
ProxyHat oferuje residential, mobile i datacenter proxy z jedną bramą: gate.proxyhat.com. Konfiguracja sprowadza się do ustawienia nazwy użytkownika z flagami geo-targetingu i sesji. Sprawdź cennik ProxyHat oraz dostępne lokalizacje.
Dla typowych zastosowań — web scraping i SERP tracking — residential proxy z rotacją per-żądanie są optymalne. Dla logowania i sesji koszyka używaj sticky sessions z flagą session-.
Pełny przykład produkcyjny z logowaniem
library(httr2)
library(rvest)
library(purrr)
library(dplyr)
library(logger)
# Konfiguracja logowania
log_appender(appender_file("scrape_log.txt"))
log_threshold(INFO)
# Konfiguracja ProxyHat
PROXY_HOST <- "http://gate.proxyhat.com:8080"
PROXY_PASS <- Sys.getenv("PROXYHAT_PASSWORD")
# Funkcja z circuit breaker
fetch_with_breaker <- function(url, country = "US", max_failures = 5) {
failure_count <- 0
while (failure_count < max_failures) {
session_id <- paste0("sess-", format(Sys.time(), "%Y%m%d%H%M%S"),
"-", sample(1000:9999, 1))
proxy_user <- paste0("user-country-", country, "-session-", session_id)
req <- request(url) |>
req_proxy(PROXY_HOST, proxy_user, PROXY_PASS) |>
req_user_agent(
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
) |>
req_headers(
"Accept-Language" = "en-US,en;q=0.9",
"Accept" = "text/html,application/xhtml+xml"
) |>
req_retry(max_tries = 3, backoff = ~ 2^failure_count) |>
req_throttle(1.5 / 1) |>
req_options(timeout_ms = 30000)
tryCatch({
resp <- req_perform(req)
if (resp_status(resp) == 200) {
log_info(sprintf("Sukces: %s (IP session: %s)", url, session_id))
return(resp_body_string(resp) |> read_html())
} else {
log_warn(sprintf("Status %d dla %s", resp_status(resp), url))
failure_count <- failure_count + 1
}
}, error = function(e) {
log_error(sprintf("Błąd: %s — %s", url, e$message))
failure_count <- failure_count + 1
Sys.sleep(2^failure_count)
})
}
log_error(sprintf("Circuit breaker aktywowany dla %s", url))
return(NULL)
}
# Użycie
urls <- sprintf("https://example.com/page/%d", 1:20)
html_list <- map(urls, ~ fetch_with_breaker(.x, country = "GB"))
Etyka i zgodność z przepisami
Web scraping niesie ze sobą odpowiedzialność prawną i etyczną. Kluczowe zasady:
- Preferuj oficjalne API — jeśli strona oferuje API (np. Google, Amazon), użyj go zamiast scrapingu. Jest stabilniejsze i legalne.
- Szanuj robots.txt — sprawdź
/robots.txtprzed scrapowaniem. Pakietrobotstxtw R automatycznie parsuje reguły. - RODO / GDPR — pobieranie danych osobowych osób w UE wymaga podstawy prawnej. Zobacz wytyczne Komisji Europejskiej.
- Warunki usługi (ToS) — niektóre strony zabraniają scrapingu w regulaminie. Należy to sprawdzić i skonsultować z radcą prawnym.
- Tempo — nie przeciążaj serwerów docelowych.
req_throttle()z limitem 1–3 żądań/s jest standardem branżowym.
Kluczowa zasada: Scraping danych publicznych (ceny, informacje o produktach) jest ogólnie akceptowany, ale pobieranie danych osobowych bez zgody narusza RODO. Zawsze konsultuj się z prawnikiem przy danych z UE.
Kluczowe wnioski
- httr2 + rvest to nowoczesny stos do web scrapingu w R —
req_proxy()obsługuje proxy,read_html()ihtml_table()parsują treść. - Residential proxy omijają blokady anti-bot, których datacenter IP nie pokonają — używaj ich do stron z ograniczeniami geograficznymi.
- Geo-targeting i sticky sessions konfiguruje się w nazwie użytkownika:
user-country-GB-city-london-session-abc123. - req_retry() i req_throttle() to podstawa odpornego pipeline — zawsze używaj retry z backoff i ograniczenia tempa.
- Strony JS wymagają
read_html_live()z chromote lub ProxyHat SDK w Python/Node dla hybrydowych pipeline'ów. - Etyka — preferuj API, szanuj robots.txt, przestrzegaj RODO i nie przeciążaj serwerów.
Często zadawane pytania
Czym jest korzystanie z proxy w R?
Korzystanie z proxy w R polega na kierowaniu żądań HTTP przez pośredni serwer, który ukrywa Twój adres IP i zastępuje go adresem proxy. W R realizuje się to głównie przez req_proxy() w pakiecie httr2, gdzie podajesz URL bramy proxy, nazwę użytkownika (z flagami geo-targetingu i sesji) oraz hasło. Proxy mogą być residential (IP od ISP), datacenter (serwery hostingowe) lub mobile (sieci komórkowe), z różnymi właściwościami wykrywalności i wydajności.
Dlaczego korzystanie z proxy w R ma znaczenie?
Bez proxy, Twój adres IP jest widoczny dla każdej strony i może zostać zablokowany po zaledwie kilkudziesięciu żądaniach. Residential proxy wyglądają jak naturalny ruch użytkowników, co pozwala na pobieranie danych ze stron z agresywnym anti-botem, ograniczeniami geograficznymi i rate limitingiem. Dla analityków pobierających dane z e-commerce, SERP czy portali informacyjnych, proxy to różnica między działającym pipeline a ciągłymi blokadami.
Który typ proxy działa najlepiej w R?
Residential proxy są najlepsze do większości zadań scrapingu w R, ponieważ adresy IP pochodzą od rzeczywistych ISP i są trudne do wykrycia. Datacenter proxy są tańsze i szybsze (<100ms vs 200–500ms), ale łatwo blokowane. Mobile proxy są najbardziej zaufane, ale najdroższe. Dla httr2 req_proxy i rvest proxy, residential z rotacją per-żądanie i geo-targetingiem to optymalny wybór dla web scrapingu.
Jak unikać blokad przy korzystaniu z proxy w R?
Stosuj kilka warstw ochrony: (1) rotuj IP per-żądanie przez zmianę identyfikatora sesji w nazwie użytkownika; (2) używaj req_retry() z backoff wykładniczym (min. 3 próby); (3) ograniczaj tempo przez req_throttle(2/1) — max 2 żądania/s; (4) ustawiaj realistyczny User-Agent i komplet nagłówków HTTP; (5) używaj geo-targetingu dopasowanego do strony (np. user-country-GB dla stron brytyjskich). Unikaj wysyłania ponad 100 żądań/s nawet z residential proxy.
Czy httr2 obsługuje SOCKS5 proxy?
Tak, req_proxy() w httr2 obsługuje SOCKS5 przez podanie URL w formacie socks5://gate.proxyhat.com:1080. SOCKS5 jest przydatny, gdy HTTP CONNECT nie jest wystarczający lub gdy potrzebujesz niższego narzutu protokołu. ProxyHat oferuje SOCKS5 na porcie 1080, podczas gdy HTTP proxy działa na porcie 8080. Konfiguracja nazwy użytkownika i hasła jest identyczna dla obu protokołów.






