# Proxy residenziali rotanti in Python: requests, httpx e aiohttp

> Proxy residenziali rotanti in Python: requests con retries, httpx sync e async, aiohttp, sessioni sticky e conteggio byte. Codice controllato nella sintassi.

- URL: https://crawlrelay.com/it/docs/python/
- Ultimo aggiornamento: 2026-10-13
- Publisher: CrawlRelay (https://crawlrelay.com)

Pattern di produzione, non one-liner: retries che rispettano Retry-After, un nuovo IP per richiesta o un IP per flusso, concorrenza per host in asyncio e byte effettivi consumati da ogni pagina.

**Risposta rapida — Come uso i proxy residenziali rotanti in Python?**

Passa `http://USERNAME:PASSWORD@gw.crawlrelay.com:7777` come proxy sia per `http` che per `https` in requests, come `proxy=` in httpx 0.26+ o come `proxy=` per richiesta in aiohttp. **CrawlRelay** proxy residenziali ruotano a ogni nuova connessione e rimangono sticky con `-session-<id>` nello username. Costano 1,50 USD per 1 GB e 0,50 USD/GB da 50 GB. Pagati in crypto, senza KYC.

## Configurazione Python a colpo d'occhio

| Attributo | Valore |
| --- | --- |
| requests | `proxies={"http": url, "https": url}`; retries tramite urllib3 `Retry` |
| httpx | `httpx.Client(proxy=url)`; il vecchio parametro `proxies=` è stato rimosso in httpx 0.28 |
| aiohttp | `session.get(url, proxy="http://USER:PASS@host:port")`, credenziali nell'URL del proxy |
| SOCKS5 | `pip install "requests[socks]"` o `httpx[socks]`, poi `socks5h://` sulla porta 7778 |
| Nuovo IP per richiesta | Disabilita keep-alive: `Connection: close`, `max_keepalive_connections=0` o `force_close=True` |
| Stesso IP per un flusso | `-session-<id>-ttl-<minutes>` nello username, fino a 30 minuti |
| Schema URL proxy | Sempre `http://` per la porta HTTP, anche per siti HTTPS |

## Come uso un proxy con requests, con retries che rispettano Retry-After?

Monta un `HTTPAdapter` con urllib3's `Retry` su un `Session`. Riprova su errori di connessione e stati indicati, attende con backoff esponenziale e rispetta l'intestazione `Retry-After` che i siti inviano con 429 e 503. Imposta `trust_env = False` così una variabile residua `HTTPS_PROXY` non può sovrascrivere il tuo proxy.

*requests + urllib3 Retry*

```python
import os

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

USER = os.environ.get("CRAWLRELAY_USER", "USERNAME-country-us")
PASSWORD = os.environ.get("CRAWLRELAY_PASSWORD", "PASSWORD")
PROXY = "http://%s:%s@gw.crawlrelay.com:7777" % (USER, PASSWORD)

retry = Retry(
    total=4,
    connect=2,
    backoff_factor=1.0,  # waits of 0 s, 2 s, 4 s, 8 s (Retry-After wins on 429/503)
    status_forcelist=(429, 502, 503, 504),
    allowed_methods=frozenset({"GET", "HEAD"}),
    respect_retry_after_header=True,
)

session = requests.Session()
session.trust_env = False  # ignore HTTP(S)_PROXY from the environment
session.mount("http://", HTTPAdapter(max_retries=retry))
session.mount("https://", HTTPAdapter(max_retries=retry))
session.proxies = {"http": PROXY, "https": PROXY}
session.headers["User-Agent"] = "ExampleCrawler/1.0 (+https://example.com/crawler-info)"
session.headers["Connection"] = "close"  # new connection per request, so usually a new exit IP

r = session.get("https://api.ipify.org", timeout=(10, 30))
print(r.status_code, r.text)
```

## Come costruisco in sicurezza URL proxy mirati?

Genera lo username in una funzione e convalidalo. Un errore di digitazione nel codice paese o un carattere non codificato nella password produce un [407](https://crawlrelay.com/it/docs/errors/407-proxy-authentication-required/) che sembra un problema di credenziali. L’helper qui sotto mantiene l’ordine documentato (`USERNAME-country-{cc}-state-{state}-city-{city}-zip-{zip}-asn-{asn}-session-{id}-ttl-{minutes}`), rifiuta chiavi sconosciute e codifica URL entrambe le credenziali.

*Generatore username e URL proxy*

```python
import re
from urllib.parse import quote

ORDER = ("country", "state", "city", "zip", "asn", "session", "ttl")
VALUE = re.compile(r"^[a-z0-9_]+$")


def build_username(base, **params):
    """build_username("USERNAME", country="us", city="new_york", session="job1", ttl=30)"""
    parts = [base]
    for key in ORDER:
        value = params.pop(key, None)
        if value is None:
            continue
        value = str(value).lower()
        if not VALUE.match(value):
            raise ValueError("bad value for %s: %r" % (key, value))
        if key == "ttl" and not 1 <= int(value) <= 30:
            raise ValueError("ttl must be 1 to 30 minutes")
        parts.append("%s-%s" % (key, value))
    if params:
        raise ValueError("unknown parameters: %s" % ", ".join(sorted(params)))
    return "-".join(parts)


def proxy_url(username, password, socks=False):
    scheme, port = ("socks5h", 7778) if socks else ("http", 7777)
    return "%s://%s:%s@gw.crawlrelay.com:%d" % (
        scheme, quote(username, safe=""), quote(password, safe=""), port)


print(proxy_url(build_username("USERNAME", country="us", state="ny", city="new_york", session="job1", ttl=20), "p@ss:word"))
```

## Come ottengo un nuovo IP a ogni richiesta con httpx?

Disattiva il keep-alive. httpx mantiene le connessioni in pool di default, e una connessione riutilizzata conserva l’IP di uscita. Con `max_keepalive_connections=0`, ogni richiesta apre una nuova connessione al gateway e solitamente esce con un IP residenziale diverso. `num_bytes_downloaded` fornisce i byte ricevuti in trasmissione, che è la misura più vicina alla fatturazione del traffico.

*httpx: client rotante*

```python
import httpx

PROXY = "http://USERNAME-country-gb:PASSWORD@gw.crawlrelay.com:7777"

# No keep-alive: every request opens a new connection, so it usually gets a new exit IP
with httpx.Client(
    proxy=PROXY,
    timeout=httpx.Timeout(30.0, connect=10.0),
    limits=httpx.Limits(max_keepalive_connections=0),
    follow_redirects=True,
) as client:
    for _ in range(3):
        r = client.get("https://api.ipify.org")
        print(r.text, r.num_bytes_downloaded, "bytes on the wire")
```

## Come faccio scraping concorrente con asyncio senza sovraccaricare un sito?

Limita la concorrenza **per host**, non solo globalmente. Un limite globale di 50 permette a tutte le 50 richieste di colpire lo stesso sito se la lista URL è ordinata per dominio. Un semaforo per hostname mantiene ogni sito a un livello cortese mentre la crawl nel complesso resta veloce. Parti da 2 a 4 per host e leggi la [guida alla concorrenza](https://crawlrelay.com/it/docs/concurrency/) prima di aumentarla.

*httpx.AsyncClient con un semaforo per host*

```python
import asyncio
from collections import defaultdict
from urllib.parse import urlsplit

import httpx

PROXY = "http://USERNAME-country-us:PASSWORD@gw.crawlrelay.com:7777"
PER_HOST = 4  # parallel requests per target host
host_slots = defaultdict(lambda: asyncio.Semaphore(PER_HOST))


async def fetch(client, url):
    async with host_slots[urlsplit(url).hostname]:
        try:
            r = await client.get(url)
            return url, r.status_code, r.num_bytes_downloaded
        except httpx.HTTPError as exc:
            return url, type(exc).__name__, 0


async def main(urls):
    limits = httpx.Limits(max_connections=50, max_keepalive_connections=0)
    timeout = httpx.Timeout(30.0, connect=10.0)
    async with httpx.AsyncClient(proxy=PROXY, limits=limits, timeout=timeout) as client:
        for result in await asyncio.gather(*(fetch(client, u) for u in urls)):
            print(*result)


asyncio.run(main(["https://example.com/", "https://example.org/", "https://example.net/"]))
```

## Come uso un proxy con aiohttp?

Passa il gateway come `proxy=` in ogni richiesta, con credenziali nell’URL proxy. Funziona con ogni release aiohttp 3.x senza avvisi; `BasicAuth` e il parametro `proxy_auth=` sono deprecati da aiohttp 3.14 e verranno rimossi in 4.0. Un `TCPConnector(force_close=True)` chiude ogni connessione dopo la risposta, quindi ogni richiesta ottiene di solito un nuovo IP di uscita. Senza di esso, aiohttp riutilizza le connessioni e mantieni lo stesso IP per un po’.

*aiohttp con credenziali nell’URL proxy*

```python
import asyncio

import aiohttp

# Credentials go in the proxy URL; percent-encode a password that contains @ or :
PROXY = "http://USERNAME-country-fr:PASSWORD@gw.crawlrelay.com:7777"


async def main():
    # force_close=True: one connection per request, so each request usually gets a new exit IP
    connector = aiohttp.TCPConnector(limit=20, force_close=True)
    timeout = aiohttp.ClientTimeout(total=60, sock_connect=10)
    async with aiohttp.ClientSession(connector=connector, timeout=timeout) as session:
        for _ in range(3):
            async with session.get("https://api.ipify.org", proxy=PROXY) as resp:
                print(resp.status, await resp.text())


asyncio.run(main())
```

## Come mantengo un IP per un flusso a più step?

Genera un ID sessione per flusso e inseriscilo nello username con TTL più lungo del flusso. Keep-alive va bene qui: vuoi lo stesso IP di uscita. Se il dispositivo residenziale dietro la sessione va offline, l’IP può cambiare prima della scadenza del TTL, quindi rendi il flusso riavviabile dal checkpoint più recente.

*Sessione sticky con requests*

```python
import uuid

import requests

session_id = uuid.uuid4().hex[:10]  # one ID per flow, lowercase letters and digits
user = "USERNAME-country-us-state-ca-session-%s-ttl-20" % session_id
proxy = "http://%s:PASSWORD@gw.crawlrelay.com:7777" % user

with requests.Session() as s:
    s.proxies = {"http": proxy, "https": proxy}
    first = s.get("https://api.ipify.org", timeout=(10, 30)).text
    second = s.get("https://api.ipify.org", timeout=(10, 30)).text
    print(first, second, "same exit" if first == second else "exit changed")
```

## Quanto traffico consuma uno scraper Python per pagina?

Misuralo su 100 pagine reali prima di acquistare volume. Logga `num_bytes_downloaded` in httpx, oppure la lunghezza del corpo compresso nel tuo middleware, e moltiplica per il numero di pagine. Richieste solo HTML solitamente costano decine di kilobyte per pagina; la [guida al traffico](https://crawlrelay.com/it/guides/bandwidth-calculator/) fornisce i numeri e il costo per 1.000 pagine ai prezzi CrawlRelay.

Tre abitudini riducono il traffico senza cambiare i risultati:

- Invia `Accept-Encoding: gzip, br` (httpx e requests inviano gzip di default) così le pagine viaggiano compresse.
- Recupera l’endpoint JSON che una pagina carica, se il sito lo documenta o permette, invece dell’HTML completo.
- Salta le pagine non cambiate con `If-None-Match` o `If-Modified-Since`: un `304 Not Modified` costa poche centinaia di byte.

## Python e proxy: domande

### requests, httpx o aiohttp: quale dovrei utilizzare per scraping?

Usa requests per script semplici e piccoli crawl. Usa httpx se vuoi la stessa API per codice sincrono e asincrono, e HTTP/2 tramite `httpx[http2]`. Usa aiohttp per una concorrenza molto alta in puro asyncio. Per un crawl con scheduling, deduplicazione e pipeline, usa [Scrapy](https://crawlrelay.com/it/docs/scrapy/) invece di scrivere quelle parti da solo.

### Perché ottengo SSL: WRONG_VERSION_NUMBER con il mio proxy in Python?

Probabilmente hai scritto `https://` davanti all'indirizzo del proxy. La porta del gateway 7777 usa HTTP semplice e apre un tunnel CONNECT per siti HTTPS, quindi l'URL del proxy deve iniziare con `http://` sia nelle chiavi `http` che `https`. Vedi la [pagina di errore](https://crawlrelay.com/it/docs/errors/ssl-wrong-version-number/).

### Come uso i proxy SOCKS5 in Python?

Installa l'extra (`pip install "requests[socks]"` o `pip install "httpx[socks]"`), poi usa `socks5h://USERNAME:PASSWORD@gw.crawlrelay.com:7778`. Il `h` invia i nomi host al proxy, quindi il DNS viene risolto all'uscita e ottieni le stesse risposte regionali dei visitatori locali.

### Perché il mio proxy rotante restituisce due volte lo stesso IP?

Il tuo client ha riutilizzato una connessione keep-alive, e la rotazione avviene ad ogni nuova connessione. Invia `Connection: close` in requests, imposta `max_keepalive_connections=0` in httpx, oppure usa `force_close=True` in aiohttp. Controlla anche che lo username non contenga la parte `session-`, che mantiene intenzionalmente un IP fisso.

### Come imposto i timeout per le richieste proxate?

Impostali esplicitamente: requests non ha timeout di default. Un connect timeout di circa 10 secondi e un read timeout di 30 secondi vanno bene per pagine HTML; le uscite mobili e i target lenti richiedono 45-60 secondi. Riprova un timeout una volta su una nuova connessione invece di aspettare più a lungo. Vedi i [timeout di lettura](https://crawlrelay.com/it/docs/errors/read-timeout/).

### Posso usare questi proxy in Jupyter o con pandas?

Sì. Qualsiasi cosa che usa requests o httpx sotto funziona con le stesse impostazioni proxy. Per `pandas.read_html` o `read_csv` su un URL, scarica prima il contenuto con una chiamata requests proxyata, poi passa testo o bytes a pandas.

**Esegui il tuo scraper Python tramite proxy residenziali** 1,50 USD per 1 GB, 0,50 USD/GB da 50 GB. [Vedi i prezzi](https://crawlrelay.com/it/pricing/) · [Guida Scrapy](https://crawlrelay.com/it/docs/scrapy/)

---

CrawlRelay: CrawlRelay è un servizio di proxy residenziali rotanti e proxy mobili 4G/5G primo per sviluppatori: traffico pay-as-you-go senza scadenza da 0,50 USD/GB, pagato in crypto, senza caricamento documenti. Senza KYC — niente documento, selfie o altri documenti. Paga in crypto.
