La rotazione proxy in Crawlee per Python è una delle competenze più richieste da chi costruisce crawler di produzione. Se hai mai visto il tuo scraper bloccato dopo 50 richieste da un firewall WAF come Cloudflare o DataDome, sai quanto sia fragile un setup senza gestione intelligente degli IP. Crawlee — il framework open-source di Apify — offre strumenti nativi per affrontare il problema: ProxyConfiguration, SessionPool e un sistema di autoscaling che insieme permettono di distribuire il traffico su dozzine di IP residenziali mantenendo cookie e fingerprint coerenti. In questa guida esploriamo ogni punto di estensione del framework, con codice runnable che usa i gateway ProxyHat.
Nota legale: il web scraping di dati pubblici è generalmente lecito, ma l'accesso non autorizzato a sistemi protetti può violare legali come il Computer Fraud and Abuse Act (CFAA) negli USA o il GDPR in Europa. Rispetta sempre i
robots.txt, i termini di servizio e i limiti di frequenza. Preferisci API ufficiali quando disponibili.
Rotazione proxy in Crawlee per Python: il quadro generale
Crawlee per Python (crawlee.dev/python) è il successore spirituale dell'Apify SDK, riscritto come framework standalone con un'architettura modulare. Due crawler principali gestiscono il grosso del lavoro:
- BeautifulSoupCrawler — HTTP puro con parsing via BeautifulSoup o lxml. Veloce, leggero, ideale per siti statici.
- PlaywrightCrawler — browser headless via Playwright. Necessario per SPA, contenuti renderizzati lato client o sfide JavaScript anti-bot.
Entrambi i crawler condividono la stessa infrastruttura: una code di richieste unificata (RequestQueue o RequestList), un pool di sessioni (SessionPool) che lega cookie, fingerprint e IP proxy, e un autoscaled pool che regola la concorrenza in base alle prestazioni. La rotazione proxy non è un hack esterno: si integra a livello di configurazione del crawler, e il framework la propaga automaticamente a ogni richiesta.
BeautifulSoupCrawler vs PlaywrightCrawler: quando scegliere
Il BeautifulSoupCrawler effettua richieste HTTP dirette — tipicamente 200-500ms per pagina su una connessione proxy residenziale. Il PlaywrightCrawler avvia un'istanza Chromium completa, con overhead di 2-5 secondi per pagina. La regola è semplice: se il contenuto è nel HTML iniziale, usa BeautifulSoup. Se il sito richiede esecuzione JavaScript o supera sfide anti-bot dinamiche, passa a Playwright. Molti team mantengono entrambi: BeautifulSoup per il 90% del volume, Playwright per i casi problematici.
SessionPool: il cuore della gestione sessioni in Crawlee
Il crawlee session pool è il componente che distingue Crawlee da un semplice wrapper HTTP. Ogni Session nel pool mantiene:
- Cookie accumulati durante la navigazione
- Un ID univoco usato per pinning dell'IP proxy
- Stato (attiva, bloccata, ritirata)
- Contatori di errori e successo
Quando il crawler assegna una sessione a una richiesta, usa l'ID della sessione per ottenere un proxy coerente da ProxyConfiguration. Questo significa che lo stesso IP residenziale naviga attraverso più pagine dello stesso dominio, mantenendo i cookie validi — esattamente come un utente reale. Se la sessione viene bloccata (session.retire()), il pool genera una nuova sessione con un nuovo IP.
ProxyConfiguration: l'approccio idiomatico in Crawlee python proxy
La classe ProxyConfiguration è il punto di estensione centrale per la gestione dei proxy. Si costruisce una volta e si passa al crawler:
from crawlee.proxy_configuration import ProxyConfiguration
# Rotazione round-robin tra endpoint ProxyHat
proxy_configuration = ProxyConfiguration(
proxy_urls=[
'http://user-country-US:pass@gate.proxyhat.com:8080',
'http://user-country-DE:pass@gate.proxyhat.com:8080',
'http://user-country-GB:pass@gate.proxyhat.com:8080',
]
)
# new_url(session_id=...) pinna un IP a una sessione specifica
proxy_url = await proxy_configuration.new_url(session_id='abc123')
Il metodo new_url(session_id=...) è la chiave della rotazione session-bound. Quando passi un session_id, ProxyConfiguration restituisce deterministicamente lo stesso URL proxy per quello stesso ID. Senza session_id, ruota round-robin tra gli URL configurati. Questo è esattamente ciò che serve per i proxy residenziali ProxyHat: l'ID di sessione nel username ProxyHat (es. user-country-US-session-abc123) mantiene lo stesso IP per tutta la durata della sessione Crawlee.
Rotazione round-robin vs session-bound: quale usare
Round-robin (senza session_id) va bene per richieste stateless: API pubbliche, endpoint REST, pagine che non richiedono login. Ogni richiesta ottiene un IP diverso, massimizzando la distribuzione.
Session-bound (con session_id) è indispensabile per: siti con login, carrelli e-commerce, navigazione multi-pagina dove i cookie devono persistere, e qualsiasi target che profila il comportamento dell'IP nel tempo. Senza session-bound, i cookie raccolti su un IP verrebbero inviati da un IP diverso alla richiesta successiva — un segnale immediato di bot.
Proxy residenziali vs datacenter: Cloudflare, DataDome e fallback a livelli
I sistemi anti-bot moderni come Cloudflare Bot Management e DataDome non si limitano a controllare gli header HTTP. Analizzano la reputazione dell'IP, il ASN di provenienza, la coerenza tra IP dichiarato e fingerprint del browser, e pattern comportamentali. Un IP datacenter con ASN di AWS o DigitalOcean è un segnale immediato: nessun utente reale naviga da un IP di un datacenter.
I proxy residenziali usano IP assegnati a dispositivi consumer reali (ISP domestici), rendendo il traffico indistinguibile da quello umano. Il costo è maggiore — tipicamente $3-15 per GB contro $0.5-2 per GB dei datacenter — ma il tasso di successo su target protetti passa dal 30-50% al 90%+.
| Caratteristica | Proxy Residenziali | Proxy Datacenter | Proxy Mobile |
|---|---|---|---|
| Overhead di latenza | 50-200ms | <50ms | 200-500ms |
| Resistenza anti-bot | Alta | Bassa | Molto alta |
| Persistenza sessione IP | Fino a 72 ore | N/A (rotazione per richiesta) | 10-30 minuti |
| Costo indicativo per GB | $3-15 | $0.5-2 | $10-30 |
| Concorrenza raccomandata | 50-100 sessioni | 100-500 | 10-30 |
Strategia di fallback a livelli (tiered proxies)
Un pattern di produzione efficace usa proxy a livelli: inizia con datacenter per minimizzare i costi, e passa a residenziali solo quando rilevi blocchi. In Crawlee, questo si implementa combinando più ProxyConfiguration o gestendo il fallback nel request_handler:
from crawlee.proxy_configuration import ProxyConfiguration
# Tier 1: datacenter per traffico ad alto volume, basso rischio
dc_proxies = [
'http://dc-user:pass@gate.proxyhat.com:8080',
]
# Tier 2: residenziali per target protetti
res_proxies = [
f'http://user-country-US-session-{i}:pass@gate.proxyhat.com:8080'
for i in range(20)
]
# Crawlee ruota tra tutti gli URL; ordina per priorità
tiered_config = ProxyConfiguration(
proxy_urls=res_proxies + dc_proxies,
)
Per un controllo più fine, puoi implementare un middleware personalizzato che monitora il tasso di successo per configurazione e cambia dinamicamente. L'approccio più semplice in Crawlee è gestire il fallback nel request_handler: se una richiesta con proxy datacenter riceve 403, ritira la sessione e lascia che il pool ne crei una nuova — che potrebbe pescare un URL residenziale.
Esempio runnable: BeautifulSoupCrawler con ProxyConfiguration e ProxyHat
Ecco un esempio completo che usa crawlee proxyconfiguration con endpoint ProxyHat geo-localizzati e sessioni pin-nate. Il codice genera username per-sessione nel formato ProxyHat (user-country-US-session-{id}), permettendo al SessionPool di mantenere lo stesso IP residenziale per tutta la durata di una sessione.
import random
import string
from crawlee.beautifulsoup_crawler import (
BeautifulSoupCrawler,
BeautifulSoupCrawlingContext,
)
from crawlee.proxy_configuration import ProxyConfiguration
GATEWAY = 'gate.proxyhat.com'
PORT = 8080
BASE_USER = 'your_proxyhat_username'
PASSWORD = 'your_proxyhat_password'
def gen_session_id() -> str:
"""Genera un ID sessione alfanumerico di 12 caratteri."""
return ''.join(random.choices(string.ascii_lowercase + string.digits, k=12))
def build_proxy_url(session_id: str | None = None) -> str:
"""Costruisce un URL proxy ProxyHat con geo-targeting US e sessione pin-nata."""
sid = session_id or gen_session_id()
username = f'{BASE_USER}-country-US-session-{sid}'
return f'http://{username}:{PASSWORD}@{GATEWAY}:{PORT}'
# Pre-genera 10 URL proxy con sessioni distinte
proxy_urls = [build_proxy_url(gen_session_id()) for _ in range(10)]
proxy_configuration = ProxyConfiguration(proxy_urls=proxy_urls)
crawler = BeautifulSoupCrawler(
proxy_configuration=proxy_configuration,
max_request_retries=3,
max_concurrency=10,
)
@crawler.router.default_handler
async def handle_page(context: BeautifulSoupCrawlingContext) -> None:
title_tag = context.soup.find('title')
title = title_tag.text.strip() if title_tag else 'N/A'
session_id = context.session.id if context.session else 'no-session'
context.log.info(
f'URL: {context.request.url} | '
f'Session: {session_id} | '
f'Title: {title}'
)
# Enqueue link interni per crawling continuativo
await context.enqueue_links()
await crawler.run(['https://example.com'])
Generazione di username per-sessione con il formato ProxyHat
Il formato del username ProxyHat è potente perché permette di codificare geo-targeting e sessione direttamente nelle credenziali proxy. La sintassi è:
# Geo-targeting per paese
http://user-country-US:pass@gate.proxyhat.com:8080
# Geo-targeting per città
http://user-country-DE-city-berlin:pass@gate.proxyhat.com:8080
# Sessione pin-nata (IP stabile)
http://user-session-abc123:pass@gate.proxyhat.com:8080
# Combinato: paese + sessione
http://user-country-US-session-abc123:pass@gate.proxyhat.com:8080
Quando il SessionPool di Crawlee crea una nuova sessione con ID xyz789, e il ProxyConfiguration chiama new_url(session_id='xyz789'), l'URL risultante include session-xyz789 nel username. ProxyHat assegna allora lo stesso IP residenziale a tutte le richieste che usano quel session ID — tipicamente per fino a 72 ore consecutive.
Consulta la documentazione ufficiale ProxyHat per i dettagli sulla persistenza delle sessioni e i parametri disponibili. Per le location supportate, visita la pagina delle posizioni ProxyHat.
Pattern di produzione: session.retire(), retry e concorrenza nel crawlee session pool
Configurare i proxy è solo l'inizio. Un crawler di produzione deve gestire blocchi, timeout, concorrenza e retry in modo elegante. Crawlee offre hook specifici per ognuno di questi aspetti.
Gestione dei blocchi con session.retire()
Quando una richiesta riceve 403, 429 o un contenuto che indica un CAPTCHA, la risposta corretta non è solo ritentare — è ritirare la sessione. session.retire() marca la sessione come compromessa, libera l'IP associato e fa sì che il pool crei una sessione fresca con un nuovo proxy:
from crawlee.beautifulsoup_crawler import BeautifulSoupCrawlingContext
@crawler.router.default_handler
async def handler(context: BeautifulSoupCrawlingContext) -> None:
status = context.http_response.status_code
if status in (403, 429):
context.log.warning(
f'Blocco {status} su {context.request.url} — '
f'ritiro la sessione'
)
if context.session:
context.session.retire()
# Non processare la pagina; il retry userà una nuova sessione
return
soup = context.soup
# ... estrai i dati
await context.enqueue_links()
max_request_retries e gestione degli errori
Il parametro max_request_retries=3 sul crawler definisce quante volte ritentare una richiesta fallita prima di scartarla. Crawlee incrementa un contatore di retry per ogni richiesta e usa un backoff esponenziale tra i tentativi. Se una richiesta fallisce 3 volte consecutive, viene spostata in una code di fallimenti (RequestQueue.failed) e il crawler continua con le altre.
Per logica di retry personalizzata, puoi sovrascrivere error_handler nel crawler:
async def on_error(context, error):
context.log.error(
f'Errore su {context.request.url}: {type(error).__name__}: {error}'
)
# Logica custom: notifica, metriche, fallback a PlaywrightCrawler
crawler = BeautifulSoupCrawler(
proxy_configuration=proxy_configuration,
max_request_retries=3,
error_handler=on_error,
)
Concorrenza e autoscaling
Crawlee include un autoscaled pool che regola dinamicamente la concorrenza in base al tasso di successo. Se le richieste falliscono frequentemente, il pool riduce i worker; se tutto va liscio, scala fino a max_concurrency. Per i proxy residenziali ProxyHat, una configurazione ragionevole è:
- max_concurrency=10-50 per BeautifulSoupCrawler su target moderati
- max_concurrency=5-10 per PlaywrightCrawler (ogni worker usa un browser)
- max_concurrency=100+ per API pubbliche con proxy datacenter
Scaling con container: headless fleet
Per volumi che superano le capacità di un singolo processo, il pattern standard è containerizzare il crawler e eseguire multiple istanze. Ogni container esegue un'istanza indipendente di Crawlee con il proprio SessionPool e ProxyConfiguration. La code di richieste può essere condivisa via storage distribuito (Redis, o il RequestQueue gestito di Apify). Con 10 container a 50 sessioni concorrenti ciascuno, si raggiungono 500 sessioni attive — sufficienti per la maggior parte dei casi d'uso di web scraping e SERP tracking.
Per il deployment containerizzato, un'immagine Docker tipica include Python 3.11+, Crawlee, Playwright con dipendenze browser, e le variabili d'ambiente per le credenziali ProxyHat. Il gateway gate.proxyhat.com:8080 supporta 100+ sessioni concorrenti per account, con un uptime dichiarato del 99.9%.
Quando NON usare il browser crawling, e considerazioni etiche
Il PlaywrightCrawler è potente ma costoso: ogni pagina consuma 200-500MB di RAM e 2-5 secondi di tempo. Prima di raggiungerlo, considera:
- Il contenuto è nel HTML iniziale? Usa BeautifulSoupCrawler. Molte SPA espongono i dati in chiamate API JSON separate — intercetta quelle invece di renderizzare la pagina.
- Esiste un'API ufficiale? Molte piattaforme (Reddit, Twitter/X, Amazon) offrono API con rate limit generosi. Un'API ufficiale è sempre più affidabile e legalmente sicura del scraping.
- Il sito blocca solo per comportamento, non per JavaScript? Headers HTTP corretti (User-Agent realistici, Accept-Language, Referer) con BeautifulSoupCrawler spesso bastano.
Dal punto di vista etico e legale, rispetta sempre il RFC 9309 sul protocollo robots.txt — è lo standard IETF che definisce come i crawler devono interpretare le direttive di esclusione. Crawlee non applica automaticamente i robots.txt: devi implementare il controllo nel tuo handler o usare un middleware. Implementa anche rate limiting rispettoso: 1 richiesta ogni 2-5 secondi per dominio è una buona pratica per siti senza ToS espliciti.
Key Takeaways
- ProxyConfiguration è il punto di estensione idiomatico per la rotazione proxy in Crawlee. Usa
new_url(session_id=...)per pinning IP per-sessione, senzasession_idper round-robin. - SessionPool lega cookie, fingerprint e IP proxy. Quando ritiri una sessione con
session.retire(), il pool crea una nuova sessione con un nuovo IP — non devi gestire la rotazione manualmente. - I proxy residenziali battono i datacenter sui target protetti (Cloudflare, DataDome). Usa proxy a livelli: datacenter per traffico ad alto volume, residenziali per fallback sui blocchi.
- Il formato username ProxyHat (
user-country-US-session-abc123) codifica geo-targeting e sessione direttamente nelle credenziali — si integra nativamente con il session_id di Crawlee. - Autoscaling e containerizzazione permettono di scalare da 10 a 500+ sessioni concorrenti. Configura
max_concurrencyin base al tipo di proxy e crawler. - Etica prima di tutto: rispetta robots.txt, rate limit e ToS. Preferisci API ufficiali quando disponibili.
Configura ProxyHat con Crawlee
ProxyHat offre proxy residenziali, datacenter e mobile con geo-targeting per oltre 90 paesi. Il gateway gate.proxyhat.com:8080 è compatibile nativamente con ProxyConfiguration di Crawlee — basta inserire l'URL nel parametro proxy_urls. Consulta il prezzario ProxyHat per i piani disponibili, o visita la documentazione per i dettagli tecnici completi.






