Die meisten Scraping-Setups wählen eine Proxy-Konfiguration einmal, zur Entwurfszeit, und behalten sie bei. Residential mit City-Targeting für diese Website, ISP-Proxys für jene, Datacenter, wo es funktioniert. Die Wahl entsteht meist dadurch, dass man an einem Nachmittag ein paar Optionen ausprobiert, und sie wird selten überdacht, bis etwas nicht mehr funktioniert.
Das ist eine Entscheidung unter Unsicherheit, die tausende Male am Tag getroffen wird, mit Feedback nach jeder Anfrage. Es ist genau die Form von Problem, das Statistiker als Multi-Armed Bandit bezeichnen, und es gibt dafür gut verstandene Algorithmen. Dieser Leitfaden erklärt den Rahmen, liefert eine kurze getestete Implementierung und zeigt, was sie in einer Simulation bewirkte, bei der sich die beste Option auf halbem Weg änderte.
Die wichtigsten Erkenntnisse
- Jede Proxy-Konfiguration, die man für ein Ziel verwenden könnte, ist ein „Arm“; jede Anfrage ist ein Zug; eine gute Antwort ist eine Belohnung. Das Ziel sind die niedrigsten Kosten pro guter Antwort, nicht die höchste Erfolgsrate um jeden Preis.
- Thompson Sampling löst den Kompromiss zwischen der Nutzung der besten bekannten Option und dem Testen der anderen, mit wenigen Zeilen Code und ohne Abstimmungsplan.
- Ziele ändern sich, daher sollten alte Belege verblassen. Ein Discount-Faktor gibt dem Selektor ein Gedächtnis von ungefähr den letzten tausend Ergebnissen.
- In unserer Simulation lag diskontiertes Thompson Sampling innerhalb von 2% einer Strategie, die die wahren Raten im Voraus kannte, während eine feste „sichere“ Wahl 25% mehr pro guter Antwort kostete und das Festhalten am frühen Gewinner 57% mehr kostete.
- Zählen Sie nur wirklich gute Antworten als Erfolge, und begrenzen Sie, wie viel Exploration ein sensibles Ziel zu sehen bekommt.
Der Rahmen
Stellen Sie sich eine Reihe von Spielautomaten vor, von denen jeder mit einer unbekannten Wahrscheinlichkeit auszahlt. Jeder Zug lehrt Sie etwas über eine Maschine, und jeder Zug, der auf das Lernen über eine schlechte Maschine verwendet wird, ist ein Zug, der nicht für eine gute Maschine ausgegeben wird. Dieses Spannungsverhältnis zwischen dem Ausnutzen dessen, was man weiß, und dem Erkunden dessen, was man nicht weiß, ist das Multi-Armed-Bandit-Problem.
Für die Proxy-Auswahl ist die Zuordnung direkt:
| Bandit-Begriff | Im Scraping |
|---|---|
| Arm | Eine Proxy-Konfiguration für ein Ziel: Proxy-Typ, Targeting, Session-Einstellung |
| Zug | Eine Anfrage |
| Belohnung | Eine Antwort, die die gewünschten Daten enthält |
| Kosten eines Zugs | Bandbreite, Wiederholungen und Zeit, die für diese Anfrage aufgewendet werden |
| Nicht-Stationarität | Das Ziel ändert seine Abwehrmaßnahmen, oder der Ruf eines Pools verschiebt sich |
Zwei Merkmale machen die Scraping-Version schwieriger als die Lehrbuchversion. Arme kosten unterschiedlich viel, sodass der beste Arm derjenige mit den niedrigsten Kosten pro Erfolg ist, nicht der mit der höchsten Erfolgsrate. Und die Raten bewegen sich: Eine Konfiguration, die heute funktioniert, kann morgen blockiert werden, was das Muster hinter Subnetz-Ansteckung ist und der Grund, einen Target-Health-Score aufzubauen.
Der Selektor
Thompson Sampling hält eine Wahrscheinlichkeitsverteilung über die Erfolgsrate jedes Arms aufrecht, eine Beta-Verteilung, die aus seinen Erfolgen und Misserfolgen aufgebaut ist. Vor jeder Anfrage zieht es eine plausible Rate für jeden Arm und wählt den Arm, der unter diesen Zügen am günstigsten pro Erfolg erscheint. Arme mit wenig Beleg haben breite Verteilungen, sodass sie gelegentlich eine hohe Rate ziehen und ausprobiert werden. Arme mit viel Beleg ziehen nahe an ihrer wahren Rate. Die Exploration verblasst von selbst, während sich der Beleg aufbaut.
Die untenstehende Version fügt zwei Dinge hinzu: Kosten pro Versuch und einen Discount, der alte Belege in Richtung der Prior-Verteilung abklingen lässt, sodass der Selektor Veränderungen bemerken kann.
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
In der Praxis rufen Sie choose() vor jeder Anfrage auf, senden die Anfrage über die gewählte Konfiguration und rufen update() mit der Information auf, ob die Antwort gut war. Ein Discount von 0,999 ergibt ein effektives Gedächtnis von etwa tausend Ergebnissen; 0,995 etwa zweihundert.
Die Simulation
Um zu sehen, wie sich der Selektor verhält, wenn sich die Antwort ändert, simulierten wir ein Ziel mit vier Konfigurationen. Die unten aufgeführten Erfolgsraten und Kosten sind Annahmen, die gewählt wurden, um die Kompromisse sichtbar zu machen, und keine Messungen eines bestimmten Anbieters oder Netzwerks:
| Konfiguration | Angenommene Erfolgsrate | Angenommene Kosten pro Versuch | Kosten pro Erfolg |
|---|---|---|---|
| Datacenter | 25% | 0,5 | 2,00 |
| ISP | 80%, fallend auf 30% nach Anfrage 5.000 | 0,9 | 1,13, dann 3,00 |
| Residential, Country-Targeting | 92% | 1,3 | 1,41 |
| Residential, City-Targeting | 95% | 1,6 | 1,68 |
Die Kosten sind relative Einheiten, die Bandbreite und den Mehraufwand eines Versuchs abdecken. Für die ersten 5.000 Anfragen ist ISP der günstigste Weg zu einer guten Antwort; dann beginnt das Ziel, es zu blockieren, und Residential mit Country-Targeting wird zur besten Wahl. Jede Strategie führte 20.000 Anfragen durch, und wir wiederholten jede Strategie 100 Mal mit unterschiedlichen Zufallszahlen-Seeds.
| Strategie | Kosten pro guter Antwort | Gegenüber perfekter Rückschau | Gute Antworten | Anfragen bis zur Anpassung nach der Änderung |
|---|---|---|---|---|
| Perfekte Rückschau (kennt die wahren Raten) | 1,348 | Basislinie | 17.806 | 0 |
| Thompson Sampling, Discount 0,999 | 1,375 | +2,0% | 16.694 | etwa 800 |
| Thompson Sampling, Discount 0,995 | 1,389 | +3,0% | 15.704 | etwa 1.075 |
| Thompson Sampling, kein Discount | 1,428 | +5,9% | 15.933 | etwa 3.500 |
| Epsilon-Greedy, 10% Exploration | 1,441 | +6,9% | 15.848 | etwa 2.800 |
| Thompson Sampling, Discount 0,98 | 1,443 | +7,0% | 13.834 | nie eingependelt |
| Immer Residential, City-Targeting | 1,684 | +24,9% | 19.000 | nicht zutreffend |
| Round Robin über alle vier | 1,689 | +25,3% | 12.731 | nicht zutreffend |
| Immer ISP, der frühe Gewinner | 2,118 | +57,1% | 8.501 | nicht zutreffend |
Die Zahlen sind Durchschnittswerte über 100 Durchläufe; für jede Strategie umfassten die mittleren 90% der Durchläufe weniger als 2,5% ihres Durchschnitts. „Anfragen bis zur Anpassung“ ist die mediane Anzahl von Anfragen nach der Änderung, bis 90% eines 200-Anfragen-Fensters an die neue beste Konfiguration gingen.
Was die Ergebnisse sagen
Feste Entscheidungen sind in beide Richtungen teuer. Die durchgehende Verwendung der zuverlässigsten Konfiguration lieferte die meisten guten Antworten, kostete aber 25% mehr pro Antwort. Die durchgehende Verwendung der Konfiguration, die früh gewann, war die schlechteste Strategie von allen, sobald sich das Ziel änderte, mit 57% über der Basislinie.
Lernen reicht ohne Vergessen nicht aus. Undiskontiertes Thompson Sampling brauchte etwa 3.500 Anfragen nach der Änderung, um aufzuhören, den Belegen zu vertrauen, die es für den alten Gewinner aufgebaut hatte. Ein Discount von 0,999 reduzierte das auf etwa 800.
Zu schnelles Vergessen ist ein eigenes Problem. Bei einem Discount von 0,98 erinnerte sich der Selektor nur an etwa 50 Ergebnisse, testete die schlechten Optionen immer wieder und pendelte sich nie ein. Der sinnvolle Bereich hängt vom Datenverkehr ab: Das Gedächtnis sollte genug Anfragen abdecken, um die Konfigurationen zu unterscheiden, und nicht mehr.
Das Ziel entscheidet über die Antwort. Der Bandit minimierte die Kosten pro guter Antwort. Wenn das Wichtige die größte Datenmenge unabhängig von den Kosten ist, oder eine Frist, muss die Belohnung das widerspiegeln; sonst wird der Selektor das Falsche sehr effizient optimieren.
Einsatz bei echtem Datenverkehr
- Definieren Sie Erfolg streng. Ein HTTP 200 mit einer Blockseite oder leeren Ergebnissen ist ein Misserfolg. Füttern Sie den Selektor mit denselben Prüfungen, die Sie für die Silent-Failure-Rate verwenden, sonst wird er lernen, Konfigurationen zu bevorzugen, die still versagen.
- Betreiben Sie einen Selektor pro Ziel. Die Erfolgsraten unterscheiden sich je nach Website, sodass ein gemeinsamer Selektor genau die Unterschiede verwässert, die er lernen soll.
- Begrenzen Sie die Exploration bei sensiblen Zielen. Jede exploratorische Anfrage über eine schlechte Konfiguration ist eine Anfrage, die eine Website gegen Sie zählen kann. Entfernen Sie Konfigurationen, die klar ungeeignet sind, anstatt den Selektor dies erneut entdecken zu lassen.
- Halten Sie Sessions konsistent. Wählen Sie die Konfiguration pro Session, nicht pro Anfrage, für alles, was von Zustand abhängt, wie Logins oder Warenkörbe, wie Sticky versus Rotating Sessions erklärt.
- Verwenden Sie echte Kosten. Bei nach Bandbreite abgerechneten Proxys hängen die Kosten pro Versuch von der Seitenlast und Wiederholungen ab; die Zahlen hinter Cost per Clean Record sind die richtigen Eingaben.
- Behalten Sie eine Grundlage für Lastverteilung. Ein Bandit entscheidet, welche Konfiguration verwendet wird, nicht wie Anfragen innerhalb davon verteilt werden; das ist weiterhin die Aufgabe der Rotations-, Nebenläufigkeits- und Wiederholungsarchitektur.
Das Fazit
Eine Proxy-Konfiguration einmal zu wählen und beizubehalten ist eine Wette darauf, dass sich weder Ihre Kosten noch das Ziel ändern. Jede Anfrage als kleines Experiment zu behandeln, mit Thompson Sampling und einem sinnvollen Gedächtnis, verwandelt diese Wette in eine Messung, die sich ständig aktualisiert.
In unserer Simulation lag das innerhalb von 2% des Wissens der Antwort im Voraus, passte sich innerhalb von etwa 800 Anfragen an, als sich die beste Option änderte, und vermied den Aufschlag von 25% bis 57% der festen Strategien. Die Raten und Kosten waren Annahmen; der Code ist kurz genug, um ihn gegen Ihre eigenen Daten laufen zu lassen.
Quellen und Referenzen
- Daniel Russo und Kollegen, A Tutorial on Thompson Sampling, 2017.
- Simulation durchgeführt von Shifter am 2. Oktober 2026 mit dem obigen Code; Erfolgsraten und Kosten sind Annahmen, keine Messungen eines bestimmten Netzwerks.