Cosa fare se i proxy vengono costantemente bannati: guida completa alla diagnosi e alla risoluzione dei problemi
I blocchi costanti dei proxy sono uno dei problemi più frequenti durante il web scraping, l'automazione e il lavoro con più account. In questo articolo analizzeremo perché accade e come risolvere il problema in modo sistematico, invece di cambiare continuamente provider nella speranza di un miracolo.
Perché i proxy vengono effettivamente bannati
Prima di cercare una soluzione, è necessario comprendere il meccanismo dei blocchi. I sistemi antifrode moderni utilizzano una protezione multilivello, e il ban del proxy è solo una conseguenza, non la causa. Comprendere come funzionano questi sistemi consente di sviluppare una strategia di aggiramento efficace.
Reputazione dell'IP e liste nere
Ogni indirizzo IP ha una reputazione formata sulla base della storia del suo utilizzo. Se l'indirizzo è stato precedentemente utilizzato per spam, attacchi DDoS o scraping massivo, finisce nei database come Spamhaus, SORBS o negli elenchi proprietari di servizi specifici. Quando ti connetti tramite tale IP, il sistema ti tratta immediatamente con sospetto.
I proxy dei data center sono particolarmente soggetti a questo problema. Intere sottoreti possono essere contrassegnate come "hosting", e tutto il traffico da esse viene automaticamente sottoposto a un livello di controllo più elevato. Amazon AWS, Google Cloud, DigitalOcean — i loro intervalli IP sono ben noti e spesso bloccati preventivamente.
Puoi verificare la reputazione dell'IP tramite servizi come IPQualityScore, Scamalytics o AbuseIPDB. Se il tuo proxy mostra un fraud score superiore a 75, il problema è proprio questo — cambia provider o tipo di proxy.
Modelli di richieste
Un essere umano non effettua 100 richieste al secondo. Un essere umano non naviga tra le pagine con una periodicità perfetta di 2 secondi. Un essere umano non ignora immagini, CSS e JavaScript, richiedendo solo HTML. I sistemi antifrode analizzano esattamente questi modelli, e qualsiasi deviazione dal comportamento "umano" aumenta il rischio di blocco.
Particolarmente rivelatrice è la statistica del tempo tra le richieste. Se hai un intervallo stabile, è un chiaro segno di automazione. L'aggiunta di un ritardo casuale (ad esempio, da 1 a 5 secondi) riduce significativamente la probabilità di rilevamento.
Mancata corrispondenza dei metadati
Quando il tuo User-Agent dice che stai usando Chrome su Windows, ma gli header HTTP tradiscono le caratteristiche di Python requests — è una bandiera rossa. Quando l'indirizzo IP si geolocalizza in Germania, ma le impostazioni della lingua del browser indicano il russo — un'altra bandiera rossa. Quando il fuso orario in JavaScript non corrisponde alla geografia dell'IP — una terza bandiera.
L'accumulo di tali incongruenze porta il sistema a classificare la connessione come sospetta e ad applicare misure protettive: da captcha a blocco completo dell'IP.
Impronta del browser
I moderni sistemi di protezione raccolgono decine di parametri del browser: risoluzione dello schermo, font installati, plugin, rendering WebGL, contesto audio e molto altro. L'insieme di questi parametri crea un "impronta" unica che rimane costante anche quando cambi IP.
Se cambi proxy ma l'impronta rimane la stessa, il sistema capisce che sei lo stesso utente. E se un'impronta appare da centinaia di IP diversi in poco tempo — è un chiaro segno di automazione.
Diagnosi: come comprendere la causa del blocco
Prima di modificare le impostazioni a caso, conduci una diagnosi. Questo ti farà risparmiare ore di esperimenti e ti aiuterà a trovare la vera causa del problema. Un approccio sistematico alla diagnosi è la chiave per una soluzione efficace.
Passaggio 1: Verifica il proxy stesso
Inizia con una verifica di base della funzionalità del proxy indipendentemente dal tuo script principale:
import requests
proxy = {
"http": "http://user:pass@proxy-server:port",
"https": "http://user:pass@proxy-server:port"
}
# Verifica della funzionalità di base
try:
response = requests.get("https://httpbin.org/ip", proxies=proxy, timeout=10)
print(f"IP tramite proxy: {response.json()['origin']}")
except Exception as e:
print(f"Errore di connessione: {e}")
# Verifica della perdita dell'IP reale
response = requests.get("https://browserleaks.com/ip", proxies=proxy)
# Confronta con il tuo IP reale
Se il proxy non funziona nemmeno su richieste semplici — il problema è nel proxy stesso o nelle credenziali. Verifica la correttezza del formato di connessione, la disponibilità di fondi nel saldo e i limiti del provider.
Passaggio 2: Verifica la reputazione dell'IP
Utilizza diversi servizi per una valutazione completa:
# Ottieni l'IP del proxy
proxy_ip = requests.get("https://api.ipify.org", proxies=proxy).text
# Verifica su questi servizi:
# https://www.ipqualityscore.com/free-ip-lookup-proxy-vpn-test
# https://scamalytics.com/ip/{proxy_ip}
# https://www.abuseipdb.com/check/{proxy_ip}
# https://whatismyipaddress.com/ip/{proxy_ip}
print(f"Verifica l'IP {proxy_ip} sui servizi sopra")
Presta attenzione ai seguenti indicatori: fraud score (dovrebbe essere inferiore a 50), tipo di IP (residenziale è meglio di data center), presenza in liste nere. Se l'IP è contrassegnato come VPN/Proxy — molti siti lo tratteranno con sospetto fin dall'inizio.
Passaggio 3: Isola il problema
Prova lo stesso proxy su diversi siti di destinazione. Se il blocco avviene ovunque — il problema è nel proxy o nella tua configurazione. Se solo su un sito specifico — il problema è nella protezione di quel sito o nel tuo comportamento su di esso.
Prova anche diversi proxy sullo stesso sito. Se vengono tutti bloccati — il problema non è nel proxy, ma nel tuo script, impronta o modello di comportamento. Questo è un test critico che molti saltano.
Passaggio 4: Analisi delle risposte del server
Diversi tipi di blocchi si manifestano diversamente. Impara a distinguerli:
def analyze_response(response):
status = response.status_code
if status == 403:
print("Accesso vietato — possibile IP in lista nera")
elif status == 429:
print("Troppi richieste — riduci la frequenza")
elif status == 503:
print("Servizio non disponibile — possibile protezione DDoS")
elif status == 407:
print("Autenticazione proxy richiesta — verifica le credenziali")
elif "captcha" in response.text.lower():
print("Captcha rilevata — sospetto di bot")
elif "blocked" in response.text.lower():
print("Blocco esplicito — cambia IP e riconsiderai l'approccio")
elif len(response.text) < 1000:
print("Risposta sospettosamente breve — possibile stub")
else:
print(f"Stato {status}, lunghezza risposta: {len(response.text)}")
Rotazione corretta: frequenza, logica, implementazione
La rotazione del proxy non è semplicemente "cambiare IP più spesso". Una rotazione scorretta può fare più male che bene. Consideriamo diverse strategie e quando applicarle.
Strategia 1: Rotazione per numero di richieste
L'approccio più semplice — cambiare IP dopo un certo numero di richieste. Adatto per lo scraping dove non è necessaria la sessione:
import random
class ProxyRotator:
def __init__(self, proxy_list, requests_per_proxy=50):
self.proxies = proxy_list
self.requests_per_proxy = requests_per_proxy
self.current_proxy = None
self.request_count = 0
def get_proxy(self):
if self.current_proxy is None or self.request_count >= self.requests_per_proxy:
# Aggiungiamo casualità al numero di richieste
self.requests_per_proxy = random.randint(30, 70)
self.current_proxy = random.choice(self.proxies)
self.request_count = 0
self.request_count += 1
return self.current_proxy
# Utilizzo
rotator = ProxyRotator(proxy_list)
for url in urls_to_scrape:
proxy = rotator.get_proxy()
response = requests.get(url, proxies={"http": proxy, "https": proxy})
Nota la casualità nel numero di richieste per proxy. Un numero fisso (ad esempio, esattamente 50) è un modello che può essere rilevato. Un intervallo casuale rende il comportamento meno prevedibile.
Strategia 2: Rotazione per tempo
Per compiti dove la sessione è importante (ad esempio, lavoro con account), è meglio legare l'IP al tempo:
import time
import random
class TimeBasedRotator:
def __init__(self, proxy_list, min_minutes=10, max_minutes=30):
self.proxies = proxy_list
self.min_seconds = min_minutes * 60
self.max_seconds = max_minutes * 60
self.current_proxy = None
self.rotation_time = 0
def get_proxy(self):
current_time = time.time()
if self.current_proxy is None or current_time >= self.rotation_time:
self.current_proxy = random.choice(self.proxies)
# Intervallo casuale fino alla prossima rotazione
interval = random.randint(self.min_seconds, self.max_seconds)
self.rotation_time = current_time + interval
print(f"Nuovo proxy, prossima rotazione tra {interval//60} minuti")
return self.current_proxy
Strategia 3: Sessioni sticky per account
Quando lavori con più account, è fondamentale che ogni account utilizzi un IP costante. Cambiare IP per un account loggato è il modo sicuro per ottenere un ban:
class AccountProxyManager:
def __init__(self, proxy_pool):
self.proxy_pool = proxy_pool
self.account_proxies = {} # account_id -> proxy
self.used_proxies = set()
def get_proxy_for_account(self, account_id):
# Se all'account è già assegnato un proxy — lo restituiamo
if account_id in self.account_proxies:
return self.account_proxies[account_id]
# Troviamo un proxy libero
available = [p for p in self.proxy_pool if p not in self.used_proxies]
if not available:
raise Exception("Nessun proxy disponibile per nuovi account")
proxy = random.choice(available)
self.account_proxies[account_id] = proxy
self.used_proxies.add(proxy)
return proxy
def release_account(self, account_id):
"""Libera il proxy quando l'account viene eliminato"""
if account_id in self.account_proxies:
proxy = self.account_proxies.pop(account_id)
self.used_proxies.discard(proxy)
# Utilizzo
manager = AccountProxyManager(residential_proxy_list)
for account in accounts:
proxy = manager.get_proxy_for_account(account.id)
# Tutte le azioni di questo account vanno attraverso un IP
Strategia 4: Rotazione adattiva
L'approccio più avanzato — cambiare proxy in risposta ai segnali dal sito di destinazione:
class AdaptiveRotator:
def __init__(self, proxy_list):
self.proxies = proxy_list
self.current_proxy = random.choice(proxy_list)
self.proxy_scores = {p: 100 for p in proxy_list} # "Salute" iniziale del proxy
def get_proxy(self):
return self.current_proxy
def report_result(self, success, response_code=200):
"""Chiamato dopo ogni richiesta"""
if success and response_code == 200:
# Richiesta riuscita — aumentiamo leggermente lo score
self.proxy_scores[self.current_proxy] = min(100,
self.proxy_scores[self.current_proxy] + 1)
elif response_code == 429:
# Rate limit — riduciamo molto e ruotiamo
self.proxy_scores[self.current_proxy] -= 30
self._rotate()
elif response_code == 403:
# Ban — azzeriamo lo score e ruotiamo
self.proxy_scores[self.current_proxy] = 0
self._rotate()
elif response_code == 503:
# Possibile protezione — riduciamo e ruotiamo
self.proxy_scores[self.current_proxy] -= 20
self._rotate()
def _rotate(self):
# Selezioniamo il proxy con il miglior score
available = [(p, s) for p, s in self.proxy_scores.items() if s > 20]
if not available:
# Tutti i proxy sono "morti" — resettiamo gli score
self.proxy_scores = {p: 50 for p in self.proxies}
available = list(self.proxy_scores.items())
# Selezione ponderata per score
self.current_proxy = max(available, key=lambda x: x[1])[0]
print(f"Rotazione al proxy con score {self.proxy_scores[self.current_proxy]}")
Impronta del browser e il suo ruolo nei blocchi
L'impronta è l'insieme delle caratteristiche del browser che consente di identificare l'utente anche senza cookie. Se cambi IP ma l'impronta rimane la stessa, il sistema di protezione collega facilmente tutte le tue sessioni.
Di cosa consiste l'impronta
L'impronta moderna include decine di parametri. Ecco le categorie principali:
| Categoria | Parametri | Peso nell'identificazione |
|---|---|---|
| User-Agent | Browser, versione, SO | Medio |
| Schermo | Risoluzione, profondità colore, pixel ratio | Medio |
| Font | Elenco dei font installati | Alto |
| WebGL | Renderer, vendor, hash del rendering | Molto alto |
| Canvas | Hash dell'immagine disegnata | Molto alto |
| Audio | AudioContext fingerprint | Alto |
| Fuso orario | Fuso orario, offset | Medio |
| Lingue | navigator.languages | Medio |
| Plugin | navigator.plugins | Basso (nei browser moderni) |
Coerenza tra impronta e IP
È fondamentale che l'impronta corrisponda alla geografia dell'IP. Se il proxy si trova in Germania, l'impronta dovrebbe sembrare un utente tedesco:
// Esempio di incoerenza (MALE):
// IP: Germania
// Fuso orario: America/New_York
// Lingue: ["ru-RU", "ru"]
// Questo causerà sospetto
// Impronta coerente (BENE):
// IP: Germania
// Fuso orario: Europe/Berlin
// Lingue: ["de-DE", "de", "en-US", "en"]
Strumenti per gestire l'impronta
Per un lavoro serio utilizza strumenti specializzati:
Playwright con Stealth:
from playwright.sync_api import sync_playwright
from playwright_stealth import stealth_sync
with sync_playwright() as p:
browser = p.chromium.launch(
proxy={"server": "http://proxy:port", "username": "user", "password": "pass"}
)
context = browser.new_context(
viewport={"width": 1920, "height": 1080},
locale="de-DE",
timezone_id="Europe/Berlin",
geolocation={"latitude": 52.52, "longitude": 13.405},
permissions=["geolocation"]
)
page = context.new_page()
stealth_sync(page) # Applichiamo patch stealth
page.goto("https://target-site.com")
Puppeteer con puppeteer-extra:
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
const browser = await puppeteer.launch({
args: [`--proxy-server=http://proxy:port`]
});
const page = await browser.newPage();
// Sovrascriviamo il fuso orario
await page.evaluateOnNewDocument(() => {
Object.defineProperty(Intl.DateTimeFormat.prototype, 'resolvedOptions', {
value: function() {
return { timeZone: 'Europe/Berlin' };
}
});
});
Browser anti-rilevamento
Per il lavoro con account spesso si usano browser anti-rilevamento (Multilogin, GoLogin, Dolphin Anty e altri). Permettono di creare profili isolati con impronta unica. Ogni profilo ha il suo set di parametri, cookie, localStorage — un ambiente completamente isolato.
Il vantaggio dei browser anti-rilevamento è che risolvono il problema dell'impronta "out of the box". Lo svantaggio è il costo e la complessità dell'automazione (anche se molti hanno API).
Modelli comportamentali: come non sembrare un bot
Anche con un'impronta perfetta e un IP pulito, puoi ricevere un ban a causa di un comportamento non umano. I sistemi moderni analizzano non solo i parametri tecnici, ma anche i modelli di interazione con il sito.
Ritardi temporali
Un essere umano non effettua richieste con intervalli costanti. Aggiungi ritardi casuali con distribuzione normale:
import random
import time
import numpy as np
def human_delay(min_sec=1, max_sec=5, mean=2.5):
"""
Genera un ritardo simile a quello umano.
Utilizza distribuzione lognormale —
la maggior parte dei ritardi è breve, ma a volte ce ne sono di lunghi.
"""
delay = np.random.lognormal(mean=np.log(mean), sigma=0.5)
delay = max(min_sec, min(max_sec, delay))
return delay
def human_typing_delay():
"""Ritardo tra i tasti quando si digita il testo"""
return random.uniform(0.05, 0.25)
# Utilizzo
for url in urls:
response = requests.get(url, proxies=proxy)
process(response)
time.sleep(human_delay()) # Pausa casuale tra le richieste
Imitazione della navigazione
Un essere umano non va direttamente a una pagina di prodotto tramite un link diretto. Entra nella home page, usa la ricerca, sfoglia le categorie. Imita questo percorso:
async def human_like_navigation(page, target_url):
"""Imita la navigazione umana verso la pagina di destinazione"""
# 1. Andiamo alla home page
await page.goto("https://example.com")
await page.wait_for_timeout(random.randint(2000, 4000))
# 2. A volte scorriamo la home page
if random.random() > 0.5:
await page.evaluate("window.scrollBy(0, 300)")
await page.wait_for_timeout(random.randint(1000, 2000))
# 3. Usiamo la ricerca o la navigazione
if random.random() > 0.3:
search_box = await page.query_selector('input[type="search"]')
if search_box:
await search_box.type("search query", delay=100)
await page.keyboard.press("Enter")
await page.wait_for_timeout(random.randint(2000, 4000))
# 4. Andiamo alla pagina di destinazione
await page.goto(target_url)
# 5. Scorriamo la pagina come un essere umano
await human_scroll(page)
async def human_scroll(page):
"""Imita lo scorrimento umano"""
scroll_height = await page.evaluate("document.body.scrollHeight")
current_position = 0
while current_position < scroll_height * 0.7: # Non fino in fondo
scroll_amount = random.randint(200, 500)
await page.evaluate(f"window.scrollBy(0, {scroll_amount})")
current_position += scroll_amount
await page.wait_for_timeout(random.randint(500, 1500))
Movimenti del mouse
Alcuni sistemi tracciano i movimenti del mouse. Il movimento rettilineo dal punto A al punto B è un segno di bot. Un essere umano muove il mouse lungo una curva con micro-correzioni:
import bezier
import numpy as np
def generate_human_mouse_path(start, end, num_points=50):
"""
Genera un percorso del mouse simile a quello umano,
utilizzando curve di Bezier con leggero rumore.
"""
# Punti di controllo per la curva di Bezier
control1 = (
start[0] + (end[0] - start[0]) * random.uniform(0.2, 0.4) + random.randint(-50, 50),
start[1] + (end[1] - start[1]) * random.uniform(0.2, 0.4) + random.randint(-50, 50)
)
control2 = (
start[0] + (end[0] - start[0]) * random.uniform(0.6, 0.8) + random.randint(-50, 50),
start[1] + (end[1] - start[1]) * random.uniform(0.6, 0.8) + random.randint(-50, 50)
)
# Creiamo la curva di Bezier
nodes = np.asfortranarray([
[start[0], control1[0], control2[0], end[0]],
[start[1], control1[1], control2[1], end[1]]
])
curve = bezier.Curve(nodes, degree=3)
# Generiamo punti sulla curva
points = []
for t in np.linspace(0, 1, num_points):
point = curve.evaluate(t)
# Aggiungiamo micro-rumore
x = point[0][0] + random.uniform(-2, 2)
y = point[1][0] + random.uniform(-2, 2)
points.append((x, y))
return points
async def human_click(page, selector):
"""Clicca su un elemento con movimento del mouse simile a quello umano"""
element = await page.query_selector(selector)
box = await element.bounding_box()
# Punto di destinazione — non il centro, ma un punto casuale all'interno dell'elemento
target_x = box['x'] + random.uniform(box['width'] * 0.2, box['width'] * 0.8)
target_y = box['y'] + random.uniform(box['height'] * 0.2, box['height'] * 0.8)
# Posizione attuale del mouse (o punto di partenza casuale)
start_x = random.randint(0, 1920)
start_y = random.randint(0, 1080)
# Generiamo il percorso
path = generate_human_mouse_path((start_x, start_y), (target_x, target_y))
# Muoviamo il mouse lungo il percorso
for x, y in path:
await page.mouse.move(x, y)
await page.wait_for_timeout(random.randint(5, 20))
# Piccola pausa prima del clic
await page.wait_for_timeout(random.randint(50, 150))
await page.mouse.click(target_x, target_y)
Caricamento delle risorse
Un vero browser carica non solo HTML, ma anche CSS, JavaScript, immagini, font. Se usi requests e richiedi solo HTML — è sospetto. Quando lavori con browser headless questo problema viene risolto automaticamente, ma quando usi client HTTP devi considerare questo aspetto.
Scelta del tipo di proxy per il tuo compito
Diversi tipi di proxy hanno caratteristiche diverse e sono adatti per diversi compiti. Una scelta sbagliata è una causa frequente di blocchi.
Proxy data center
I proxy data center sono indirizzi IP appartenenti a provider di hosting. Possono essere identificati dall'appartenenza a AS (sistemi autonomi) di grandi data center.
Vantaggi:
- Alta velocità e stabilità
- Basso costo
- Grandi pool di IP
Svantaggi:
- Facili da rilevare
- Spesso in liste nere
- Non adatti per siti con protezione seria
Adatti per: strumenti SEO, verifica della disponibilità, lavoro con API senza protezione rigorosa, test.
Proxy residenziali
I proxy residenziali sono indirizzi IP di utenti reali, forniti tramite programmi partner o SDK in app. Appartengono a provider Internet ordinari (ISP).
Vantaggi:
- Sembrano utenti ordinari
- Basso fraud score
- Ampia geografia
- Difficili da rilevare
Svantaggi:
- Costo più alto (pagamento per traffico)
- La velocità dipende dall'utente finale
- Gli IP possono "andarsene" (l'utente ha spento il dispositivo)
Adatti per: scraping di siti protetti, lavoro con social media, e-commerce, qualsiasi compito dove è importante non essere rilevati.
Proxy mobili
I proxy mobili sono indirizzi IP di operatori mobili (MTS, Beeline, Megafon e analoghi in altri paesi). Hanno uno status speciale a causa della tecnologia CGNAT.
Vantaggi:
- Massima fiducia da parte dei siti
- Un IP è utilizzato da migliaia di utenti reali — difficile da bannare
- Ideali per il lavoro con account
- Cambio di IP su richiesta (riconnessione alla rete)
Svantaggi:
- Costo più alto
- Velocità limitata
- Meno scelta di geografie
Adatti per: multi-accounting, lavoro con Instagram/Facebook/TikTok, registrazione di account, qualsiasi compito con alto rischio di ban.
Tabella comparativa
| Parametro | Data center | Residenziali | Mobili |
|---|---|---|---|
| Rilevabilità | Alta | Bassa | Molto bassa |
| Velocità | Alta | Media | Bassa-media |
| Costo | $ | $$ | $$$ |
| Per social media | Non adatti | Adatti | Ideali |
| Per scraping | Siti semplici | Qualsiasi sito | Eccessivo |
Tecniche avanzate per aggirare la protezione
Quando i metodi di base non funzionano, è necessario utilizzare tecniche più complesse. Consideriamo diversi approcci avanzati.
Lavoro con Cloudflare e protezioni simili
Cloudflare, Akamai, PerimeterX — questi sistemi utilizzano sfide JavaScript per verificare il browser. Una semplice richiesta HTTP non passerà. Opzioni di soluzione:
1. Utilizzo di un vero browser:
from playwright.sync_api import sync_playwright
def bypass_cloudflare(url, proxy):
with sync_playwright() as p:
browser = p.chromium.launch(
headless=False, # A volte headless viene rilevato
proxy={"server": proxy}
)
page = browser.new_page()
page.goto(url)
# Aspettiamo il passaggio della verifica (di solito 5-10 secondi)
page.wait_for_timeout(10000)
# Verifichiamo se abbiamo passato
if "challenge" not in page.url:
# Salviamo i cookie per le richieste successive
cookies = page.context.cookies()
return cookies
browser.close()
return None
2. Utilizzo di soluzioni pronte:
# cloudscraper — libreria per aggirare Cloudflare
import cloudscraper
scraper = cloudscraper.create_scraper(
browser={
'browser': 'chrome',
'platform': 'windows',
'desktop': True
}
)
scraper.proxies = {"http": proxy, "https": proxy}
response = scraper.get("https://protected-site.com")
Risoluzione del captcha
Se il sito mostra un captcha, ci sono diversi approcci:
Servizi di riconoscimento: 2Captcha, Anti-Captcha, CapMonster. Risolvono il captcha per te