Limiti di velocità di scorrimento spiegati

Come i limiti di tasso funzionano, come i siti rilevano i rottami e le strategie pratiche per rimanere sotto i limiti. Include codice di ottimizzazione adattativo e schemi di limitazione della velocità distribuiti.

Limiti di velocità di scorrimento spiegati

Quali sono i limiti di velocità di scorrimento?

I limiti di tasso sono le pareti invisibili che i siti web costruiscono per controllare quanto velocemente qualsiasi singolo cliente può fare richieste. Quando si raschia un sito troppo aggressivo, si colpisce queste pareti - e le conseguenze variano da rallentamenti temporanei ai divieti IP permanenti. Capire come i limiti di velocità funzionano, come si rileva e come rimanere sotto di loro è fondamentale per la costruzione di raschietti che forniscono dati in modo affidabile.

Questa guida spiega la meccanica dietro il limite di velocità, i segnali di rilevamento siti web uso, e strategie pratiche per l'adaptive throttling che mantengono i raschiatori in esecuzione senza intoppi.

Per una visione più ampia di raschiamento con i proxy, vedere il nostro Guida completa ai proxy Web Scraping. Per evitare blocchi in generale, leggere Come Graffiare i Siti web senza Bloccarsi.

Come limitare il tasso di lavoro

I siti web implementano limiti di velocità a più strati, ciascuno con diversa granulosità di rilevamento:

Livello 1: Limiti di velocità basati su IP

L'approccio più comune. Il server traccia richieste per indirizzo IP all'interno di una finestra temporale. Superato la soglia e ricevi risposte HTTP 429 (Too Many Requests) o 503.

# Typical rate limit behavior
Request 1-50:    HTTP 200 (normal)
Request 51:      HTTP 429 (rate limited)
Wait 60 seconds...
Request 52:      HTTP 200 (reset)

Livello 2: Limiti basati su sessione/cookie

Tracce richiesta frequenza per sessione o cookie del browser. Anche se si ruotano gli IP, lo stesso token di sessione che colpisce il server veloce si attivano limiti.

Livello 3: Limiti basati su account

Per i siti che richiedono il login, i limiti sono legati all'account utente indipendentemente dall'IP. Comune sulle piattaforme API e SaaS.

Livello 4: Analisi comportamentale

Sistemi avanzati come Cloudflare, PerimeterX e Akamai analizzano i modelli comportamentali: richiedere tempistiche, flusso di navigazione, movimenti del mouse (nei contesti del browser). Questo strato è il più difficile da bypassare perché non si basa su semplici contatori.

Segnali di rilevamento del limite del tasso comune

I siti web utilizzano più segnali contemporaneamente per rilevare la demolizione automatizzata:

SegnaleCosa rilevaDifficoltà di Evade
Richieste per IP al minutoVelocitàFacile (utilizzare i proxy)
Richieste per IP all'ora/giornoVolume ottenutoMedia (rotato IPs)
Richiedi la regolarità dei tempiIntervalli simili a macchineMedio (add jitter)
Intestazioni mancanti/rongClienti non-browserFacile (set intestazioni appropriate)
Modelli di URL sequenzialiStrisciamento sistemicoMedio (ordine randomito)
Impronte TLSLibreria vs browserHard (usare browser reali)
esecuzione JavaScriptbrowser intestatoHard (configura avanzata)
Eventi di Mouse/TastieraComportamento BotMolto difficile

Ulteriori informazioni sui meccanismi di rilevamento nella nostra guida Come i sistemi anti-bot rilevano i proxy.

Codici di risposta HTTP che limite di velocità di segnale

Sapere quali codici HTTP indicano il limite di velocità ti aiuta a costruire una corretta logica di riprovazione:

CodiceSignificatoAzione
200 (con CAPTCHA)Blocco morbido — pagina di sfida servitaRuota IP, rallenta
403 ProibitaIP o sessione bloccataRuotare immediatamente l'IP
429 Troppe richiesteLimite di velocità esattoAttendere e riprovare con backoff
503 Servizio Non disponibileSovraccarico del server o bloccoIndietro, controllare se bloccato
302/307 a CAPTCHA URLChallenge redirectRuota IP, riduce la velocità

Strategia 1: Respective Throttling

L'approccio più semplice — mantenere il tasso di richiesta ben al di sotto di ciò che l'obiettivo consente. Ciò significa meno fallimenti, meno larghezza di banda sprecata e raschiamento più sostenibile.

import requests
import time
import random
PROXY = "http://USERNAME:PASSWORD@gate.proxyhat.com:8080"
def respectful_scrape(urls: list[str], rpm_limit: int = 10) -> list[str]:
    """Scrape URLs while respecting a requests-per-minute limit."""
    delay = 60.0 / rpm_limit
    results = []
    for url in urls:
        try:
            resp = requests.get(
                url,
                proxies={"http": PROXY, "https": PROXY},
                timeout=30
            )
            results.append(resp.text if resp.status_code == 200 else None)
        except requests.RequestException:
            results.append(None)
        # Add delay with random jitter (±30%) to look less robotic
        jitter = delay * random.uniform(0.7, 1.3)
        time.sleep(jitter)
    return results

Strategia 2: Adaptive Throttling

Invece di un tasso fisso, regolare dinamicamente la velocità in base alle risposte che ricevi. Accelerare quando tutto funziona, rallentare quando si vedono segnali di avvertimento.

Attuazione di Python

import requests
import time
import random
from dataclasses import dataclass, field
PROXY = "http://USERNAME:PASSWORD@gate.proxyhat.com:8080"
@dataclass
class AdaptiveThrottle:
    """Automatically adjusts request rate based on server responses."""
    base_delay: float = 2.0      # seconds between requests
    min_delay: float = 0.5
    max_delay: float = 30.0
    current_delay: float = 2.0
    success_streak: int = 0
    warning_codes: set = field(default_factory=lambda: {429, 403, 503})
    def on_success(self):
        self.success_streak += 1
        # Speed up after 10 consecutive successes
        if self.success_streak >= 10:
            self.current_delay = max(self.current_delay * 0.85, self.min_delay)
            self.success_streak = 0
    def on_rate_limit(self):
        self.success_streak = 0
        # Double the delay on rate limit
        self.current_delay = min(self.current_delay * 2.0, self.max_delay)
    def on_block(self):
        self.success_streak = 0
        # Aggressive backoff on block
        self.current_delay = min(self.current_delay * 3.0, self.max_delay)
    def wait(self):
        jitter = self.current_delay * random.uniform(0.7, 1.3)
        time.sleep(jitter)
def scrape_adaptive(urls: list[str]) -> list[dict]:
    throttle = AdaptiveThrottle()
    results = []
    for url in urls:
        try:
            resp = requests.get(
                url,
                proxies={"http": PROXY, "https": PROXY},
                timeout=30
            )
            if resp.status_code == 200:
                throttle.on_success()
                results.append({"url": url, "status": 200, "body": resp.text})
            elif resp.status_code == 429:
                throttle.on_rate_limit()
                # Check Retry-After header
                retry_after = int(resp.headers.get("Retry-After", 0))
                if retry_after:
                    time.sleep(retry_after)
                results.append({"url": url, "status": 429, "body": None})
            elif resp.status_code == 403:
                throttle.on_block()
                results.append({"url": url, "status": 403, "body": None})
            else:
                results.append({"url": url, "status": resp.status_code, "body": resp.text})
        except requests.RequestException as e:
            throttle.on_block()
            results.append({"url": url, "status": 0, "error": str(e)})
        throttle.wait()
        print(f"Current delay: {throttle.current_delay:.1f}s")
    return results

Node.js Attuazione

const HttpsProxyAgent = require('https-proxy-agent');
const fetch = require('node-fetch');
class AdaptiveThrottle {
  constructor() {
    this.currentDelay = 2000; // ms
    this.minDelay = 500;
    this.maxDelay = 30000;
    this.successStreak = 0;
  }
  onSuccess() {
    this.successStreak++;
    if (this.successStreak >= 10) {
      this.currentDelay = Math.max(this.currentDelay * 0.85, this.minDelay);
      this.successStreak = 0;
    }
  }
  onRateLimit() {
    this.successStreak = 0;
    this.currentDelay = Math.min(this.currentDelay * 2, this.maxDelay);
  }
  onBlock() {
    this.successStreak = 0;
    this.currentDelay = Math.min(this.currentDelay * 3, this.maxDelay);
  }
  async wait() {
    const jitter = this.currentDelay * (0.7 + Math.random() * 0.6);
    return new Promise(resolve => setTimeout(resolve, jitter));
  }
}
async function scrapeAdaptive(urls) {
  const throttle = new AdaptiveThrottle();
  const agent = new HttpsProxyAgent('http://USERNAME:PASSWORD@gate.proxyhat.com:8080');
  const results = [];
  for (const url of urls) {
    try {
      const res = await fetch(url, { agent, timeout: 30000 });
      if (res.ok) {
        throttle.onSuccess();
        results.push({ url, status: res.status, body: await res.text() });
      } else if (res.status === 429) {
        throttle.onRateLimit();
        const retryAfter = parseInt(res.headers.get('retry-after') || '0');
        if (retryAfter) await new Promise(r => setTimeout(r, retryAfter * 1000));
        results.push({ url, status: 429, body: null });
      } else if (res.status === 403) {
        throttle.onBlock();
        results.push({ url, status: 403, body: null });
      }
    } catch (err) {
      throttle.onBlock();
      results.push({ url, status: 0, error: err.message });
    }
    await throttle.wait();
    console.log(`Current delay: ${throttle.currentDelay.toFixed(0)}ms`);
  }
  return results;
}

Strategia 3: Limitamento del tasso distribuito

Durante l'esecuzione di più istanze raschietto in parallelo, coordinare il limite di tasso in tutti i lavoratori. Senza coordinamento, ogni lavoratore rispetta il proprio limite, ma il traffico combinato supera ancora l'obiettivo.

import requests
import time
import threading
class DistributedRateLimiter:
    """Thread-safe rate limiter for multiple scraper workers."""
    def __init__(self, max_rpm: int):
        self.min_interval = 60.0 / max_rpm
        self.lock = threading.Lock()
        self.last_request_time = 0.0
    def acquire(self):
        """Block until it is safe to make the next request."""
        with self.lock:
            now = time.time()
            elapsed = now - self.last_request_time
            if elapsed < self.min_interval:
                time.sleep(self.min_interval - elapsed)
            self.last_request_time = time.time()
# Shared limiter across all threads
limiter = DistributedRateLimiter(max_rpm=30)
PROXY = "http://USERNAME:PASSWORD@gate.proxyhat.com:8080"
def worker(urls: list[str], results: list):
    for url in urls:
        limiter.acquire()
        try:
            resp = requests.get(
                url,
                proxies={"http": PROXY, "https": PROXY},
                timeout=30
            )
            results.append({"url": url, "status": resp.status_code})
        except Exception as e:
            results.append({"url": url, "error": str(e)})

Strategia 4: Richiedi la coda con la priorità

Per progetti di demolizione complessi, utilizzare una coda prioritaria che gestisce limiti di tasso per dominio target:

import requests
import time
import heapq
import threading
from collections import defaultdict
PROXY = "http://USERNAME:PASSWORD@gate.proxyhat.com:8080"
class DomainRateLimiter:
    """Per-domain rate limiting with priority queue."""
    def __init__(self, default_rpm: int = 10):
        self.default_rpm = default_rpm
        self.domain_limits = {}          # domain -> max RPM
        self.domain_last = defaultdict(float)  # domain -> last request time
        self.lock = threading.Lock()
    def set_limit(self, domain: str, rpm: int):
        self.domain_limits[domain] = rpm
    def wait_for_domain(self, domain: str):
        rpm = self.domain_limits.get(domain, self.default_rpm)
        min_interval = 60.0 / rpm
        with self.lock:
            now = time.time()
            elapsed = now - self.domain_last[domain]
            if elapsed < min_interval:
                time.sleep(min_interval - elapsed)
            self.domain_last[domain] = time.time()
# Configure per-domain limits
limiter = DomainRateLimiter(default_rpm=10)
limiter.set_limit("amazon.com", 3)        # Very conservative for Amazon
limiter.set_limit("example.com", 30)      # Lenient for simple sites
limiter.set_limit("google.com", 5)        # Moderate for Google

Lettura Robots.txt per il tasso

Molti siti pubblicano le loro preferenze striscianti in robots.txt. The Crawl-delay direttiva ti dice i secondi minimi tra le richieste:

import requests
from urllib.parse import urlparse
from urllib.robotparser import RobotFileParser
def get_crawl_delay(base_url: str, user_agent: str = "*") -> float | None:
    """Extract Crawl-delay from robots.txt."""
    parsed = urlparse(base_url)
    robots_url = f"{parsed.scheme}://{parsed.netloc}/robots.txt"
    try:
        resp = requests.get(robots_url, timeout=10)
        if resp.status_code != 200:
            return None
        rp = RobotFileParser()
        rp.parse(resp.text.splitlines())
        delay = rp.crawl_delay(user_agent)
        return delay
    except Exception:
        return None
# Check before scraping
delay = get_crawl_delay("https://example.com")
if delay:
    print(f"Site requests {delay}s between requests")
else:
    print("No crawl-delay specified")

Errori di limite del tasso comune

  • Ignorando 429 risposte. Molti raschiatori trattano tutte le risposte non-200 lo stesso. Un 429 vi dice esattamente quello che è successo — utilizzare l'intestazione Retry-After e indietro.
  • Risolto ritardi senza jitter. Una richiesta esattamente ogni 2.000 secondi sembra robotica. Aggiungi variazione casuale (jitter) ai tuoi ritardi.
  • Non coordinare i lavoratori paralleli. Cinque lavoratori ciascuno facendo 10 RPM uguale 50 RPM totale. Utilizzare un limitatore di tasso condiviso.
  • IP rotanti senza rallentare. La rotazione IP ti acquista il tempo, ma se ogni nuovo IP immediatamente martella il sito, il rilevamento avanzato ti cattura ancora. Combinare la rotazione con un corretto throttling.
  • Scraping durante le ore di punta. I siti sono più aggressivi con la limitazione dei tassi durante periodi ad alto traffico. Pianificate i pesanti strisciamenti durante le ore fuori quota per il fuso orario del bersaglio.

Per calcolare quanti proxy è necessario sostenere la raschiatura limitata al tasso, vedere Quanti Proxies hai bisogno di Scraping?. Per le strategie di rotazione proxy che completano il limite di velocità, leggere Strategie di rotazione proxy per Scraping a grande scala.

Iniziare con il raschiamento del tasso limitato correttamente utilizzando ProxyHat Python SDK o esplorare piani di prezzi per il tuo progetto.

Domande frequenti

Cosa succede quando supero un limite di tariffa?

La risposta dipende dal sito. La maggior parte del ritorno HTTP 429 con un intestazione Retry-After. Alcuni servono i CAPTCHA. I siti aggressivi bloccano immediatamente l'IP con una risposta di 403. Nel peggiore dei casi, ripetute violazioni portano a divieti IP permanenti.

Come trovo il limite di tariffa di un sito?

Avviare lentamente e aumentare gradualmente durante il monitoraggio dei codici di risposta. Controllare robots.txt per le direttive Crawl-delay. Osserva le intestazioni di risposta per i campi X-RateLimit-Limit e X-RateLimit-Remaining. Alcune API pubblicano i loro limiti nella documentazione.

Usa i limiti di velocità di bypass dei proxy?

I proxy distribuiscono richieste su più IP, quindi ogni IP rimane sotto il limite per-IP. Tuttavia, i siti sofisticati tracciano anche sessioni, impronte digitali e modelli comportamentali. I proxy sono necessari ma non sufficienti — li combinano con i modelli di richiesta adeguati e realistici.

Qual è il tasso di richiesta più sicuro per la raschiatura?

Non c'è risposta universale. Per obiettivi aggressivi come Google o Amazon, 1-5 richieste al minuto per IP è sicuro. Per i siti leggermente protetti, possono funzionare 20-60 RPM per IP. Iniziare sempre conservatore e aumentare in base ai tassi di successo osservati.

Pronto per iniziare?

Accedi a oltre 50M di IP residenziali in oltre 148 paesi con filtraggio AI.

Vedi i prezziProxy residenziali
← Torna al Blog