Abrufen von Kontodaten mittels Scraping
Übersicht
Der Benutzer meldet sich eigenhändig im Playwright-Browser an; Sie finden die Datenquelle, die die Website selbst verwendet, und spielen sie innerhalb der Seite nach (Sitzungscookies + echte Header) und speichern die rohen Antworten auf der Festplatte. Niemals mit Anmeldeinformationen umgehen.
Wann verwenden
- „Holen Sie meine -Historie“, „Scrapen Sie meine Bestellungen/Reisen/Buchungen“, „Verwenden Sie dieses Scraper-Repository“
- Alte Scraper-Repositories (requests + Passwort/OTP-Login): Lesen Sie sie auf GitHub nur für Endpunktnamen und Selektoren. Führen Sie sie niemals aus – Bot-Prüfungen (HTTP 202 leerer Body, Captchas) unterbrechen sie, und sie verlangen das Passwort.
Zuerst offiziellen Export versuchen. Google Takeout, „Ihre Daten herunterladen“, DSGVO-/DPDP-Anfragen, öffentliche APIs, E-Mail-Rechnungen. Langsame Exporte dauern Tage – bitten Sie den Benutzer, gleich zu Beginn einen zu beantragen, scrapen Sie dann jetzt und gleichen Sie später anhand der ID ab.
Arbeitsablauf
- Ausgabeverzeichnis vor dem Schreiben zu .gitignore hinzufügen (
data/<dienst>/oder das entsprechende Projektverzeichnis); mitgit check-ignorebestätigen. Rohdaten enthalten Adressen, Telefonnummern, GPS-Koordinaten. - Navigieren, Login übergeben. Die Kontoseite öffnen; den Benutzer auffordern, Telefonnummer/Passwort/OTP im Browserfenster einzugeben und zu antworten, wenn er fertig ist. Niemals nach Codes im Chat fragen.
- Die Quelle ermitteln (
browser-snippets.md§1). Seite mit einem Listener neu laden und klassifizieren:- JSON XHR/fetch (am besten) – Listenendpunkt notieren, Paginierungsform (
next_url/Cursor,page_number, Offset = letzte ID,endOfList) und den Detailendpunkt (ein Element öffnen). - GraphQL – ein
POST /graphql; den vollständigen Request-Body aufzeichnen (operationName, query oder Hash der persisted query, Variablen). - Serverseitig gerendertes HTML – Daten kommen in der
document-Antwort; Listen- und Detailseiten in-page abrufen und mitDOMParserparsen; rohes HTML ebenfalls speichern, um erneut parsen zu können. - Aufgeteilte Listen – wenn die Liste durch einen Filter aufgeteilt ist (Jahres-Dropdown, Datumsbereich, Tabs wie archiviert/storniert/digital), jeden Filterwert auslesen und jeden einzeln paginieren.
- JSON XHR/fetch (am besten) – Listenendpunkt notieren, Paginierungsform (
- Authentifizierung. Zuerst
fetch(url, {credentials: 'include'})versuchen. Falls das 401/403/leer zurückgibt, die Nicht-Browser-Header aus einer Live-Anfrage kopieren (§2) – kopieren, niemals Werte raten. Nur die Header-Namen protokollieren; niemals Werte ausgeben oder speichern. - Im Hintergrund innerhalb der Seite paginieren (§2):
window.__x = {running, pages, error}, 1,5 s zwischen den Anfragen, Wiederholungsversuche, mit kurzen Evaluates abfragen. Jede Antwort: wenn es sich um eine Login-/Captcha-Seite handelt (URL enthält signin/captcha oder HTML statt erwartetem JSON),errorsetzen und stoppen – diese kommen oft als 200 zurück. - Vor dem Navigieren speichern (§3). Jedes
goto/Neuladen löschtwindow.browser_evaluatemit einem absolutenfilename-Pfad verwenden und ein Objekt zurückgeben (JSON.stringifydoppelt kodiert). ~5 MB pro Datei sind in Ordnung; darüber hinaus pro Batch aufteilen. - Signierte/nicht wiederholbare Anfragen (Request-Signatur-Header, Nonce-Bodies, persisted queries, die Bearbeitungen ablehnen): jede Detailseite besuchen und deren Antworten passiv aufzeichnen (§4).
- Erstellen + Verifizieren (siehe unten) mit einem Parser-Skript außerhalb des Datenverzeichnisses, dann eine Erkenntnisnotiz. Dem vorhandenen Layout des Repos folgen, falls vorhanden (z. B.
tools/<dienst>/build_db.py, Notizen neben den Daten); andernfalls nachfragen.
Nur lesend. Nur das wiedergeben, was die Seite selbst anfordert, um Daten anzuzeigen. GraphQL: nach Operation beurteilen (query/Get*/List*), niemals mutation. Niemals neu anordnen, stornieren, bewerten, archivieren, löschen.
Verifikation – alle durchführen
| Prüfung | Grund |
|---|---|
| Paginierung endete, weil der Cursor/die Liste erschöpft war, nicht wegen eines Fehlers oder Login-Weiterleitung | Stille Abschneidung |
| Nach Text wie „ältere Bestellungen in der App ansehen“ am Ende suchen | Web-Ansichten begrenzen den Verlauf (Swiggy: ~11 Monate) |
| Sich nicht allein auf den Zähl-Endpunkt oder -Header der Website verlassen | Blinkit sagte 40; die Liste hatte 83 |
| Komponenten summieren sich zum Gesamtbetrag pro Zeile (Artikel + Gebühren − Rabatte = bezahlt) | Erfasst Parsing-Abweichungen; wenn die Website Zeilen für alte Einträge weglässt, dies angeben – nicht erzwingen |
| Artikelpreise summieren sich zum Artikelgesamtbetrag | Verwechslung von Stückpreis und Zeilenendbetrag |
| Eindeutige IDs; exakte Duplikate entfernen | Exporte und Rechnungen listen doppelt |
| Querverweis mit einer anderen Quelle anhand der ID (Export, Rechnungen) | Belegt die Abdeckung |
| Jede Liste/jeder Tab/jeder Filter wurde abgedeckt (archiviert, storniert, andere Jahre, Unterdienste) | „Alle Bestellungen“ umfasst oft mehrere Listen |
Hinweise zum Parsen
- Server-gesteuerte UI-APIs (
snippets,widgets,widget_type,layout): Widgets der Reihe nach durchgehen, dietext-Felder jedes Widgets sammeln, anhand bekannter Labels zuordnen. Ein Ein-Text-Widget ist oft ein Label für das nächste – Labelnamen auf eine Whitelist setzen, sonst verschieben sich alle Felder. - Beträge können in Paise/Cent sein; „FREE“ = 0; führendes „-“ = Rabatt; Währungssymbole und indische Komma-Gruppierung entfernen. Währungen getrennt halten.
- Zeitstempel: Käufe → Zeitzone des Benutzers; Fahrten/Reisen → die Ortszeit des Datensatzes.
- Rohdateien datiert halten (
orders-raw-YYYY-MM-DD.json) neben der erstellten SQLite-Datenbank ablegen.
Git
Nur committen: die .gitignore-Zeile, das Parser-Skript, ggf. eine README-/Index-Zeile. Rohdateien und die SQLite bleiben lokal. Explizite Pfade stagen (niemals git add -A in einem verschmutzten Baum); nur auf Anfrage committen.
Häufige Fehler
- Eine lange synchrone Evaluate-Schleife – führt zu Timeout; im Hintergrund ausführen und abfragen.
- Bulk-Daten statt
filenamein den Chat zurückgeben. - Snippet-Listener bleiben hängen –
page.offverwenden, sonst stapeln sich Handler bei wiederholten Ausführungen. - Die Suche auf xhr/fetch auf einer HTML-gerenderten Seite beschränken und zu dem Schluss kommen, „keine Datenquelle“.