Proxies résidentiels

Limitation de débit et régulation des requêtes avec des proxies résidentiels

Un large pool de proxies ne vous exempte pas des limites de débit, il change simplement l'endroit où elles s'appliquent. Voici comment cadencer les requêtes par cible, par IP et sur l'ensemble d'une flotte.

Matt Brown

Matt Brown

26 août 2026 · 9 min de lecture

Il existe une logique tentante qui piège de nombreuses équipes : les limites de débit sont appliquées par IP, un pool résidentiel vous donne de nombreuses IP, donc les limites de débit ne s’appliquent plus. Les deux premières propositions sont vraies et la conclusion est fausse, et c’est dans cet écart qu’un nombre surprenant de scrapers finissent bloqués.

Un pool change l’endroit où la limite s’exerce, pas le fait qu’elle existe. Vous devez toujours doser votre rythme, mais selon des axes différents d’une configuration à IP unique, et l’axe que les gens oublient est celui qui les fait repérer. Voici comment le throttling fonctionne réellement lorsque vos requêtes sont réparties sur un pool rotatif.

Trois limites, pas une seule

Lorsque vous scrapez via un pool, au moins trois plafonds distincts s’appliquent simultanément, et votre débit est déterminé par celui que vous atteignez en premier.

Par IP, par cible. C’est le cas classique. Une seule adresse ne peut envoyer qu’un certain nombre de requêtes à un site donné avant que ce site ne la limite ou ne la bloque. La rotation est ce qui vous permet de rester sous ce seuil, c’est tout l’intérêt de répartir la charge sur un pool comme décrit dans load balancing.

Par cible, en agrégat. C’est celui qui surprend les gens. Un site ne compte pas seulement par adresse ; il observe aussi sa propre charge entrante totale et peut reconnaître un schéma coordonné à travers de nombreuses adresses, en particulier si les requêtes partagent le timing, les chemins, ou le comportement. Vos cinquante IP envoyant chacune deux requêtes par seconde représentent cent requêtes par seconde arrivant vers une seule origine, et aucune quantité de rotation d’adresses ne fait ressembler cela à du trafic organique ordinaire. Les systèmes anti-bots raisonnent de plus en plus à ce niveau.

Votre propre capacité. La concurrence que vous pouvez réellement soutenir : connexions ouvertes, threads de travail, mémoire, et le fait que la latence résidentielle est plus élevée qu’une connexion directe, de sorte qu’un nombre de threads fixe produit moins de requêtes par seconde que vous ne pourriez l’espérer.

La conséquence pratique est que votre throttle doit être exprimé par cible plutôt que globalement. Une seule limite de débit globale vous prive de ressources sur les cinquante sites qui pourraient supporter plus, ou martèle celui qui ne le peut pas.

Partez de la cible, pas de vos ambitions

Avant d’écrire le moindre code de throttling, découvrez ce que la cible tolère réellement, car deviner produit soit une tâche inutilement lente, soit une tâche bloquée.

Lisez les signaux que le site vous donne. Un 429 est une déclaration explicite que vous allez trop vite. Un en-tête Retry-After est le site qui vous dit exactement combien de temps attendre, et le respecter est à la fois correct et bien moins coûteux que de découvrir la réponse par essais successifs. Certaines API publient des limites dans leur documentation ou renvoient des en-têtes de quota restant. Et si le site possède un robots.txt avec une directive crawl-delay, c’est une préférence exprimée qui mérite d’être respectée.

Là où rien n’est publié, calibrez empiriquement : commencez prudemment, augmentez progressivement, et surveillez le taux de succès validé plutôt que les codes de statut, car un site sous tension se dégrade souvent avant de refuser, et une page de challenge avec un statut 200 ressemble à un succès pour un compteur naïf, comme traité dans detecting blocked or fake content. Le point où le taux de succès commence à glisser est votre véritable plafond, et vous voulez opérer en dessous plutôt qu’à ce niveau.

Implémenter le throttle

Un token bucket par cible est l’outil standard et est assez simple à écrire soi-même. Les tokens se rechargent à votre taux choisi, chaque requête en consomme un, et une requête attend lorsque le bucket est vide. Cela vous donne une moyenne stable avec une tolérance de rafale contrôlée, ce qui correspond mieux au comportement du trafic réel qu’un délai rigide d’une requête par N secondes.

import time, threading

class TargetLimiter:
    """Token bucket, one instance per target host."""
    def __init__(self, rate_per_sec, burst=5):
        self.rate, self.capacity = rate_per_sec, burst
        self.tokens, self.updated = burst, time.monotonic()
        self.lock = threading.Lock()

    def acquire(self):
        while True:
            with self.lock:
                now = time.monotonic()
                self.tokens = min(self.capacity,
                                  self.tokens + (now - self.updated) * self.rate)
                self.updated = now
                if self.tokens >= 1:
                    self.tokens -= 1
                    return
                wait = (1 - self.tokens) / self.rate
            time.sleep(wait)               # sleep outside the lock

LIMITS = {                                  # per target, tuned per target
    "shop.example.com":  TargetLimiter(2.0),
    "search.example.com": TargetLimiter(0.5),
}

def fetch(url, host, proxies):
    LIMITS[host].acquire()
    return requests.get(url, proxies=proxies, timeout=20)

Deux raffinements comptent en production. Ajoutez du jitter pour que les requêtes ne tombent pas sur des intervalles exacts, car un espacement parfaitement régulier est en soi une signature machine et cela synchronise aussi vos workers en vagues. Et faites en sorte que le limiteur soit partagé entre les workers plutôt que par processus, sinon dix processus faisant chacun poliment deux requêtes par seconde en font collectivement vingt. Dans une configuration distribuée, cela signifie un compteur partagé dans Redis ou équivalent, ce qui est le même problème que doit résoudre scaling scraping across Kubernetes.

La concurrence est l’autre moitié

Le débit et la concurrence sont deux réglages différents et tous deux ont besoin de limites. Le débit contrôle la fréquence à laquelle vous démarrez des requêtes ; la concurrence contrôle combien sont en vol simultanément. Une tâche avec une limite de débit modeste mais une concurrence illimitée ouvrira quand même des centaines de connexions simultanées vers une seule origine dès que cette origine ralentit, car les réponses lentes font s’accumuler les requêtes.

Plafonnez la concurrence par cible avec un sémaphore, et dimensionnez-la en fonction de ce que la cible tolère plutôt que de ce que votre machine peut ouvrir. Côté proxy, les connexions concurrentes ne sont généralement pas votre contrainte, ce qui fait facilement oublier que la tolérance de la cible, elle, l’est. La question de distribution, à savoir combien d’adresses cela implique, est traitée dans how many proxy IPs you actually need.

Le throttling adaptatif bat un chiffre fixe

Un débit statique est une supposition qui devient fausse. Les sites changent leur tolérance, ralentissent sous charge, ou resserrent les limites pendant les heures de pointe. Le schéma le plus robuste est d’ajuster en fonction de ce que vous observez, dans le style de l’augmentation additive et la diminution multiplicative : faites grimper le débit lentement tant que tout va bien, et réduisez-le brutalement au premier signe de tension.

Déclenchez la diminution sur un signal composite plutôt que sur un seul code de statut : un 429, une part croissante de pages de challenge, une latence en hausse, ou une baisse du taux de succès validé. Puis récupérez progressivement plutôt que de revenir directement à l’ancien débit, car un retour immédiat à pleine vitesse après un blocage est en soi un schéma reconnaissable.

class AdaptiveRate:
    def __init__(self, start=2.0, floor=0.2, ceiling=10.0):
        self.rate, self.floor, self.ceiling = start, floor, ceiling

    def ok(self):                       # healthy response
        self.rate = min(self.ceiling, self.rate * 1.02)   # creep up

    def strained(self):                 # 429, challenge, timeout, latency spike
        self.rate = max(self.floor, self.rate * 0.5)      # back off hard

Où le throttling rencontre la rotation et les retries

Trois interactions méritent d’être explicitées, car les manquer annule le throttle.

Ne répondez pas à un signal de débit en tournant. Un 429 signifie ralentir. Passer à une adresse fraîche pour maintenir le même rythme est le comportement qui transforme une adresse throttlée en pool grillé, et c’est précisément ce qui fait ressembler le trafic à de l’évasion plutôt qu’à de l’enthousiasme. Attendez lorsque le signal concerne le débit ; tournez lorsque le signal concerne l’adresse.

Les retries doivent respecter le limiteur. Un retry est une autre requête et doit acquérir un token comme n’importe quelle autre, sinon votre chemin d’erreur contourne discrètement le dosage que vous avez mis en place. Les tempêtes de retries sont la manière la plus courante dont une tâche throttlée devient une tâche bloquée, ce qui est le sujet de retry and backoff.

Les sessions collantes concentrent la charge. Conserver une adresse pour un flux à plusieurs étapes signifie que cette adresse porte toute la séquence, donc le dosage par IP compte davantage à l’intérieur d’une session collante que dans un travail rotatif où la charge se répartit naturellement.

La politesse est de l’intérêt bien compris

Il est facile de lire tout cela comme une contrainte de conformité, mais les incitations vont dans le même sens. Le dosage préserve la réputation des adresses dont vous dépendez, maintient votre taux de succès élevé afin que vous ne payiez pas de bande passante pour des pages de challenge, et évite le cycle d’escalade où une collecte agressive provoque des défenses plus strictes qui rendent la cible plus difficile pour tout le monde, vous-même y compris le trimestre suivant. Respecter les limites déclarées d’un site, ses directives robots, et ses conditions d’utilisation est à la fois la chose juste et la chose la moins coûteuse.

En résumé

Un pool ne supprime pas les limites de débit, il les déplace. Faites du throttling par cible plutôt que globalement, car un seul chiffre est faux pour chaque site que vous touchez. Trouvez la véritable tolérance de chaque cible à partir de ses propres signaux, respectez Retry-After et 429 comme des instructions plutôt que des obstacles, et calibrez par rapport au taux de succès validé plutôt qu’aux codes de statut. Implémentez avec un token bucket partagé plus du jitter, plafonnez la concurrence séparément du débit, et rendez la limite adaptative afin qu’elle se dégrade rapidement et récupère lentement. Puis gardez les interactions claires : ne répondez jamais à un signal de dosage en tournant les adresses, faites toujours passer les retries par le limiteur, et dosez plus fort à l’intérieur des sessions collantes. Bien fait, le throttling est ce qui permet à un pool de fonctionner à sa capacité réelle au lieu de l’épuiser.

Le pool lui-même est ce qui vous donne la marge de manœuvre : les proxies résidentiels répartissent une tâche correctement dosée sur de nombreuses adresses réelles de qualité domestique avec un ciblage par pays et par ville, et la tarification au Go signifie qu’une tâche qui dose bien son rythme et ne récupère que ce dont elle a besoin coûte moins cher qu’une tâche qui martèle et retente.

Prêt à commencer ?

Essayez les proxies résidentiels de Shifter, 205M+ IPs, 195+ pays, à partir de $0.75/GB.

Commencer