Ein Dutzend Lucerne-Eier in einem Safeway in Washington, D.C. wurde auf Instacart für 3,99 $, 4,28 $, 4,59 $, 4,69 $ und 4,79 $ angeboten. Dieselbe Filiale, derselbe Moment, unterschiedliche Kunden. Welche dieser fünf Zahlen gehört in dein Preis-Panel?
Die Herausforderung
Im Lebensmittelbereich ist Preis-Intelligence nicht mehr einfach nur "Produktseite abrufen, Preis parsen". Ein Lebensmittelpreis gehört zu einer Filiale, oft zu einer Lieferzone und immer häufiger zu der Person, die ihn gerade ansieht.
Beginnen wir mit der Filiale. Scrapflys Leitfaden zum Preisvergleich bei Lebensmitteln beziffert eine Gallone Milch in einem Walmart auf 3,98 $ und in einem anderen, 20 Meilen entfernt, auf 4,29 $. Zudem wird davor gewarnt, dass eine Session ohne Filialstandort Standarddaten erhält, die möglicherweise mit keinem echten Geschäft übereinstimmen. ScrapeInsights Leitfaden für Lebensmittellieferungen 2026 stellt auf der nächsten Ebene dasselbe fest: 3,99 $ in einer Lieferzone, 4,49 $ ein paar Meilen weiter. Bright Datas ShopGrok-Fallstudie beschreibt, wie die Preise im australischen Einzelhandel im Laufe der fast vierjährigen Geschäftstätigkeit des Unternehmens postleitzahlenabhängig wurden.
Dann der Kunde. Im Dezember 2025 führten die Groundwork Collaborative, Consumer Reports und More Perfect Union Live-Tests mit 437 Kunden in vier Städten durch, die zur selben Zeit identische Instacart-Warenkörbe in denselben Filialen füllten. 74 % der Artikel erschienen zu mehr als einem Preis. Wo die Preise voneinander abwichen, lag die Differenz zwischen dem niedrigsten und dem höchsten Preis im Schnitt bei 13 %, und ganze Warenkörbe variierten um etwa 7 %.
Zusammengefasst führt das zu dem Fehler, der Lebensmitteldaten teuer macht. Verlierst du den Filialkontext, schlägt nichts fehl. Die Website gibt keinen Fehler zurück. Sie liefert einen vollkommen validen Preis für eine Filiale, nach der du nicht gefragt hast, mit einer SKU, einer Zahl und einem Zeitstempel, die jeden deiner Null-Checks bestehen.
Wir haben bereits über den Fall geschrieben, bei dem eine Blockierung wie ein Datenpunkt aussieht. Dieser Fall hier ist schwerer zu erkennen, da die Seite tatsächlich eine Preisseite ist. Nur eben nicht deine.
Die Filiale in der Response verifizieren
Die meisten Scraper senden die Filialauswahl (ein Cookie, ein Query-Parameter, ein Header) und vertrauen darauf. Der robustere Ansatz besteht darin, die Filiale über die Response verifizieren zu lassen. Wenn die Seite die Filiale nennt, für die sie gerendert wurde, etwa über eine Filial-ID in den eingebetteten Daten oder einen Filialnamen im Abhol-Banner, wird dieser Marker zu deiner Erfolgsdefinition. Bei FourA ist das ein einziger Proxy Finder-Aufruf:
import requests
r = requests.post(
"https://api.foura.ai/api/proxy",
headers={"X-API-Key": "pk_live_..."},
json={
"maxTries": 6,
"request": {
"method": "GET",
"url": "https://grocer.example/product/0001234",
"headers": [["Cookie", "store=1234"]],
"validate": {
"status": {"accept": [200]},
"data": {
"accept": ["\"storeId\":\"1234\""],
"fail": ["Just a moment", "Access Denied"]
}
}
}
}
).json()
if "error" in r:
report = r.get("attemptReport", {})
print(report.get("summary", r["error"]))
if report.get("contentRejected", 0) > report.get("defense", 0) + report.get("noResponse", 0):
print("the site answered for another store: check the store selection")
else:
page, exit_id = r["data"], r["proxy"]
Drei Details in diesem Request sind leicht zu übersehen.
accept ist erfolgreich, sobald einer der Strings auf der Seite erscheint. Setzt du einen Preismarker neben den Filialmarker, geht eine Seite der falschen Filiale durch, da sie ebenfalls einen Preis enthält. Belasse den Filialmarker allein in accept und setze die Challenge-Strings in fail.
Dein eigener Cookie-Header ändert das Verhalten von Retries. Wenn eine Website den von uns bereitgestellten Browser ablehnt, wechselt Proxy Finder beim nächsten Versuch normalerweise zu einer anderen Browser-Familie. Nicht jedoch, wenn dein Cookie angehängt ist. Eine Session ist an die Signatur gebunden, mit der sie erstellt wurde. Ein Request mit eigenem Cookie behält daher den ursprünglichen Browser bei. Wenn die Website einer Kette bei Browsern wählerisch ist, wähle explizit einen über browser profiles aus, anstatt dich auf die Rotation zu verlassen.
Und ein fehlgeschlagener Task zeigt dir den genauen Fehlertyp an. Jede fehlgeschlagene Proxy Finder-Response enthält einen attemptReport. defense zählt Antworten, bei denen ein Bot-Check erkannt wurde, noResponse zählt Exits, die die Website nie erreicht haben, und contentRejected zählt Seiten, die als HTTP 200 ohne Bot-Check zurückkamen und nur durch deine Content-Regel verworfen wurden. Für einen Grocery-Collector bedeutet dieser letzte Zähler etwas Konkretes: Die Website hat geantwortet, nur nicht für deine Filiale. Mehr Exits lösen das nicht. Der attempt report guide behandelt die anderen Zähler.
Käuferprofil fixieren oder Streuung messen
Die Instacart-Ergebnisse führen eine zweite Variable ein, und es gibt zwei saubere Wege, damit umzugehen.
Für ein Preis-Panel solltest du die Datenerfassung jeder Filiale auf einer Identität halten. Spiele den erfolgreichen Exit (r["proxy"], eine opake ID) über Single mit demselben Cookie erneut ab und speichere diese ID sowie den Response-Header X-FourA-Request-Id zu jeder Zeile. Wenn ein Preis springt, kannst du eine Änderung in der Filiale von einem Session-Wechsel unterscheiden.
Für die Preisforschung machst du bewusst das Gegenteil. Frage dieselbe SKU in derselben Filiale über mehrere unabhängige Sessions ab und erfasse die Verteilung, nicht nur die erste Antwort. Ein Panel, das einen einzelnen Wert meldet, wo Käufer fünf verschiedene sehen, ist nicht präzise. Es hat nur Glück.
Ergebnisse
Ein Beispiel: Eine regionale Kette vergleicht 120 Filialen von Mitbewerbern anhand von 2.500 SKUs, täglich aktualisiert (illustratives Szenario basierend auf Branchen-Benchmarks). Das sind 300.000 Seitenaufrufe pro Tag, und in jeder beliebigen Nacht werden einige davon für die falsche Filiale zurückkommen: Ein Cookie-Format hat sich geändert, eine Filiale wurde geschlossen oder eine Website bevorzugt nun den IP-Standort gegenüber dem Cookie.
- Seiten mit falschem Store schlagen direkt beim Erfassen fehl. Sie werden nie zu Datenzeilen, daher ist eine Preisänderung im Panel auch eine echte Änderung in diesem Store.
- Fehler kommen vorsortiert an. Ein hoher Wert für
contentRejectedgeht an das Team für die Store-Auswahl, ein hoher Wert fürdefenseist ein Zugriffsproblem, ein hoher Wert fürnoResponsebetrifft die Exits. Drei Verantwortliche, drei Lösungen, und niemand muss den ganzen Vormittag nach der Ursache suchen. - Jede Zeile ist rückverfolgbar. Eine Exit-ID und eine Request-ID pro Beobachtung machen aus der Frage "Ist dieser Spike real?" ein einfaches Lookup.
- Die Preisspanne wird zu einer konkreten Zahl. Für SKUs, die du über mehrere Sessions hinweg erfasst, kannst du die tatsächliche Spanne angeben, die Kunden sehen, statt nur einen einzelnen Messwert.
Wo das Ganze an Grenzen stößt: Mitglieder- und Treuepreise liegen hinter Login-Schranken, und A/B-Tests für eingeloggte Kunden tauchen in anonymen Sessions gar nicht auf. Diese Daten zu erfassen, ist erst eine rechtliche Frage zu den AGB, bevor es ein Engineering-Problem wird. Zudem ist ein Store-Marker nur so gut wie seine Definition. Lies ihn direkt aus dem Page Source aus, nicht aus dem Cache, und prüfe ihn nach jedem Redesign der Kette erneut.
Key Takeaway
Früher war ein Lebensmittelpreis eine Eigenschaft des Produkts. Heute ist er eine Eigenschaft aus Produkt, Store und Kunde. Ein Collector, der nur das Produkt erfasst, liefert schlicht Rauschen in einem sauberen Schema.
Wenn sich personalisierte Preise weiter durchsetzen, ändert sich die Kernfrage an Lebensmitteldaten: von "Was kostet das?" hin zu "Was kostet es hier, und wie groß ist die Spanne?" Die vertrauenswürdigen Collector sind daher nicht jene mit den meisten Exits. Es sind die, die für jeden Request belegen können, welchem Store er galt.