Versteckte Preise durch Rückentwicklung von Hintergrund-API-Aufrufen finden
Finden Sie Preise, die im HTML nicht angezeigt werden, indem Sie DevTools zur Überwachung der XHR-, REST- und GraphQL-Aufrufe hinter der Seite nutzen und anschließend diese Anfragen direkt erneut senden.
Zweiter Teil der Serie „Wie Scraping tatsächlich funktioniert“
In dem vorherigen Artikel stießen wir auf ein Problem: Manchmal ist der benötigte Preis einfach nirgends in dem HTML enthalten, das von requests abgerufen wird. Er ist weder in JSON-LD eingebettet noch in einer Meta-Tag – er ist einfach nicht auffindbar. Das bedeutet jedoch nicht, dass man feststeckt – in der Regel wird diese Datenmenge erst nach der initialen Ladezeit über einen separaten Hintergrundaufruf geladen. Unser Ziel ist es, diesen Aufruf aufzuspüren und selbst durchzuführen.
Schritt 1: Die Registerkarte „Netzwerk“ öffnen
Wir gehen anhand eines konkreten Beispiels vor: Eine Produktseite auf der Website von Target, target.com/p/dyson-ball-animal-3-upright-vacuum/-/A-85269291 (das dient nur zur Veranschaulichung – der gleiche Ansatz funktioniert auf praktisch jeder Produktseite).
- Laden Sie eine beliebige Produktseite auf.
F12). Schritt 2: Finden Sie die Anfrage, die Sie wirklich benötigen
Gehen Sie die Liste Schritt für Schritt durch, klicken Sie auf jede Eintragung und prüfen Sie das Response- oder Preview-Panel auf der rechten Seite. Sie suchen nach einem JSON-Payload, der den Preis, die Anzahl der Bewertungen oder jeden anderen Wert enthält, nach dem Sie suchen.
Es gibt einen schnelleren Weg: Nutzen Sie das Suchfeld in Form einer Lupe im Reiter „Netzwerk“ und geben Sie einen Wert ein, der auf der Seite selbst sichtbar ist. Im Beispiel „Target“ führt das Eingeben von „449.99“ direkt zu einem POST-Aufruf mit dem Namen deferred_enrichment/modules. Sein Antwortkörper ist ein umfangreiches JSON-Objekt, und darin finden Sie an einer Stelle "formatted_current_price":"$449.99.
Schritt 3: Führen Sie den Aufruf selbst aus
Wählen Sie diese Anfrage aus und notieren Sie sich die vollständige URL. In diesem Fall handelt es sich um eine POST-Anfrage, doch alles, was benötigt wird, ist bereits im Abfragesatz enthalten:
https://www.target.com/cdui_orchestrations/v1/pages/pdp/deferred_enrichment/modules
?tcin=85269291
&key=9f36aeafbe60771e321a7cc95a78140772ab3e96
&channel=WEB
&device_type=DESKTOP
So sieht es aus, wenn man es in Python nachstellt:
import requests
url = "https://www.target.com/cdui_orchestrations/v1/pages/pdp/deferred_enrichment/modules"
params = {
"tcin": "85269291", # the product ID, from the page URL
"key": "9f36aeafbe60771e321a7cc95a78140772ab3e96",
"channel": "WEB",
"device_type": "DESKTOP",
}
resp = requests.post(url, params=params, headers={"User-Agent": "Mozilla/5.0"})
data = resp.json()
price = data["modules"][0]["module_data"]["data"]["product"]["price"]["formatted_current_price"]
print(price) # $449.99
Das ist der ganze Trick. Keine Parsing-Bibliotheken, keine CSS-Selektoren – Sie senden einfach dieselbe Abfrage, die die eigene JavaScript-Logik der Seite im Hintergrund sendet.
Tipp: In den DevTools klicken Sie mit der rechten Maustaste auf die Anfrage und wählen Kopieren → Als cURL kopieren. Dadurch erhalten Sie die genauen Header und Cookies, die der Browser mitgeschickt hat, die Sie in ein Tool wie Postman übertragen oder in Python umwandeln können, um herauszufinden, welche Teile tatsächlich erforderlich sind.
Schritt 4: Wenn die API GraphQL verwendet
Manche Websites leiten alles über GraphQL statt über eine traditionelle REST-Struktur weiter. Dieses Muster erkennt man daran, dass jede Anfrage auf dem gleichen Endpunkt landet (häufig /graphql) und die eigentliche Anfrage im Anfragenbody statt in der URL enthalten ist:
{
"operationName": "GetPrice",
"variables": { "productId": "12345" },
"query": "query GetPrice($productId: ID!) { product(id: $productId) { price } }"
}
Mehrere Websites gehen noch einen Schritt weiter und verwenden persistierte Abfragen, bei denen anstelle des vollständigen Abfragetextes nur ein kurzer Hash gesendet wird, der diesen ersetzt:
/graphql/GetPrice/a1b2c3d4e5f6...?variables={"productId":"12345"}
Dieser Hash kann nicht selbst ermittelt werden – man muss ihn direkt aus den DevTools übernehmen. Da er mit der Frontend-Kompilierung der Website verknüpft ist, kann ein Update auf deren Seite den Hash ändern und damit heimlich Ihre Skripte beeinträchtigen. Das ist eine echte, anhaltende Kostenfaktor bei der Verwendung dieser Methode.
Schritt 5: Überprüfen, ob der API-Schlüssel tatsächlich privat ist
Betrachten Sie erneut die Ziel-URL aus Schritt 3 – achten Sie auf key=9f36aeafbe60771e321a7cc95a78140772ab3e96, der offen in der Abfragesuche steht. Ist das etwas, zu dem Sie eigentlich keinen Zugang haben sollten?
In der Regel nicht. Die schnellste Möglichkeit zur Überprüfung besteht darin, Seitenquellcode ansehen (Ctrl+U) zu wählen und anschließend mit Ctrl+F nach apiKey zu suchen. Sie werden denselben Schlüssel in Klartext innerhalb eines Konfigurationsblocks finden, den Target jedem einzelnen Besucher sendet.
Falls ein Wert bereits in der eigenen Markup-Struktur der Seite enthalten ist – ohne dass etwas umgangen oder eine Authentifizierung erforderlich ist – war er von Anfang an nicht vertraulich. Es handelt sich dabei einfach um einen öffentlichen Schlüssel, der im Browser ausgeführt werden soll. (Manchmal befindet er sich stattdessen in einer separaten .js-Datei; falls „Seite anzeigen“ nichts anzeigt, versuchen Sie das „Quellen“-Panel der DevTools und suchen mit Ctrl+Shift+F).
Eine gute Praxis: Bevor Sie sich die Mühe machen, einen Endpunkt zu reverse-engineern, überprüfen Sie, ob das Unternehmen bereits eine dokumentierte öffentliche oder Partner-API anbietet. Solche APIs sind in der Regel weitaus stabiler als solche, die man durch Herumprobieren in den DevTools entdeckt.
Schritt 6: Wenn die Daten überhaupt nicht vom Händler stammen
Mannchmal geht die Anfrage, nach der man sucht, nicht einmal auf die eigene Domain des Händlers – stattdessen verweist sie auf einen Dienst Dritter wie bazaarvoice.com oder judge.me. Das ist ein Zeichen dafür, dass der Händler keine eigene Bewertungsinfrastruktur betreibt; er nutzt vielmehr das System eines Anbieters und fügt einfach dessen Widget auf die Seite ein.
Diese von Anbietern betriebenen Widget-APIs sind in der Regel weniger streng geschützt als die Hauptseite eines Händlers, da sie dafür konzipiert sind, in Tausenden unzusammenhängender Shops eingebettet zu werden, anstatt gezielt Scrapper zu erkennen und zu blockieren.
Was in diesem Artikel absichtlich weggelassen wird
Falls Sie alle oben genannten Schritte befolgt haben und die angeforderte Datenmenge dennoch mit 403, einem leeren Inhalt oder etwas ähnlichem wie einer Sicherheitsprüfung statt mit tatsächlichen Daten zurückkommt, handelt es sich dabei nicht um einen Fehler in Ihrem Code. Das bedeutet, die Website hat erkannt, dass es sich nicht um eine normale Browser-Sitzung handelt. Diese Herausforderung ist so erheblich, dass sie eine eigene Erörterung verdient – genau dort setzt der nächste Artikel an.
Glossar
Network-Tab – das DevTools-Fenster, das jede von einer Seite gesendete Anfrage sowie das Gesendete und Empfangene protokolliert.
XHR / Fetch – die beiden Mechanismen, die JavaScript verwendet, um Hintergrundanfragen ohne vollständige Seitenneuladung durchzuführen; der Network-Tab kann so gefiltert werden, dass nur diese angezeigt werden.
REST API – ein gängiges API-Muster, bei dem jede Ressource ihre eigene spezielle URL hat, wie zum Beispiel /products/123/price.
GraphQL – ein API-Stil, der auf einem einzigen Endpunkt basiert, wobei im Anfragekörper genau angegeben wird, welche Daten angefordert werden.
Persisted query – ein GraphQL-Shortcut, bei dem der vollständige Abfragesatz durch einen kompakten Hash ersetzt wird, den der Server bereits erkennt.
API-Schlüssel – ein Wert, der einer Anfrage zugeordnet wird, um sie zu identifizieren; einige sind tatsächlich privat, doch viele sind öffentliche, clientseitige Schlüssel, die in jedem Browser sichtbar sein sollen.
Kopieren als cURL – eine Option in DevTools, die eine aufgezeichnete Anfrage in einen ausführbaren Befehl für die Kommandozeile umwandelt, was nützlich ist, um deren genaue Header und Cookies zu überprüfen.
Drittanbieter-Widget – eine Funktion wie ein Rezensionsbereich, die eine Website nicht selbst entwickelt, sondern stattdessen aus dem Produkt eines anderen Unternehmens einbettet.