Most des configurations de scraping choisissent une configuration de proxy une fois, au moment de la conception, et s’y tiennent. Résidentiel avec ciblage par ville pour ce site, proxies ISP pour cet autre, datacenter là où ça fonctionne. Le choix est généralement fait en testant quelques options pendant un après-midi, et il est rarement reconsidéré avant qu’un problème survienne.
Il s’agit d’une décision prise dans l’incertitude, répétée des milliers de fois par jour, avec un retour après chaque requête. C’est exactement le type de problème que les statisticiens appellent un bandit manchot (multi-armed bandit), et il existe des algorithmes bien connus pour le résoudre. Ce guide explique le cadre conceptuel, propose une implémentation courte et testée, et montre ce qu’elle a donné dans une simulation où la meilleure option changeait à mi-parcours.
Points clés
- Chaque configuration de proxy que vous pourriez utiliser pour une cible est un « bras » ; chaque requête est un tirage ; une bonne réponse est une récompense. L’objectif est le coût le plus bas par bonne réponse, et non le taux de réussite le plus élevé à n’importe quel prix.
- L’échantillonnage de Thompson gère le compromis entre l’exploitation de la meilleure option connue et le test des autres, avec quelques lignes de code et sans calendrier de réglage.
- Les cibles évoluent, donc les anciennes preuves doivent s’estomper. Un facteur d’actualisation donne au sélecteur une mémoire portant sur environ les mille derniers résultats.
- Dans notre simulation, l’échantillonnage de Thompson actualisé est resté à 2% d’une stratégie connaissant les taux réels à l’avance, tandis qu’un choix fixe « sûr » coûtait 25% de plus par bonne réponse et le fait de s’en tenir au gagnant initial coûtait 57% de plus.
- Ne comptez comme succès que les réponses véritablement bonnes, et limitez la part d’exploration à laquelle une cible sensible est exposée.
Le cadre conceptuel
Imaginez une rangée de machines à sous, chacune payant avec une probabilité inconnue. Chaque tirage vous apprend quelque chose sur une machine, et chaque tirage consacré à apprendre sur une mauvaise machine est un tirage non consacré à une bonne. Cette tension entre exploiter ce que vous savez et explorer ce que vous ne savez pas est le problème du bandit manchot.
Pour la sélection de proxy, la correspondance est directe :
| Terme du bandit | En scraping |
|---|---|
| Bras | Une configuration de proxy pour une cible : type de proxy, ciblage, paramètre de session |
| Tirage | Une requête |
| Récompense | Une réponse qui contient les données souhaitées |
| Coût d’un tirage | Bande passante, nouvelles tentatives et temps consacrés à cette requête |
| Non-stationnarité | Le changement des défenses de la cible, ou l’évolution de la réputation d’un pool |
Deux caractéristiques rendent la version scraping plus difficile que celle des manuels. Les bras ont des coûts différents, de sorte que le meilleur bras est celui dont le coût par succès est le plus bas, et non celui dont le taux de réussite est le plus élevé. Et les taux évoluent : une configuration qui fonctionne aujourd’hui peut être bloquée demain, ce qui correspond au schéma décrit dans la contagion de sous-réseau et à la raison de construire un score de santé de la cible.
Le sélecteur
L’échantillonnage de Thompson conserve une distribution de probabilité sur le taux de réussite de chaque bras, une distribution Beta construite à partir de ses succès et de ses échecs. Avant chaque requête, il tire un taux plausible pour chaque bras et choisit celui qui semble le moins coûteux par succès selon ces tirages. Les bras disposant de peu de preuves ont des distributions larges, de sorte qu’ils tirent parfois un taux élevé et sont testés. Les bras disposant de nombreuses preuves tirent un taux proche de leur taux réel. L’exploration s’estompe d’elle-même à mesure que les preuves s’accumulent.
La version ci-dessous ajoute deux éléments : le coût par tentative, et une actualisation qui fait décroître les anciennes preuves vers l’a priori afin que le sélecteur puisse détecter un changement.
import random
class ProxySelector:
"""Pick a proxy configuration per request with Thompson sampling, minimising cost per successful response.
Each configuration keeps a Beta(successes, failures) belief about its success rate. A discount below 1
lets old outcomes fade, so the selector notices when a configuration that used to work starts failing.
"""
def __init__(self, costs, discount=0.999, prior=(1.0, 1.0), rng=None):
self.costs = dict(costs) # configuration -> cost per attempt
self.discount = discount
self.prior = prior
self.wins = {arm: prior[0] for arm in self.costs}
self.losses = {arm: prior[1] for arm in self.costs}
self.rng = rng or random.Random()
def choose(self):
"""Sample a plausible success rate for each configuration and take the cheapest per expected success."""
def sampled_cost_per_success(arm):
rate = self.rng.betavariate(self.wins[arm], self.losses[arm])
return self.costs[arm] / max(rate, 1e-9)
return min(self.costs, key=sampled_cost_per_success)
def update(self, arm, success):
"""Record one outcome. Every belief decays towards the prior first, so recent results count most."""
for a in self.costs:
self.wins[a] = self.prior[0] + self.discount * (self.wins[a] - self.prior[0])
self.losses[a] = self.prior[1] + self.discount * (self.losses[a] - self.prior[1])
if success:
self.wins[arm] += 1
else:
self.losses[arm] += 1
En pratique, appelez choose() avant chaque requête, envoyez la requête via la configuration choisie, puis appelez update() en indiquant si la réponse était bonne. Une actualisation de 0.999 donne une mémoire effective d’environ mille résultats ; 0.995 environ deux cents.
La simulation
Pour voir comment le sélecteur se comporte lorsque la réponse change, nous avons simulé une cible avec quatre configurations. Les taux de réussite et les coûts ci-dessous sont des hypothèses choisies pour rendre les compromis visibles, et non des mesures d’un fournisseur ou d’un réseau particulier :
| Configuration | Taux de réussite supposé | Coût supposé par tentative | Coût par succès |
|---|---|---|---|
| Datacenter | 25% | 0.5 | 2.00 |
| ISP | 80%, tombant à 30% après la requête 5,000 | 0.9 | 1.13, puis 3.00 |
| Résidentiel, ciblage par pays | 92% | 1.3 | 1.41 |
| Résidentiel, ciblage par ville | 95% | 1.6 | 1.68 |
Les coûts sont des unités relatives couvrant la bande passante et la charge d’une tentative. Pour les 5,000 premières requêtes, l’ISP est le moyen le moins coûteux d’obtenir une bonne réponse ; puis la cible commence à le bloquer, et le résidentiel avec ciblage par pays devient le meilleur choix. Chaque stratégie a effectué 20,000 requêtes, et nous avons répété chaque stratégie 100 fois avec des graines aléatoires différentes.
| Stratégie | Coût par bonne réponse | Par rapport à la référence parfaite | Bonnes réponses | Requêtes pour s’adapter après le changement |
|---|---|---|---|---|
| Référence parfaite (connaît les taux réels) | 1.348 | référence | 17,806 | 0 |
| Échantillonnage de Thompson, actualisation 0.999 | 1.375 | +2.0% | 16,694 | environ 800 |
| Échantillonnage de Thompson, actualisation 0.995 | 1.389 | +3.0% | 15,704 | environ 1,075 |
| Échantillonnage de Thompson, sans actualisation | 1.428 | +5.9% | 15,933 | environ 3,500 |
| Epsilon-glouton, 10% d’exploration | 1.441 | +6.9% | 15,848 | environ 2,800 |
| Échantillonnage de Thompson, actualisation 0.98 | 1.443 | +7.0% | 13,834 | jamais stabilisé |
| Toujours résidentiel, ciblage par ville | 1.684 | +24.9% | 19,000 | non applicable |
| Round robin sur les quatre | 1.689 | +25.3% | 12,731 | non applicable |
| Toujours ISP, le gagnant initial | 2.118 | +57.1% | 8,501 | non applicable |
Les chiffres sont des moyennes sur 100 exécutions ; pour chaque stratégie, les 90% médians des exécutions couvraient moins de 2.5% de sa moyenne. « Requêtes pour s’adapter » correspond au nombre médian de requêtes après le changement jusqu’à ce que 90% d’une fenêtre de 200 requêtes soit allé à la nouvelle meilleure configuration.
Ce que disent les résultats
Les choix fixes sont coûteux dans les deux sens. Toujours utiliser la configuration la plus fiable a produit le plus de bonnes réponses, mais a coûté 25% de plus pour chacune. Toujours utiliser la configuration qui avait gagné au départ a été la pire stratégie de toutes une fois la cible modifiée, à 57% au-dessus de la référence.
Apprendre ne suffit pas sans oublier. L’échantillonnage de Thompson sans actualisation a mis environ 3,500 requêtes après le changement avant de cesser de faire confiance aux preuves accumulées pour l’ancien gagnant. Une actualisation de 0.999 a réduit ce chiffre à environ 800.
Oublier trop vite pose son propre problème. Avec une actualisation de 0.98, le sélecteur ne se souvenait que d’environ 50 résultats, continuait à retester les mauvaises options et ne s’est jamais stabilisé. La plage utile dépend du trafic : la mémoire doit couvrir suffisamment de requêtes pour distinguer les configurations entre elles, et pas plus.
L’objectif détermine la réponse. Le bandit minimisait le coût par bonne réponse. Si ce qui compte est le maximum de données indépendamment du coût, ou un délai à respecter, la récompense doit le refléter ; sinon le sélecteur optimisera très efficacement la mauvaise chose.
L’utiliser sur du trafic réel
- Définissez le succès de manière stricte. Un code HTTP 200 accompagné d’une page de blocage ou de résultats vides est un échec. Fournissez au sélecteur les mêmes vérifications que celles utilisées pour le taux d’échec silencieux, sinon il apprendra à préférer les configurations qui échouent silencieusement.
- Utilisez un sélecteur par cible. Les taux de réussite diffèrent selon le site, donc un sélecteur partagé lisse exactement les différences que vous souhaitez lui faire apprendre.
- Plafonnez l’exploration sur les cibles sensibles. Chaque requête d’exploration via une configuration médiocre est une requête qu’un site peut retenir contre vous. Retirez les configurations clairement inadaptées plutôt que de laisser le sélecteur le redécouvrir.
- Gardez des sessions cohérentes. Choisissez la configuration par session, et non par requête, pour tout ce qui dépend d’un état, comme les connexions ou les paniers, comme l’explique sessions persistantes versus rotatives.
- Utilisez des coûts réels. Pour les proxies facturés à la bande passante, le coût par tentative dépend du poids de la page et des nouvelles tentatives ; les chiffres derrière le coût par enregistrement propre constituent les bonnes données d’entrée.
- Conservez une base d’équilibrage de charge. Un bandit décide quelle configuration utiliser, pas comment répartir les requêtes en son sein ; cela reste le rôle de l’architecture de rotation, de concurrence et de nouvelles tentatives.
En résumé
Choisir une configuration de proxy une fois et s’y tenir revient à parier que ni vos coûts ni la cible ne changeront. Traiter chaque requête comme une petite expérience, avec l’échantillonnage de Thompson et une mémoire raisonnable, transforme ce pari en une mesure qui continue de se mettre à jour.
Dans notre simulation, cela est resté à 2% de la connaissance de la réponse à l’avance, s’est adapté en environ 800 requêtes lorsque la meilleure option a changé, et a évité la surcharge de 25% à 57% des stratégies fixes. Les taux et les coûts étaient des hypothèses ; le code est assez court pour être exécuté sur vos propres données.
Sources et références
- Daniel Russo et ses collègues, A Tutorial on Thompson Sampling, 2017.
- Simulation réalisée par Shifter le 2 October 2026 à l’aide du code ci-dessus ; les taux de réussite et les coûts sont des hypothèses, non des mesures d’un réseau quelconque.