- Teilt praktische Erfahrungen beim Crawlen von 1 Milliarde Webseiten in 24 Stunden und den Entwurfsprozess eines modernen Web-Crawling-Systems
- Mit aktueller Hardware und Cloud-Infrastruktur wurde Crawling im großen Maßstab für nur einige hundert Dollar realisiert; dabei zeigte sich Parsing als zentraler Engpass
- Es wurde nur HTML geparst, ohne JavaScript auszuführen, dennoch blieb ein erheblicher Teil der Webseiten zugänglich
- Entwurf einer Redis-basierten Node-Cluster-Architektur; maximale Effizienz durch domainbasiertes Sharding und Optimierung der Prozessstruktur
- Nicht das Netzwerk, sondern CPU, SSL und Speicher erwiesen sich als wichtigste Engpässe; die Verwaltung großer Domain-Frontiers war das Kernproblem
Problemdefinition
- Zielsetzung: 1 Milliarde Webseiten in 24 Stunden crawlen
- Das Budget lag bei einigen hundert Dollar (am Ende etwa 462 Dollar) und wurde auf ein ähnliches Niveau wie im Beispiel von 2012 ausgerichtet
- Es wurde nur HTML gesammelt; JavaScript wurde nicht ausgeführt, und es wurden nur
<a>-Links extrahiert
- Politeness stand im Vordergrund: robots.txt wurde beachtet, User-Agent-Informationen wurden mitgegeben, Domains wurden auf Anfrage ausgeschlossen, Ziel waren nur die Top 1 Million populärsten Domains, und für dieselbe Domain galt eine Wartezeit von 70 Sekunden
- Fehlertoleranz wurde eingeplant: Bei Node-Ausfällen wurde ein Neustart und ein gewisser Datenverlust in Kauf genommen, da der Ansatz auf Sampling basierte
Architektur und Design
- Anders als beim klassischen Stil von Systemdesign-Interviews (Verteilung nach Funktionen) wurde eine Struktur gewählt, in der jede Node alle Funktionen selbst übernimmt (Crawl-Status, Parsing, Fetching, Speicherung usw.)
- 12 Nodes, jede mit einer
i7i.4xlarge-Instanz (16 vCPU, 128GB RAM, 10Gbps, 3750GB Storage)
- Jede Node bestand aus 1 Redis, 9 Fetchern und 6 Parser-Prozessen
- In Redis wurden Domain-Frontiers, Fetch-Queues, besuchte URLs, Bloom-Filter, robots.txt und Parsing-Queues gespeichert
- Fetcher: Holen URLs domainweise aus der Queue und fetchen sie; mit asyncio liefen 6000 bis 7000 Aufgaben gleichzeitig, wobei CPU der Hauptengpass war
- Parser: 80 asynchrone Worker für HTML-Parsing und Link-Extraktion, ebenfalls CPU-lastig
- Storage: Statt S3 wurde lokaler Instanz-Speicher genutzt, um die Kosten für die Speicherung großer Seiten zu senken
- Sharding: Verteilung nach Domains auf Nodes (ohne Cross-Kommunikation); zur Lösung von Ungleichgewichten bei populären Domains wurde die Zahl der Sharding-Nodes angepasst
Wichtige Alternativen und Experimente
- Verschiedene Speicherlösungen wie SQLite und PostgreSQL wurden getestet; am Ende bot Redis die beste Performance
- Auch vertikale Skalierung (eine einzelne große Instanz) wurde ausprobiert, stieß aber wegen Software-Limits auf Engpässe; deshalb fiel die Entscheidung auf horizontale Skalierung (mehrere Nodes)
- Cross-Kommunikation zwischen den Nodes wurde eliminiert, Parallelisierung fand stattdessen innerhalb einzelner Nodes statt
Zentrale Erkenntnisse aus dem Crawling
Parsing ist der größte Engpass
- Die durchschnittliche Seitengröße ist gegenüber früher (2012: 51KB) stark gestiegen (Durchschnitt 242KB, Median 138KB)
- Der Wechsel von lxml zu selectolax (auf Lexbor-Basis) beschleunigte das Parsing deutlich
- Durch Trunkierung auf eine maximale Seitengröße von 250KB wurde die Effizienz verbessert
- Im Ergebnis wurden mit einem einzelnen Parser 160 Seiten pro Sekunde geparst; nach Anpassung des Verhältnisses von Fetchern zu Parsern auf 9:6 wurden etwa 950 Seiten pro Sekunde verarbeitet
Fetching: Was einfacher und was schwieriger wurde
- Netzwerkbandbreite war überraschenderweise nicht der Engpass (pro Node wurden von 25Gbps nur etwa 8Gbps genutzt)
- Auch DNS war kein Problem, da nur populäre Domains Ziel waren
- Dagegen erwies sich der SSL-Handshake mit 25 % der gesamten CPU-Nutzung als einer der größten Engpässe
- Da die meisten Seiten inzwischen auf HTTPS umgestellt haben, sind die CPU-Kosten gestiegen
Praktischer Crawl-Lauf und Probleme
- In ersten Experimenten lief das System nur einige Stunden auf einer einzelnen Node (
i7i.2xlarge), für den eigentlichen Crawl wurde dann auf 12 Nodes skaliert
- Es traten Speicherprobleme auf: Die Frontier populärer Domains (noch nicht besuchte URLs) wuchs auf mehrere Dutzend GB an, wodurch Nodes wiederholt ausfielen
- Populäre Domains (z. B. yahoo.com, wikipedia.org) oder Seiten mit ungewöhnlich vielen Links verursachten die Probleme
- Problematische Domains wurden manuell ausgeschlossen; bei Ausfällen wurde durch Node-Neustarts und Trunkierung der Frontier wiederhergestellt
Vergleich von Theorie und Praxis
- Im Vergleich zur klassischen Schätzung „10 Milliarden Seiten in 5 Tagen mit 5 Maschinen“ lagen die realen Zahlen einigermaßen nahe daran
- Berücksichtigt man die tatsächliche Netzwerk- und CPU-Auslastung pro Node, wäre mit weiterer Optimierung noch höherer Durchsatz möglich
Künftige Aufgaben und Überlegungen
- Es wurde erneut bestätigt, dass bereits mit reinem HTML-Parsing ein erheblicher Teil der Webseiten zugänglich ist; bei großen Plattformen (z. B. GitHub) steckt aussagekräftiger Inhalt jedoch im JS und ist so nicht parsebar
- Als künftige Aufgabe wird die Untersuchung von Kosten und Verfahren für großskaliges Crawling auf Basis von JS-Rendering genannt
- Auch Datenanalysen (Metadaten der tatsächlich gesammelten Seiten, Verhältnis aktiver/inaktiver Seiten usw.) werden als Folgethema erwähnt
- In jüngster Zeit nimmt zudem aggressives Crawling in Kombination mit KI zu, während mit pay-per-crawl von Cloudflare neue Abwehrmechanismen entstehen – das Umfeld des Web-Crawlings verändert sich erneut
3 Kommentare
Großartig … Applaus, Applaus …
Interessant. Ich habe es mir gut angesehen, danke.
Beeindruckend … Ist das ein Kampf zwischen Speer und Schild? Haha