Protezione contro i blocchi durante le richieste di massa: tecniche e strumenti
Il blocco di account e indirizzi IP è il principale problema durante il parsing, l'automazione e le operazioni di massa sui social media. I moderni sistemi anti-bot analizzano decine di parametri: dalla frequenza delle richieste alle impronte del browser. In questa guida analizzeremo i meccanismi specifici di rilevamento dell'automazione e i modi pratici per bypassarli.
Meccanismi di rilevamento dell'automazione
I moderni sistemi di protezione utilizzano un'analisi multilivello per identificare i bot. Comprendere questi meccanismi è fondamentale per scegliere la giusta strategia di bypass.
Parametri principali dell'analisi
Reputazione IP: I sistemi anti-bot controllano la storia dell'indirizzo IP, l'appartenenza ai data center e la presenza in blacklist. Gli IP provenienti da pool di proxy noti vengono bloccati più frequentemente.
Frequenza delle richieste (Request Rate): Un essere umano non può fisicamente inviare 100 richieste al minuto. I sistemi analizzano non solo il numero totale, ma anche la distribuzione nel tempo: intervalli uniformi tra le richieste rivelano un bot.
Modelli di comportamento: Sequenza di azioni, profondità di scorrimento, movimenti del mouse, tempo sulla pagina. Un bot che passa istantaneamente da un link all'altro senza ritardi è facilmente riconoscibile.
Impronte tecniche: User-Agent, intestazioni HTTP, ordine delle intestazioni, impronta TLS, fingerprinting Canvas/WebGL. Discrepanze in questi parametri sono un campanello d'allarme per i sistemi anti-bot.
| Parametro | Cosa viene analizzato | Rischio di rilevamento |
|---|---|---|
| Indirizzo IP | Reputazione, ASN, geolocalizzazione | Alto |
| User-Agent | Versione del browser, OS, dispositivo | Medio |
| Impronta TLS | Set di cifrature, estensioni | Alto |
| Impronta HTTP/2 | Ordine delle intestazioni, impostazioni | Alto |
| Canvas/WebGL | Rendering grafico | Medio |
| Comportamento | Clic, scorrimento, tempo | Alto |
Limitazione della velocità e controllo della frequenza delle richieste
Il controllo della velocità delle richieste è la prima linea di difesa contro i blocchi. Anche con la rotazione dei proxy, un parsing troppo aggressivo porterà a un ban.
Ritardi dinamici
Intervalli fissi (ad esempio, esattamente 2 secondi tra le richieste) sono facilmente riconoscibili. Utilizzare ritardi casuali con distribuzione normale:
import time
import random
import numpy as np
def human_delay(min_delay=1.5, max_delay=4.0, mean=2.5, std=0.8):
"""
Generazione di un ritardo con distribuzione normale
che imita il comportamento umano
"""
delay = np.random.normal(mean, std)
# Limitare l'intervallo
delay = max(min_delay, min(delay, max_delay))
# Aggiungere micro-ritardi per realismo
delay += random.uniform(0, 0.3)
time.sleep(delay)
# Utilizzo
for url in urls:
response = session.get(url)
human_delay(min_delay=2, max_delay=5, mean=3, std=1)
Limitazione della velocità adattiva
Un approccio più avanzato è l'adattamento della velocità in base alle risposte del server. Se si ricevono codici 429 (Troppe richieste) o 503, ridurre automaticamente il ritmo:
class AdaptiveRateLimiter:
def __init__(self, initial_delay=2.0):
self.current_delay = initial_delay
self.min_delay = 1.0
self.max_delay = 30.0
self.error_count = 0
def wait(self):
time.sleep(self.current_delay + random.uniform(0, 0.5))
def on_success(self):
# Acceleriamo gradualmente in caso di richieste riuscite
self.current_delay = max(
self.min_delay,
self.current_delay * 0.95
)
self.error_count = 0
def on_rate_limit(self):
# Rallentiamo drasticamente in caso di blocco
self.error_count += 1
self.current_delay = min(
self.max_delay,
self.current_delay * (1.5 + self.error_count * 0.5)
)
print(f"Limite di velocità raggiunto. Nuovo ritardo: {self.current_delay:.2f}s")
# Applicazione
limiter = AdaptiveRateLimiter(initial_delay=2.0)
for url in urls:
limiter.wait()
response = session.get(url)
if response.status_code == 429:
limiter.on_rate_limit()
time.sleep(60) # Pausa prima del ripristino
elif response.status_code == 200:
limiter.on_success()
else:
# Gestione di altri errori
pass
Consiglio pratico: Per diversi siti, la velocità ottimale varia. Le grandi piattaforme (Google, Facebook) tollerano 5-10 richieste al minuto da un solo IP. I siti più piccoli possono bloccare già a 20-30 richieste all'ora. Iniziate sempre in modo conservativo e aumentate gradualmente il carico, monitorando la percentuale di errori.
Rotazione dei proxy e gestione degli indirizzi IP
Utilizzare un solo indirizzo IP per richieste di massa garantisce il blocco. La rotazione dei proxy distribuisce il carico e riduce il rischio di rilevamento.
Strategie di rotazione
1. Rotazione per richieste: Cambio IP dopo ogni richiesta o ogni N richieste. Adatto per il parsing dei motori di ricerca, dove l'anonimato di ogni richiesta è importante.
2. Rotazione per tempo: Cambio IP ogni 5-15 minuti. Efficace per lavorare con i social media, dove la stabilità della sessione è importante.
3. Sessioni sticky: Utilizzo di un solo IP per l'intera sessione utente (autenticazione, sequenza di azioni). Critico per i siti con protezione CSRF.
import requests
from itertools import cycle
class ProxyRotator:
def __init__(self, proxy_list, rotation_type='request', rotation_interval=10):
"""
rotation_type: 'request' (ogni richiesta) o 'time' (per tempo)
rotation_interval: numero di richieste o secondi
"""
self.proxies = cycle(proxy_list)
self.current_proxy = next(self.proxies)
self.rotation_type = rotation_type
self.rotation_interval = rotation_interval
self.request_count = 0
self.last_rotation = time.time()
def get_proxy(self):
if self.rotation_type == 'request':
self.request_count += 1
if self.request_count >= self.rotation_interval:
self.current_proxy = next(self.proxies)
self.request_count = 0
print(f"Ruotato a: {self.current_proxy}")
elif self.rotation_type == 'time':
if time.time() - self.last_rotation >= self.rotation_interval:
self.current_proxy = next(self.proxies)
self.last_rotation = time.time()
print(f"Ruotato a: {self.current_proxy}")
return {'http': self.current_proxy, 'https': self.current_proxy}
# Esempio di utilizzo
proxy_list = [
'http://user:pass@proxy1.example.com:8000',
'http://user:pass@proxy2.example.com:8000',
'http://user:pass@proxy3.example.com:8000',
]
rotator = ProxyRotator(proxy_list, rotation_type='request', rotation_interval=5)
for url in urls:
proxies = rotator.get_proxy()
response = requests.get(url, proxies=proxies, timeout=10)
Scelta del tipo di proxy
| Tipo di proxy | Livello di fiducia | Velocità | Applicazione |
|---|---|---|---|
| Data center | Basso | Alto | Parsing semplice, API |
| Residenziali | Alto | Medio | Social media, siti protetti |
| Mobili | Molto alto | Medio | Instagram, TikTok, antifrode |
Per operazioni di massa sui social media e su piattaforme con protezioni serie, utilizzare proxy residenziali. Questi appaiono come normali connessioni domestiche e raramente finiscono nelle blacklist. I data center sono adatti per risorse meno protette, dove la velocità è importante.
Fingerprinting del browser e impronte TLS
Anche con la rotazione degli IP, si può essere identificati tramite impronte tecniche del browser e della connessione TLS. Questi parametri sono unici per ogni cliente e difficili da falsificare.
Fingerprinting TLS
Durante l'instaurazione di una connessione HTTPS, il client invia un ClientHello con un insieme di cifrature e estensioni supportate. Questa combinazione è unica per ogni libreria. Ad esempio, Python requests utilizza OpenSSL, la cui impronta è facilmente distinguibile da quella di Chrome.
Problema: Le librerie standard (requests, urllib, curl) hanno impronte diverse rispetto ai veri browser. Servizi come Cloudflare, Akamai, DataDome utilizzano attivamente il fingerprinting TLS per bloccare i bot.
Soluzione: Utilizzare librerie che imitano le impronte TLS dei browser. Per Python, questo include curl_cffi, tls_client o playwright/puppeteer per un'emulazione completa del browser.
# Installazione: pip install curl-cffi
from curl_cffi import requests
# Imitazione di Chrome 110
response = requests.get(
'https://example.com',
impersonate="chrome110",
proxies={'https': 'http://proxy:port'}
)
# Alternativa: tls_client
import tls_client
session = tls_client.Session(
client_identifier="chrome_108",
random_tls_extension_order=True
)
response = session.get('https://example.com')
Fingerprinting HTTP/2
Oltre al TLS, i sistemi anti-bot analizzano i parametri HTTP/2: ordine delle intestazioni, impostazioni del frame SETTINGS, priorità dei flussi. Le librerie standard non rispettano l'ordine esatto delle intestazioni di Chrome o Firefox.
# Ordine corretto delle intestazioni per Chrome
headers = {
':method': 'GET',
':authority': 'example.com',
':scheme': 'https',
':path': '/',
'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'upgrade-insecure-requests': '1',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)...',
'accept': 'text/html,application/xhtml+xml...',
'sec-fetch-site': 'none',
'sec-fetch-mode': 'navigate',
'sec-fetch-user': '?1',
'sec-fetch-dest': 'document',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'en-US,en;q=0.9',
}
Fingerprinting Canvas e WebGL
I browser rendono la grafica in modo diverso a seconda della GPU, dei driver e del sistema operativo. I siti utilizzano questo per creare un'impronta unica del dispositivo. Quando si utilizzano browser headless (Selenium, Puppeteer), è importante mascherare i segni di automazione:
// Puppeteer: nascondere la modalità headless
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
const browser = await puppeteer.launch({
headless: true,
args: [
'--disable-blink-features=AutomationControlled',
'--no-sandbox',
'--disable-setuid-sandbox',
`--proxy-server=${proxyUrl}`
]
});
const page = await browser.newPage();
// Ridefinizione di navigator.webdriver
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, 'webdriver', {
get: () => false,
});
});
Intestazioni, cookie e gestione delle sessioni
Un corretto lavoro con le intestazioni HTTP e i cookie è fondamentale per imitare un vero utente. Errori in questi parametri sono una causa comune di blocchi.
Intestazioni obbligatorie
Il set minimo di intestazioni per imitare il browser Chrome:
import requests
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': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
'DNT': '1',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-User': '?1',
'Cache-Control': 'max-age=0',
}
session = requests.Session()
session.headers.update(headers)
Gestione dei cookie
Molti siti impostano cookie di tracciamento al primo accesso e controllano la loro presenza nelle richieste successive. L'assenza di cookie o la loro incongruenza sono segni di un bot.
import requests
import pickle
class SessionManager:
def __init__(self, session_file='session.pkl'):
self.session_file = session_file
self.session = requests.Session()
self.load_session()
def load_session(self):
"""Caricamento della sessione salvata"""
try:
with open(self.session_file, 'rb') as f:
cookies = pickle.load(f)
self.session.cookies.update(cookies)
except FileNotFoundError:
pass
def save_session(self):
"""Salvataggio dei cookie per un uso successivo"""
with open(self.session_file, 'wb') as f:
pickle.dump(self.session.cookies, f)
def request(self, url, **kwargs):
response = self.session.get(url, **kwargs)
self.save_session()
return response
# Utilizzo
manager = SessionManager('instagram_session.pkl')
response = manager.request('https://www.instagram.com/explore/')
Importante: Durante la rotazione dei proxy, non dimenticate di resettare i cookie se sono legati a un IP specifico. L'incongruenza tra IP e cookie (ad esempio, cookie con geolocalizzazione negli Stati Uniti e IP dalla Germania) susciterà sospetti.
Referer e Origin
Le intestazioni Referer e Origin mostrano da dove proviene l'utente. La loro assenza o valori errati sono un campanello d'allarme.
# Sequenza corretta: home → categoria → prodotto
session = requests.Session()
# Passo 1: accesso alla home
response = session.get('https://example.com/')
# Passo 2: passaggio alla categoria
response = session.get(
'https://example.com/category/electronics',
headers={'Referer': 'https://example.com/'}
)
# Passo 3: visualizzazione del prodotto
response = session.get(
'https://example.com/product/12345',
headers={'Referer': 'https://example.com/category/electronics'}
)
Imitazione del comportamento umano
I parametri tecnici sono solo metà del lavoro. I moderni sistemi anti-bot analizzano i modelli comportamentali: come l'utente interagisce con la pagina, quanto tempo trascorre, come si muove il mouse.
Scorrimento e movimento del mouse
Quando si utilizza Selenium o Puppeteer, aggiungere movimenti casuali del mouse e scorrimento della pagina:
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import random
import time
def human_like_mouse_move(driver):
"""Movimento casuale del mouse sulla pagina"""
action = ActionChains(driver)
for _ in range(random.randint(3, 7)):
x = random.randint(0, 1000)
y = random.randint(0, 800)
action.move_by_offset(x, y)
action.pause(random.uniform(0.1, 0.3))
action.perform()
def human_like_scroll(driver):
"""Imitazione di uno scorrimento naturale"""
total_height = driver.execute_script("return document.body.scrollHeight")
current_position = 0
while current_position < total_height:
# Passo di scorrimento casuale
scroll_step = random.randint(100, 400)
current_position += scroll_step
driver.execute_script(f"window.scrollTo(0, {current_position});")
# Pausa con variazione
time.sleep(random.uniform(0.5, 1.5))
# A volte scorrere un po' indietro (come fanno le persone)
if random.random() < 0.2:
back_scroll = random.randint(50, 150)
current_position -= back_scroll
driver.execute_script(f"window.scrollTo(0, {current_position});")
time.sleep(random.uniform(0.3, 0.8))
# Utilizzo
driver = webdriver.Chrome()
driver.get('https://example.com')
human_like_mouse_move(driver)
time.sleep(random.uniform(2, 4))
human_like_scroll(driver)
Tempo sulla pagina
Gli utenti reali trascorrono tempo sulla pagina: leggono contenuti, esaminano immagini. Un bot che passa istantaneamente da un link all'altro è facilmente riconoscibile.
def realistic_page_view(driver, url, min_time=5, max_time=15):
"""
Visualizzazione realistica della pagina con attività
"""
driver.get(url)
# Ritardo iniziale (caricamento e "lettura")
time.sleep(random.uniform(2, 4))
# Scorrimento
human_like_scroll(driver)
# Attività aggiuntiva
total_time = random.uniform(min_time, max_time)
elapsed = 0
while elapsed < total_time:
action_choice = random.choice(['scroll', 'mouse_move', 'pause'])
if action_choice == 'scroll':
# Piccolo scorrimento su/giù
scroll_amount = random.randint(-200, 300)
driver.execute_script(f"window.scrollBy(0, {scroll_amount});")
pause = random.uniform(1, 3)
elif action_choice == 'mouse_move':
human_like_mouse_move(driver)
pause = random.uniform(0.5, 2)
else: # pausa
pause = random.uniform(2, 5)
time.sleep(pause)
elapsed += pause
Modelli di navigazione
Evitare modelli sospetti: passaggi diretti a pagine profonde, ignorare la home page, attraversare sequenzialmente tutti gli elementi senza saltare.
Buone pratiche:
- Iniziare dalla home page o dalle sezioni popolari
- Utilizzare la navigazione interna del sito, non URL diretti
- A volte tornare indietro o passare ad altre sezioni
- Variare la profondità di visualizzazione: non sempre arrivare fino in fondo
- Aggiungere "errori": passaggi a link non esistenti, ritorni
Bypass di Cloudflare, DataDome e altre protezioni
I sistemi anti-bot specializzati richiedono un approccio complesso. Utilizzano sfide JavaScript, CAPTCHA, analisi del comportamento in tempo reale.
Cloudflare
Cloudflare utilizza diversi livelli di protezione: Browser Integrity Check, JavaScript Challenge, CAPTCHA. Per bypassare la protezione di base, è sufficiente un'impronta TLS corretta e l'esecuzione di JavaScript:
# Opzione 1: cloudscraper (soluzione automatica per le sfide JS)
import cloudscraper
scraper = cloudscraper.create_scraper(
browser={
'browser': 'chrome',
'platform': 'windows',
'desktop': True
}
)
response = scraper.get('https://protected-site.com')
# Opzione 2: undetected-chromedriver (per casi complessi)
import undetected_chromedriver as uc
options = uc.ChromeOptions()
options.add_argument('--proxy-server=http://proxy:port')
driver = uc.Chrome(options=options)
driver.get('https://protected-site.com')
# Attesa per il superamento della sfida
time.sleep(5)
# Ottenere i cookie per requests
cookies = driver.get_cookies()
session = requests.Session()
for cookie in cookies:
session.cookies.set(cookie['name'], cookie['value'])
DataDome
DataDome analizza il comportamento dell'utente in tempo reale: movimenti del mouse, scrittura sulla tastiera, tempistiche. Per bypassare è necessario un browser completo con imitazione dell'attività:
from playwright.sync_api import sync_playwright
import random
def bypass_datadome(url, proxy=None):
with sync_playwright() as p:
browser = p.chromium.launch(
headless=False, # DataDome rileva headless
proxy={'server': proxy} if proxy else None
)
context = browser.new_context(
viewport={'width': 1920, 'height': 1080},
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64)...'
)
page = context.new_page()
# Iniezione di script per mascherare l'automazione
page.add_init_script("""
Object.defineProperty(navigator, 'webdriver', {get: () => false});
window.chrome = {runtime: {}};
""")
page.goto(url)
# Imitazione del comportamento umano
time.sleep(random.uniform(2, 4))
# Movimenti casuali del mouse
for _ in range(random.randint(5, 10)):
page.mouse.move(
random.randint(100, 1800),
random.randint(100, 1000)
)
time.sleep(random.uniform(0.1, 0.3))
# Scorrimento
page.evaluate(f"window.scrollTo(0, {random.randint(300, 800)})")
time.sleep(random.uniform(1, 2))
content = page.content()
browser.close()
return content
CAPTCHA
Per risolvere automaticamente i CAPTCHA, utilizzare servizi di riconoscimento (2captcha, Anti-Captcha) o strategie di evitamento:
- Ridurre la frequenza delle richieste a un livello che non attivi il CAPTCHA
- Utilizzare IP residenziali puliti con buona reputazione
- Lavorare tramite account autorizzati (hanno una soglia CAPTCHA più alta)
- Distribuire il carico nel tempo (evitare le ore di punta)
Monitoraggio e gestione dei blocchi
Anche con le migliori pratiche, i blocchi sono inevitabili. È importante rilevarli rapidamente e gestirli correttamente.
Indicatori di blocco
| Segnale | Descrizione | Azione |
|---|---|---|
| HTTP 429 | Troppe richieste | Aumentare i ritardi, cambiare IP |
| HTTP 403 | Vietato (ban IP) | Cambiare proxy, controllare fingerprint |
| CAPTCHA | Richiesta di verifica | Risolvi o riduci l'attività |
| Risposta vuota | Il contenuto non viene caricato | Controllare JavaScript, cookie |
| Redirect su /blocked | Blocco esplicito | Cambio completo della strategia |
Sistema di ripetizione
import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
def create_session_with_retries():
"""
Sessione con ripetizioni automatiche e gestione degli errori
"""
session = requests.Session()
retry_strategy = Retry(
total=5,
backoff_factor=2, # 2, 4, 8, 16, 32 secondi
status_forcelist=[429, 500, 502, 503, 504],
method_whitelist=["GET", "POST"]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("http://", adapter)
session.mount("https://", adapter)
return session
def safe_request(url, session, max_attempts=3):
"""
Richiesta con gestione dei blocchi
"""
for attempt in range(max_attempts):
try:
response = session.get(url, timeout=15)
# Controllo per il blocco
if response.status_code == 403:
print(f"IP bloccato. Rotazione del proxy...")
# Logica per cambiare proxy
continue
elif response.status_code == 429:
wait_time = int(response.headers.get('Retry-After', 60))
print(f"Limitato dalla velocità. Attesa {wait_time}s...")
time.sleep(wait_time)
continue
elif 'captcha' in response.text.lower():
print("CAPTCHA rilevato")
# Logica per risolvere il CAPTCHA o saltare
return None
return response
except requests.exceptions.Timeout:
print(f"Timeout al tentativo {attempt + 1}")
time.sleep(5 * (attempt + 1))
except requests.exceptions.ProxyError:
print("Errore del proxy. Rotazione...")
# Cambio proxy
continue
return None
Logging e analisi
Monitorare le metriche per ottimizzare la strategia:
import logging
from collections import defaultdict
from datetime import datetime
class ScraperMetrics:
def __init__(self):
self.stats = {
'total_requests': 0,
'successful': 0,
'rate_limited': 0,
'blocked': 0,
'captcha': 0,
'errors': 0,
'proxy_failures': defaultdict(int)
}
def log_request(self, status, proxy=None):
self.stats['total_requests'] += 1
if status == 200:
self.stats['successful'] += 1
elif status == 429:
self.stats['rate_limited'] += 1
elif status == 403:
self.stats['blocked'] += 1
if proxy:
self.stats['proxy_failures'][proxy] += 1
def get_success_rate(self):
if self.stats['total_requests'] == 0:
return 0
return (self.stats['successful'] / self.stats['total_requests']) * 100
def print_report(self):
print(f"\n=== Rapporto di scraping ===")
print(f"Richieste totali: {self.stats['total_requests']}")
print(f"Percentuale di successo: {self.get_success_rate():.2f}%")
print(f"Limitato dalla velocità: {self.stats['rate_limited']}")
print(f"Bloccato: {self.stats['blocked']}")
print(f"CAPTCHA: {self.stats['captcha']}")
if self.stats['proxy_failures']:
print(f"\nProxy problematici:")
for proxy, count in sorted(
self.stats['proxy_failures'].items(),
key=lambda x: x[1],
reverse=True
)[:5]:
print(f" {proxy}: {count} fallimenti")
# Utilizzo
metrics = ScraperMetrics()
for url in urls:
response = safe_request(url, session)
if response:
metrics.log_request(response.status_code, current_proxy)
metrics.print_report()
Indicatori ottimali: Una percentuale di successo superiore al 95% è un ottimo risultato. 80-95% è accettabile, ma c'è spazio per migliorare. Sotto l'80% — rivedere la strategia: potrebbe essere una limitazione della velocità troppo aggressiva, proxy scadenti o problemi di fingerprinting.
Conclusione
La protezione contro i blocchi durante le richieste di massa richiede un approccio strategico e l'implementazione di diverse tecniche. Con la giusta preparazione e attenzione ai dettagli, è possibile navigare con successo attraverso le sfide poste dai moderni sistemi anti-bot.