Torna al blog

Come evitare il blocco durante richieste massicce

Analizziamo i meccanismi di rilevamento dell'automazione e le tecniche specifiche di protezione contro i blocchi durante le richieste massicce: dalla rotazione di proxy di base all'imitazione del comportamento umano.

📅21 dicembre 2025
```html

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.

```