Quelles sont les limites de taux de scraping?
Les limites de taux sont les murs invisibles que les sites Web construisent pour contrôler la rapidité avec laquelle un seul client peut faire des requêtes. Lorsque vous grattez un site trop agressivement, vous frappez ces murs — et les conséquences vont des ralentissements temporaires aux interdictions permanentes de PI. Comprendre comment les limites de taux fonctionnent, comment ils vous détectent et comment rester sous eux est fondamental pour construire des racleurs qui fournissent des données fiables.
Ce guide explique les mécanismes derrière la limitation de vitesse, l'utilisation des sites Web de signaux de détection, et des stratégies pratiques pour le throttling adaptatif qui permettent à vos racleurs de fonctionner en douceur.
Pour un aperçu plus large du grattage avec des proxies, voir notre Guide complet des produits de scraping Web. Pour éviter les blocs en général, lire : Comment gratter les sites Web sans se faire bloquer.
Comment fonctionne la limitation des taux
Les sites web appliquent des limites de vitesse à plusieurs couches, chacune avec une granularité de détection différente:
Couche 1: Limites de taux fondées sur la PI
L'approche la plus courante. Le serveur suit les requêtes par adresse IP dans une fenêtre de temps. Excédé le seuil et vous recevez HTTP 429 (Trop de demandes) ou 503 réponses.
# Typical rate limit behavior
Request 1-50: HTTP 200 (normal)
Request 51: HTTP 429 (rate limited)
Wait 60 seconds...
Request 52: HTTP 200 (reset)Couche 2 : Limites fondées sur la session/les témoins
Les pistes demandent la fréquence par session ou cookie de navigateur. Même si vous faites pivoter des IP, la même session en appuyant rapidement sur le serveur déclenchera des limites.
Calque 3 : Limites fondées sur les comptes
Pour les sites nécessitant une connexion, les limites sont liées au compte utilisateur indépendamment de l'IP. Commun sur les API et les plateformes SaaS.
Couche 4: Analyse comportementale
Les systèmes avancés comme Cloudflare, PerimeterX et Akamai analysent les modèles comportementaux : chronométrage des requêtes, flux de navigation, mouvements de souris (dans les contextes du navigateur). Cette couche est la plus difficile à contourner car elle ne repose pas sur des compteurs simples.
Signalisation de la limite de vitesse commune
Les sites Web utilisent simultanément plusieurs signaux pour détecter le grattage automatisé:
| Signal | Ce qu'il détecte | Difficulté à éluder |
|---|---|---|
| Demandes par IP par minute | Vitesse brute | Facile (utiliser des proxies) |
| Demandes par IP par heure/jour | Volume soutenu | Moyenne (IP rotatives) |
| Fréquence des demandes | Intervalles de type machine | Moyenne |
| En-têtes manquants/mauvaises | Clients non-navigateurs | Facile ( réglez les en-têtes appropriés) |
| Modèles d'URL séquentiels | Rampage systématique | Moyenne (ordre randomisé) |
| Empreinte TLS | Bibliothèque vs navigateur | Dur (utiliser des navigateurs réels) |
| Exécution JavaScript | Navigateur sans tête | Dur (configuration avancée) |
| Manifestations sur la souris et le clavier | Comportement du bot | Très dur |
En savoir plus sur les mécanismes de détection dans notre guide Comment les systèmes anti-bot détectent les proxies.
Codes de réponse HTTP qui limitent le taux de signal
Savoir quels codes HTTP indiquent des limites de vitesse vous aide à construire une logique de réessayer appropriée :
| Code | Signification | Décision |
|---|---|---|
| 200 (avec CAPTCHA) | Bloc souple — page de défi servie | Rotation IP, ralentissement |
| 403 Interdit | IP ou session bloquée | Rotation immédiate de l'IP |
| 429 Trop de demandes | Taux limite explicite atteint | Attendez et réessayez avec le recul |
| 503 Service non disponible | surcharge ou bloc du serveur | En arrière, vérifier si elle est bloquée |
| 302/307 vers l'URL CAPTCHA | Redirection des défis | Rotation IP, réduction de la vitesse |
Stratégie 1: Throttling respectueux
L'approche la plus simple — maintenez votre taux de demande bien en dessous de ce que la cible permet. Cela signifie moins d'échecs, moins de bande passante gaspillée et un grattage plus durable.
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 resultsStratégie 2: Throttling adaptatif
Au lieu d'un taux fixe, ajustez dynamiquement votre vitesse en fonction des réponses que vous recevez. Accélérez quand tout fonctionne, ralentissez quand vous voyez des signes d'avertissement.
Mise en œuvre de 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 resultsMise en œuvre de Node.js
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;
}Stratégie 3 : Limite des taux distribués
Lorsque vous utilisez plusieurs instances de racleur en parallèle, coordonnez la limite de taux pour tous les travailleurs. Sans coordination, chaque travailleur respecte sa propre limite, mais le trafic combiné dépasse encore la cible.
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)})Stratégie 4 : Requête avec priorité
Pour les projets de démolition complexes, utilisez une file d'attente prioritaire qui gère les limites de taux par domaine cible :
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 GoogleLecture Robots.txt pour Conseils de Taux
De nombreux sites publient leurs préférences de rampe dans robots.txt. Les Crawl-delay directive vous indique le minimum de secondes entre les requêtes:
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")Erreurs de limite de taux commune
- En ignorant 429 réponses. De nombreux racleurs traitent toutes les réponses non-200 de la même façon. Un 429 vous indique exactement ce qui s'est passé — utilisez l'en-tête Retry-After et reculez.
- Délais fixes sans blague. Une demande exactement toutes les 2.000 secondes semble robotique. Ajoutez une variation aléatoire (jitter) à vos retards.
- Ne pas coordonner les travailleurs parallèles. Cinq travailleurs chacun faisant 10 RPM égale 50 RPM au total. Utilisez un limiteur de taux partagé.
- Rotation des IP sans ralentissement. La rotation IP vous fait gagner du temps, mais si chaque nouvelle IP martele immédiatement le site, la détection avancée vous rattrapera encore. Combiner la rotation avec un étranglement approprié.
- Scraping pendant les heures de pointe. Les sites sont plus agressifs avec une limitation de vitesse pendant les périodes de forte circulation. Planifiez de fortes rampes pendant les heures creuses pour le fuseau horaire de la cible.
Pour calculer le nombre de proxies dont vous avez besoin pour soutenir votre grattage à taux limité, voir Combien de proxies avez-vous besoin pour le scraping?. Pour les stratégies de rotation par procuration qui complètent les limites de taux, lire Stratégies de rotation par procuration pour le scrapage à grande échelle.
Commencez avec le grattage correctement limité à la vitesse ProxyHat Python SDK ou explorer plans de prix pour votre projet.
Foire aux questions
Que se passe-t-il lorsque je dépasse une limite de taux?
La réponse dépend du site. La plupart renvoient HTTP 429 avec un en-tête Retry-After. Certains servent les CAPTCHA. Les sites agressifs bloquent immédiatement l'IP avec une réponse 403. Dans le pire des cas, les violations répétées conduisent à des interdictions permanentes de la propriété intellectuelle.
Comment puis-je trouver la limite de tarifs d'un site?
Commencez lentement et augmentez graduellement tout en surveillant les codes de réponse. Vérifiez robots.txt pour les directives Crawl-delay. Observez les en-têtes de réponse pour les champs X-RateLimit-Limit et X-RateLimit-Remaining. Certaines API publient leurs limites dans la documentation.
Est-ce que l'utilisation de limites de taux de contournement de proxies?
Proxies distribue des requêtes sur plusieurs IP, de sorte que chaque IP reste sous la limite par IP. Cependant, des sites sophistiqués suivent également les séances, les empreintes digitales et les modèles comportementaux. Les proxies sont nécessaires, mais pas suffisantes — combinez-les avec des motifs de demande appropriés et réalistes.
Quel est le taux de demande le plus sûr pour le grattage?
Il n'y a pas de réponse universelle. Pour les cibles agressives comme Google ou Amazon, 1-5 requêtes par minute par IP est sûr. Pour les sites faiblement protégés, 20-60 RPM par IP peuvent fonctionner. Commencez toujours par la prudence et augmentez en fonction des taux de réussite observés.






