← Retour au blog

Élimination des mauvais proxies dans le pool du parseur : métriques d'erreurs et rotation en Python

Nous analysons quelles métriques d'erreurs signalent une dégradation des proxies dans le pool de parseurs et comment construire un système automatique de rejet et de rotation des IP.

📅19 octobre 2026

Si le parseur Wildberries ou Ozon a soudainement commencé à renvoyer des pages vides, des délais d'attente et des erreurs 403, le problème ne réside pas toujours dans la plateforme elle-même. Le plus souvent, une partie des adresses IP dans le pool de proxies est déjà "périmée" : elles ont été bannies, ralenties ou mises sous CAPTCHA. Nous examinons quelles métriques distinguent un proxy fonctionnel d'un mort et comment automatiser le filtrage sans contrôle manuel.

Pourquoi le pool de proxies se dégrade avec le temps

Tout pool de proxies — même acheté auprès d'un fournisseur fiable — perd avec le temps une partie de ses adresses fonctionnelles. Les raisons sont diverses : les systèmes antifraude de Wildberries, Ozon et Avito accumulent des marques de réputation sur les IP, certaines adresses se retrouvent sur des listes noires après des requêtes massives d'autres clients du fournisseur (si le proxy est partagé), les plages d'adresses des centres de données sont bannies par lots selon les sous-réseaux, et les IP résidentielles quittent parfois simplement le pool parce qu'un utilisateur réel a éteint son appareil.

Si le pool n'est pas nettoyé, la part de proxies "morts" augmente de manière non linéaire : d'abord 5 %, puis après une semaine de parsing actif — 20-30 %. Le parseur commence à dépenser des ressources sur des tentatives répétées via des IP non fonctionnelles, le temps moyen de collecte d'une page augmente, et les logs accumulent des erreurs qui masquent les problèmes réels (par exemple, des changements dans la mise en page du site). Sans un système de filtrage, vous maintenez soit un pool gonflé "pour réserve", soit vous subissez régulièrement des échecs dans la collecte de données.

L'objectif n'est pas seulement de signaler une erreur, mais de la classifier : surcharge temporaire du serveur, bannissement d'une IP spécifique, blocage régional ou problème du côté du parseur (en-têtes incorrects, cookies obsolètes). Le type d'erreur détermine s'il faut retirer le proxy du pool définitivement, le mettre en "quarantaine" temporairement ou simplement répéter la requête.

Métriques clés d'erreur pour le filtrage

Pour distinguer une panne temporaire d'un proxy réellement mort, il est nécessaire de collecter plusieurs métriques pour chaque IP, et non de se fier à un seul statut HTTP. Voici un ensemble de base à enregistrer à chaque requête :

Métrique Ce que cela signale Action
HTTP 403 / 429 Bannissement ou limitation de débit sur l'IP Quarantaine de 30-60 minutes, en cas de répétition — suppression
Délai de connexion Proxy surchargé ou mort 3 délais consécutifs — suppression du pool
CAPTCHA dans la réponse IP suspectée par l'antibot Réduire la fréquence des requêtes ou remplacer l'IP
Temps de réponse > 5 sec Dégradation de la vitesse du côté du proxy Baisser la priorité dans le pool
Taille de la réponse inférieure à la moyenne Retour d'une page de blocage Vérification supplémentaire du contenu avant acceptation
Taux de succès des requêtes (success rate) Évaluation intégrale de la qualité de l'IP En dessous de 70 % pour 50 requêtes — suppression

Il est important de ne pas confondre les codes d'erreur du site avec les erreurs réseau. Par exemple, 404 n'est pas un problème de proxy, mais un produit manquant sur Wildberries. En revanche, si la même IP reçoit régulièrement 403 là où d'autres IP reçoivent 200, c'est un signe clair de bannissement ciblé de cette adresse.

Algorithme de scoring des proxies

Au lieu d'un binaire "fonctionnel / non fonctionnel", il est plus pratique d'attribuer à chaque IP un score de 0 à 100, qui diminue progressivement en cas d'erreurs et se rétablit lors de requêtes réussies. Cela permet de ne pas jeter un proxy après un seul échec aléatoire (le serveur du marketplace a pu simplement se bloquer pendant une seconde), mais d'éliminer rapidement les IP qui renvoient systématiquement des erreurs.

La logique est simple : une requête réussie avec le code 200 et un contenu valide ajoute +2 au score (mais pas au-delà de 100). Un délai — moins 15. Code 403/429 — moins 25. CAPTCHA dans la réponse — moins 20. Si le score tombe en dessous d'un seuil (par exemple, 30), le proxy est mis en quarantaine pour une durée fixe, et si après son retour de quarantaine le score tombe à nouveau en dessous de 10 — l'IP est définitivement supprimée du pool et enregistrée pour analyse.

Cette approche ressemble à un système de réputation dans des navigateurs anti-détecteurs comme Dolphin Anty ou AdsPower, où les comptes ont une "santé" en fonction de l'activité. La même mécanique fonctionne également très bien pour les adresses IP dans le parsing.

Code : ProxyPool avec filtrage automatique en Python

Ci-dessous se trouve le cadre fonctionnel d'une classe pour gérer un pool de proxies en tenant compte des métriques d'erreur. Il peut être intégré à n'importe quel parseur utilisant requests, httpx ou Scrapy, en remplaçant la logique d'obtention de proxies par un appel à pool.get_proxy().

import time
import random
from dataclasses import dataclass, field
from typing import Optional

@dataclass
class ProxyStats:
    address: str
    score: int = 100
    quarantine_until: float = 0.0
    total_requests: int = 0
    total_success: int = 0
    consecutive_timeouts: int = 0
    is_dead: bool = False

    @property
    def success_rate(self) -> float:
        if self.total_requests == 0:
            return 1.0
        return self.total_success / self.total_requests


class ProxyPool:
    def __init__(self, proxies: list[str],
                 min_score: int = 30,
                 dead_score: int = 10,
                 quarantine_seconds: int = 1800):
        self.stats = {p: ProxyStats(address=p) for p in proxies}
        self.min_score = min_score
        self.dead_score = dead_score
        self.quarantine_seconds = quarantine_seconds

    def _is_available(self, stats: ProxyStats) -> bool:
        if stats.is_dead:
            return False
        if stats.quarantine_until > time.time():
            return False
        return True

    def get_proxy(self) -> Optional[str]:
        available = [s for s in self.stats.values() if self._is_available(s)]
        if not available:
            return None
        # trier par score, prendre un aléatoire parmi le top 30 % pour ne pas frapper une seule IP
        available.sort(key=lambda s: s.score, reverse=True)
        top_pool = available[:max(1, len(available) // 3)]
        return random.choice(top_pool).address

    def report_success(self, proxy: str):
        s = self.stats[proxy]
        s.total_requests += 1
        s.total_success += 1
        s.consecutive_timeouts = 0
        s.score = min(100, s.score + 2)

    def report_timeout(self, proxy: str):
        s = self.stats[proxy]
        s.total_requests += 1
        s.consecutive_timeouts += 1
        s.score -= 15
        if s.consecutive_timeouts >= 3:
            self._quarantine(s)
        self._check_dead(s)

    def report_blocked(self, proxy: str, captcha: bool = False):
        s = self.stats[proxy]
        s.total_requests += 1
        s.score -= 20 if captcha else 25
        self._quarantine(s)
        self._check_dead(s)

    def _quarantine(self, s: ProxyStats):
        if s.score < self.min_score:
            s.quarantine_until = time.time() + self.quarantine_seconds

    def _check_dead(self, s: ProxyStats):
        if s.score <= self.dead_score:
            s.is_dead = True

    def stats_report(self) -> list[dict]:
        return [
            {
                "proxy": s.address,
                "score": s.score,
                "success_rate": round(s.success_rate, 2),
                "dead": s.is_dead,
            }
            for s in self.stats.values()
        ]

Dans un parseur réel, les appels à report_success, report_timeout et report_blocked sont insérés directement dans le gestionnaire de réponse après chaque requête HTTP. Exemple d'intégration avec requests :

import requests

def fetch(url: str, pool: ProxyPool):
    proxy = pool.get_proxy()
    if not proxy:
        raise RuntimeError("Aucun proxy disponible dans le pool")

    proxies = {"http": proxy, "https": proxy}
    try:
        resp = requests.get(url, proxies=proxies, timeout=8)
    except requests.exceptions.Timeout:
        pool.report_timeout(proxy)
        return None

    if resp.status_code in (403, 429):
        pool.report_blocked(proxy)
        return None

    if "captcha" in resp.text.lower():
        pool.report_blocked(proxy, captcha=True)
        return None

    pool.report_success(proxy)
    return resp

Cette approche réduit la charge du monitoring manuel : vous configurez une fois les seuils, et ensuite le pool "se rétablit" tout seul, en éliminant les adresses mortes et en retournant de la quarantaine celles qui étaient temporairement suspendues.

Seuils pour le bannissement des IP

Il n'existe pas de chiffres universels — les seuils dépendent de la plateforme et de l'agressivité de son antibot. Mais il y a des repères qui fonctionnent bien en pratique lors du parsing de Wildberries, Ozon et Avito :

  • Taux de succès inférieur à 70 % sur un échantillon de 30-50 requêtes — motif pour supprimer l'IP, même si le score actuel n'est pas encore critique.
  • 3 délais consécutifs — signifie presque toujours que le proxy est physiquement inaccessible ou surchargé, il n'est pas utile de le garder dans le pool actif.
  • CAPTCHA plus d'une fois sur 20 requêtes — l'IP est déjà suspectée par l'antibot, il vaut mieux réduire la fréquence des requêtes ou remplacer l'adresse.
  • Temps de réponse moyen supérieur à 2x la médiane du pool — signal de dégradation, pas critique, mais motif de baisser la priorité lors du choix.

Une quarantaine de 30-60 minutes est généralement suffisante pour les proxies de centres de données et résidentiels, afin que l'antibot "oublie" l'activité suspecte temporaire. Pour les proxies mobiles, la situation est différente : l'IP change lors du passage à une autre cellule par l'opérateur, donc la quarantaine peut être plus courte — 10-15 minutes, car après rotation, c'est physiquement une autre adresse.

Healthcheck et rotation en arrière-plan

En plus du filtrage réactif (basé sur les erreurs dans le flux principal de parsing), il est utile de faire tourner un healthcheck en arrière-plan — un processus séparé qui, toutes les quelques minutes, teste les proxies avec des requêtes légères sur une URL neutre (par exemple, httpbin.org/ip) et met à jour les statistiques du pool avant que le proxy n'entre dans une requête active vers Wildberries ou Ozon.

import threading

def healthcheck_loop(pool: ProxyPool, interval: int = 300):
    while True:
        for proxy in list(pool.stats.keys()):
            try:
                r = requests.get(
                    "https://httpbin.org/ip",
                    proxies={"http": proxy, "https": proxy},
                    timeout=5
                )
                if r.status_code == 200:
                    pool.report_success(proxy)
                else:
                    pool.report_blocked(proxy)
            except requests.exceptions.RequestException:
                pool.report_timeout(proxy)
        time.sleep(interval)

threading.Thread(target=healthcheck_loop, args=(pool,), daemon=True).start()

Ce processus en arrière-plan est particulièrement important si le pool est grand (centaines d'IP) et que le parsing ne se fait pas de manière continue, mais par lots — sans healthcheck, une partie des proxies a simplement le temps de "périr" entre les lancements, et les premières minutes de parsing sont gaspillées à filtrer en direct, au lieu de travailler immédiatement avec des adresses vérifiées.

Quel type de proxy choisir pour le parsing des marketplaces

Le choix du type de proxy influence directement la fréquence à laquelle il faudra lancer le filtrage et la sévérité des seuils. Pour un parsing stable de Wildberries et Ozon, où l'antibot analyse les modèles comportementaux et la réputation du sous-réseau, les proxies résidentiels se montrent les plus efficaces — leurs IP appartiennent à de véritables fournisseurs d'accès Internet résidentiels, et l'antibot les bloque beaucoup moins souvent par lots selon le sous-réseau.

Pour des sources plus simples et moins protégées (par exemple, la collecte de données publiques sur Avito ou le suivi des prix sur des vitrines simples), des proxies de centres de données suffisent souvent — elles sont plus rapides et moins chères, ce qui est critique pour de gros volumes de requêtes, mais elles sont également bannies un peu plus souvent, donc les seuils de filtrage doivent être plus stricts pour elles.

Type de proxy Vitesse de filtrage Quand utiliser
Centre de données Élevée, les sous-réseaux sont bannis par lots Vitrines simples, gros volumes, faible protection
Résidentiels Moyenne, les IP sont bannies de manière ciblée Wildberries, Ozon, sites avec antibot
Mobiles Faible, l'IP change automatiquement par l'opérateur Sources fortement protégées, parsing à haute fréquence

En pratique, de nombreuses équipes utilisent un pool hybride : la majorité des requêtes passent par des IP résidentielles, et en cas de bannissements massifs, des proxies mobiles sont connectés comme "réserve d'urgence", car leur changement d'IP se fait naturellement lors de la rotation du réseau cellulaire et ne nécessite pas d'intervention manuelle.

Conclusion

Un pool de proxies sans système de filtrage se dégrade inévitablement, et plus il fonctionne sans nettoyage, plus le parseur gaspille de ressources sur des IP mortes. Un modèle de scoring tenant compte des codes HTTP, des délais, des CAPTCHA et du taux de succès permet d'identifier automatiquement les adresses problématiques et de maintenir le pool en état de fonctionnement sans contrôle manuel. Le code de cet article peut être adapté à n'importe quelle pile — requests, httpx, Scrapy ou votre propre parseur asynchrone.

Si vous commencez à constituer un pool pour le parsing des marketplaces et souhaitez réduire la fréquence des bannissements dès le départ, nous vous recommandons de vous tourner vers des proxies résidentiels — ils offrent un taux de succès nettement plus élevé sur des plateformes protégées comme Wildberries et Ozon par rapport aux adresses de centres de données ordinaires.