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.