Se il tuo script Python restituisce un errore 403, CAPTCHA o un ban per IP, significa che il sito target ti ha già notato. Collegare un proxy alla libreria requests risolve questo problema: cambi l'indirizzo IP, aggiri le restrizioni geografiche e distribuisci il carico tra più indirizzi. In questa guida troverai tutto, dalla connessione di base alla rotazione avanzata con esempi di codice reali.
Perché sono necessari i proxy negli script Python
La maggior parte dei siti e delle API tiene traccia degli indirizzi IP delle richieste in arrivo. Se un indirizzo fa più di 100 richieste al minuto, viene bloccato. Questa è una protezione standard contro i bot, utilizzata da Wildberries, Ozon, Avito, Google, Instagram e centinaia di altre piattaforme. I proxy consentono di indirizzare la richiesta attraverso un server intermedio con un altro indirizzo IP, rendendoti invisibile ai sistemi di protezione.
Ecco i principali compiti in cui i proxy in Python sono criticamente necessari:
- Scraping di marketplace — raccolta di prezzi da Wildberries, Ozon, Yandex.Market senza blocchi per IP
- Monitoraggio dei concorrenti — richieste regolari ai siti dei concorrenti ogni 5-15 minuti
- Interazione con API con limiti — distribuzione delle richieste tra più IP per non superare il rate limit
- Test di geolocalizzazione — verifica di come appare un sito da diversi paesi e regioni
- Automazione di moduli e registrazioni — creazione di account o compilazione di moduli da diversi IP
- Monitoraggio SEO — raccolta di posizioni da diverse regioni della Russia e altri paesi
Senza proxy, anche un parser ben scritto si imbatterà in un blocco dopo poche ore di lavoro. Con una rotazione IP configurata correttamente, lo stesso script funziona per settimane senza interruzioni.
Configurazione di base del proxy in requests
La libreria requests supporta i proxy nativamente — non sono necessari pacchetti aggiuntivi. Il proxy viene passato tramite un dizionario proxies nei parametri della richiesta.
Il più semplice esempio è un proxy HTTP per una singola richiesta:
import requests
proxies = {
"http": "http://123.45.67.89:8080",
"https": "http://123.45.67.89:8080",
}
response = requests.get("https://httpbin.org/ip", proxies=proxies)
print(response.json())
# {'origin': '123.45.67.89'}
Nota: nel dizionario proxies è necessario specificare entrambe le chiavi — http e https. Se ne specifichi solo una, le richieste per il secondo protocollo andranno direttamente senza proxy. Questo è un errore comune tra i principianti, che causa comunque la fuga del vero IP.
Per assicurarti che il proxy funzioni, utilizza il servizio httpbin.org/ip — restituisce l'indirizzo IP da cui è arrivata la richiesta. Se nella risposta vedi l'IP del server proxy e non il tuo, tutto è configurato correttamente.
Proxy HTTP, HTTPS e SOCKS5: differenze ed esempi di codice
I proxy possono essere di diversi tipi, e ognuno è adatto per i propri compiti. Nel contesto di Python requests, è importante comprendere la differenza tra i tre protocolli principali:
| Tipo | Protocollo nell'URL | Velocità | Supporto UDP | Miglior scenario |
|---|---|---|---|---|
| HTTP | http:// |
Alta | No | Scraping di siti HTTP |
| HTTPS | https:// |
Alta | No | Scraping di siti protetti |
| SOCKS5 | socks5:// |
Media | Sì | Massima anonimato, qualsiasi protocollo |
Per lavorare con SOCKS5 in Python è necessario installare un pacchetto aggiuntivo:
pip install requests[socks] # oppure separatamente: pip install PySocks
Dopo l'installazione, la connessione a un proxy SOCKS5 appare così:
import requests
# Proxy SOCKS5
proxies = {
"http": "socks5://123.45.67.89:1080",
"https": "socks5://123.45.67.89:1080",
}
response = requests.get("https://httpbin.org/ip", proxies=proxies)
print(response.json())
SOCKS5 è il protocollo preferito per compiti in cui l'anonimato è importante. A differenza dei proxy HTTP, SOCKS5 non aggiunge intestazioni X-Forwarded-For, che possono rivelare il tuo vero IP.
Proxy con autenticazione tramite username e password
La maggior parte dei servizi proxy a pagamento utilizza l'autenticazione tramite username e password. Questa è una pratica standard: senza autorizzazione, il proxy non permetterà semplicemente la tua richiesta. Nella libreria requests, i dati di autenticazione vengono passati direttamente nell'URL del proxy.
import requests # Formato: protocollo://username:password@host:port proxy_url = "http://myuser:[email protected]:8080" proxies = { "http": proxy_url, "https": proxy_url, } response = requests.get("https://httpbin.org/ip", proxies=proxies) print(response.status_code) print(response.json())
Se nella password o nello username ci sono caratteri speciali (ad esempio, @, #, %), devono essere URL-encoded. Per questo, utilizza il modulo urllib.parse:
import requests
from urllib.parse import quote
username = "myuser"
password = "p@ss#word!" # Caratteri speciali
# URL-encoding di username e password
encoded_user = quote(username, safe="")
encoded_pass = quote(password, safe="")
proxy_url = f"http://{encoded_user}:{encoded_pass}@123.45.67.89:8080"
proxies = {
"http": proxy_url,
"https": proxy_url,
}
response = requests.get("https://httpbin.org/ip", proxies=proxies)
print(response.json())
💡 Consiglio di sicurezza
Non hardcodare mai username e password direttamente nel codice dello script. Utilizza variabili d'ambiente o un file .env con la libreria python-dotenv. In questo modo eviterai la fuga accidentale delle credenziali quando pubblichi il codice su GitHub.
Rotazione dei proxy: cambio automatico di IP per scraping
Un proxy è ancora un solo indirizzo IP, che può essere bloccato. La vera protezione contro i ban è la rotazione: ogni richiesta (o ogni N richieste) parte con un nuovo IP. Di seguito sono riportati alcuni approcci per implementare la rotazione.
Metodo 1: Selezione casuale da una lista
import requests
import random
# Lista di proxy
proxy_list = [
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
]
def get_random_proxy():
proxy = random.choice(proxy_list)
return {"http": proxy, "https": proxy}
# Scraping di 10 pagine con rotazione IP
urls = [f"https://example.com/page/{i}" for i in range(1, 11)]
for url in urls:
proxies = get_random_proxy()
try:
response = requests.get(url, proxies=proxies, timeout=10)
print(f"URL: {url} | IP: {proxies['http'].split('@')[1]} | Status: {response.status_code}")
except requests.RequestException as e:
print(f"Errore: {e}")
Metodo 2: Rotazione ciclica tramite itertools
import requests
import itertools
proxy_list = [
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
]
# Creiamo un ciclo infinito sulla lista di proxy
proxy_cycle = itertools.cycle(proxy_list)
def get_next_proxy():
proxy = next(proxy_cycle)
return {"http": proxy, "https": proxy}
# Ogni richiesta utilizza il prossimo proxy in ciclo
for i in range(9):
proxies = get_next_proxy()
response = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
print(f"Richiesta {i+1}: {response.json()['origin']}")
Per compiti industriali con migliaia di richieste all'ora, si consiglia di utilizzare proxy residenziali con rotazione automatica integrata: il fornitore cambia automaticamente l'IP per ogni richiesta attraverso un unico endpoint, e non è necessario gestire manualmente la lista degli indirizzi.
Proxy tramite Session: connessioni permanenti e cookie
Quando è necessario effettuare più richieste all'interno di una singola sessione (ad esempio, effettuare il login e poi fare richieste), utilizza l'oggetto requests.Session(). Questo mantiene i cookie, le intestazioni e le impostazioni del proxy tra le richieste — non è necessario passare il proxy in ogni chiamata separatamente.
import requests
# Creiamo una sessione con il proxy
session = requests.Session()
session.proxies = {
"http": "http://user:[email protected]:8080",
"https": "http://user:[email protected]:8080",
}
# Aggiungiamo intestazioni per imitare un browser
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Accept-Language": "ru-RU,ru;q=0.9",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
})
# Passo 1: Autenticazione
login_data = {"username": "myuser", "password": "mypass"}
session.post("https://example.com/login", data=login_data)
# Passo 2: Richieste già con i cookie e tramite proxy
response = session.get("https://example.com/dashboard")
print(response.status_code)
# Passo 3: Chiudiamo la sessione
session.close()
Utilizzare Session è anche più efficiente in termini di prestazioni: la connessione TCP viene riutilizzata, invece di essere riaperta per ogni richiesta. Durante lo scraping di oltre 1000 pagine, questo porta a un significativo aumento della velocità.
Gestione degli errori, dei timeout e ripetizioni automatiche
I server proxy possono essere non disponibili, rispondere lentamente o restituire errori di connessione. Uno script affidabile per lo scraping deve essere in grado di gestire queste situazioni e passare automaticamente a un altro proxy in caso di errore.
import requests
import random
import time
proxy_list = [
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
]
def fetch_with_retry(url, max_retries=3, timeout=10):
"""
Effettua una richiesta con cambio automatico del proxy in caso di errore.
Restituisce un oggetto Response o None al termine dei tentativi.
"""
available_proxies = proxy_list.copy()
random.shuffle(available_proxies)
for attempt, proxy_url in enumerate(available_proxies[:max_retries], 1):
proxies = {"http": proxy_url, "https": proxy_url}
try:
response = requests.get(
url,
proxies=proxies,
timeout=timeout,
headers={"User-Agent": "Mozilla/5.0"}
)
response.raise_for_status() # Solleva un'eccezione per 4xx/5xx
print(f"✓ Successo al tentativo {attempt}")
return response
except requests.exceptions.ProxyError:
print(f"✗ Tentativo {attempt}: proxy non disponibile — {proxy_url}")
except requests.exceptions.Timeout:
print(f"✗ Tentativo {attempt}: timeout — {proxy_url}")
except requests.exceptions.HTTPError as e:
print(f"✗ Tentativo {attempt}: errore HTTP {e.response.status_code}")
if e.response.status_code == 403:
print(" → Ban ricevuto, proviamo il prossimo proxy...")
except requests.exceptions.RequestException as e:
print(f"✗ Tentativo {attempt}: errore generale — {e}")
time.sleep(1) # Pausa tra i tentativi
print(f"✗ Tutti i {max_retries} tentativi esauriti per {url}")
return None
# Utilizzo
result = fetch_with_retry("https://httpbin.org/ip")
if result:
print(result.json())
Fai attenzione a raise_for_status() — questo metodo solleva automaticamente un'eccezione per gli stati HTTP 4xx e 5xx. Senza di esso, lo script considererà anche una risposta con codice 403 (ban) o 429 (limite di richieste superato) come riuscita.
Proxy tramite variabili d'ambiente: memorizzazione sicura dei dati
La libreria requests legge automaticamente le variabili d'ambiente HTTP_PROXY e HTTPS_PROXY. Questo consente di non memorizzare le credenziali nel codice e di passare facilmente tra i proxy senza modificare lo script.
Impostazione delle variabili nel terminale (Linux/macOS):
export HTTP_PROXY="http://user:[email protected]:8080" export HTTPS_PROXY="http://user:[email protected]:8080" export NO_PROXY="localhost,127.0.0.1"
Oppure tramite un file .env con la libreria python-dotenv:
# File .env (aggiungere a .gitignore!) HTTP_PROXY=http://user:[email protected]:8080 HTTPS_PROXY=http://user:[email protected]:8080
# Script Python
from dotenv import load_dotenv
import requests
import os
load_dotenv() # Carica le variabili da .env
# requests utilizza automaticamente HTTP_PROXY e HTTPS_PROXY
response = requests.get("https://httpbin.org/ip")
print(response.json())
# Oppure esplicitamente dalle variabili d'ambiente:
proxies = {
"http": os.getenv("HTTP_PROXY"),
"https": os.getenv("HTTPS_PROXY"),
}
response = requests.get("https://httpbin.org/ip", proxies=proxies)
print(response.json())
⚠️ Importante: variabile NO_PROXY
La variabile NO_PROXY consente di escludere determinati indirizzi dal proxy. Assicurati di aggiungere localhost e 127.0.0.1, affinché le richieste locali non passino attraverso il proxy.
Casi reali: scraping di marketplace, lavoro con API e automazione
Consideriamo tre scenari pratici che gli sviluppatori affrontano più frequentemente.
Scenario 1: Scraping dei prezzi da un marketplace
Quando si monitorano i prezzi su Wildberries o Ozon, è importante imitare il comportamento di un vero utente: inviare le intestazioni corrette del browser, aggiungere ritardi tra le richieste e ruotare gli IP. Per questo compito, i proxy dei data center sono molto adatti: sono veloci e economici per gestire grandi volumi di dati.
import requests
import time
import random
HEADERS = {
"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",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "ru-RU,ru;q=0.9",
"Referer": "https://www.wildberries.ru/",
}
PROXIES = [
{"http": "http://user:[email protected]:8080",
"https": "http://user:[email protected]:8080"},
{"http": "http://user:[email protected]:8080",
"https": "http://user:[email protected]:8080"},
]
def get_product_price(article_id: int) -> dict:
"""Ottiene il prezzo del prodotto per articolo da Wildberries."""
url = f"https://card.wb.ru/cards/v1/detail?appType=1&curr=rub&nm={article_id}"
proxies = random.choice(PROXIES)
try:
resp = requests.get(url, headers=HEADERS, proxies=proxies, timeout=15)
resp.raise_for_status()
data = resp.json()
product = data["data"]["products"][0]
return {
"id": product["id"],
"name": product["name"],
"price": product["salePriceU"] / 100, # prezzo in copechi
}
except (requests.RequestException, KeyError, IndexError) as e:
return {"error": str(e)}
# Scraping di diversi articoli con ritardo
articles = [12345678, 87654321, 11223344]
for article in articles:
result = get_product_price(article)
print(result)
time.sleep(random.uniform(1.5, 3.0)) # Ritardo casuale di 1.5-3 sec
Scenario 2: Lavoro con API tramite proxy
Alcune API limitano il numero di richieste da un singolo IP (rate limiting). Distribuire le richieste tra più proxy consente di aggirare questa limitazione:
import requests
import itertools
from typing import Optional
class ProxyAPIClient:
"""Client per lavorare con API tramite rotazione di proxy."""
def __init__(self, api_key: str, proxy_list: list):
self.api_key = api_key
self.proxy_cycle = itertools.cycle(proxy_list)
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
})
def _get_proxy(self) -> dict:
proxy = next(self.proxy_cycle)
return {"http": proxy, "https": proxy}
def get(self, url: str, **kwargs) -> Optional[dict]:
proxies = self._get_proxy()
try:
resp = self.session.get(url, proxies=proxies, timeout=10, **kwargs)
resp.raise_for_status()
return resp.json()
except requests.RequestException as e:
print(f"Richiesta API fallita: {e}")
return None
# Utilizzo
proxy_list = [
"http://user:[email protected]:8080",
"http://user:[email protected]:8080",
]
client = ProxyAPIClient(api_key="your_api_key", proxy_list=proxy_list)
data = client.get("https://api.example.com/products")
Scenario 3: Test di geolocalizzazione
I marketer e gli specialisti SEO controllano spesso come appare un sito da diverse regioni. Con i proxy delle località desiderate, è possibile automatizzare questo processo:
import requests
# Proxy da diverse regioni
regional_proxies = {
"Mosca": "http://user:[email protected]:8080",
"San Pietroburgo": "http://user:[email protected]:8080",
"Novosibirsk": "http://user:[email protected]:8080",
"USA": "http://user:[email protected]:8080",
}
url = "https://example.com/prices"
for region, proxy_url in regional_proxies.items():
proxies = {"http": proxy_url, "https": proxy_url}
try:
resp = requests.get(url, proxies=proxies, timeout=15)
print(f"[{region}] Status: {resp.status_code} | "
f"Dimensione: {len(resp.content)} byte")
except requests.RequestException as e:
print(f"[{region}] Errore: {e}")
Quale tipo di proxy scegliere per il proprio compito
La scelta del tipo di proxy influisce direttamente sul successo del tuo progetto. Un proxy economico dei data center può funzionare bene per alcuni compiti e fallire completamente per altri. Ecco una guida pratica per la scelta:
| Compito | Tipo di proxy | Perché |
|---|---|---|
| Scraping di marketplace (Wildberries, Ozon) | Residenziali | Appaiono come utenti normali, vengono bannati meno frequentemente |
| Scraping di dati aperti, notizie | Data center | Veloci, economici, abbastanza anonimi |
| Lavoro con API di Facebook, Instagram | Mobili | I social media si fidano di più degli IP mobili |
| Test di geolocalizzazione | Residenziali con geotargeting | Geolocalizzazione precisa, IP reali della regione desiderata |
| Scraping ad alta intensità (10k+ richieste/ora) | Data center (pool) | Velocità e costo per grandi volumi |
| Autenticazione e lavoro con account | Residenziali o mobili | Meno trigger per i sistemi antifrode |
Per compiti in cui è massima l'affidabilità e il rischio di blocco è minimo durante il lavoro con siti protetti, gli sviluppatori scelgono più frequentemente proxy mobili — utilizzano indirizzi IP di veri operatori mobili (MTS, Beeline, MegaFon), che raramente finiscono nelle blacklist.
Checklist per il controllo dei proxy prima dell'uso
- ✅ Controlla l'IP tramite
httpbin.org/ip— è visibile il tuo vero indirizzo? - ✅ Controlla la velocità — il tempo di risposta non deve superare 2-3 secondi
- ✅ Assicurati che il proxy non sia nelle blacklist tramite
blocklist.deoipqualityscore.com - ✅ Controlla la geolocalizzazione tramite
ipinfo.io— corrisponde alla regione attesa? - ✅ Testa sul sito target con una sola richiesta prima di avviare l'intero script
- ✅ Assicurati che anche il traffico HTTPS passi attraverso il proxy (entrambe le chiavi nel dizionario)
Conclusione
Configurare i proxy in Python requests non è difficile, ma richiede attenzione ai dettagli. I principi principali da ricordare sono: specifica sempre entrambe le chiavi (http e https) nel dizionario dei proxy, utilizza Session per scenari multi-passaggio, gestisci sempre gli errori e i timeout, e memorizza le credenziali nelle variabili d'ambiente, non nel codice.
Per lo scraping industriale con migliaia di richieste al giorno, una lista manuale di proxy non è sufficiente — è necessaria la rotazione. Se stai facendo scraping di marketplace protetti come Wildberries o Ozon, lavori con i social media o testi la geolocalizzazione, ti consigliamo di provare i proxy residenziali — offrono un alto livello di fiducia da parte dei sistemi anti-bot e supportano la rotazione automatica degli IP attraverso un unico endpoint, semplificando notevolmente il codice del tuo script.