Warum Walmart-Daten so schwer zu beschaffen sind
Wenn Sie als CPG- oder Retail-Intelligence-Team Walmart product data im Maßstab benötigen, stoßen Sie schnell auf drei Probleme: die URL-Struktur ist undurchsichtig, Akamai und PerimeterX blockieren jeden naiven Request, und die produktivste Datenquelle – ein verstecktes JSON-Objekt – ist nicht dokumentiert. Dieser Guide geht das Problem pragmatisch an: API-Endpunkte zuerst, HTML nur als Fallback, mit konkreten Selektoren und echten Rate-Limit-Schwellenwerten.
Walmart-Katalogstruktur verstehen
Walmart organisiert sein Sortiment über drei Einstiegspunkte:
- Artikelseiten:
/ip/{slug}/{itemId}– Beispiel:/ip/Apple-AirPods-Pro/719073468. DieitemId(numerisch, 8–12 Stellen) ist der kanonische Schlüssel; der Slug ist optional und wird serverseitig ignoriert. - Kategorieseiten:
/cp/{categorySlug}– z. B./cp/electronics/3944. Pagination erfolgt über?page=2, liefert aber nur ~40 Items pro Seite. - Suchergebnisse:
/search?q={query}– paginiert, liefert bis zu ~1.000 Ergebnisse pro Suchbegriff.
Die itemId ist das wichtigste Identifikationsmerkmal. Alles – Preis, Bestand, Ratings, Seller – lässt sich darüber referenzieren. Sammeln Sie IDs aus Kategorieseiten oder Suchergebnissen, dann rufen Sie die Artikelseiten parallel ab.
URL-Muster auf einen Blick
| Einstiegspunkt | URL-Muster | Datenumfang |
|---|---|---|
| Artikelseite | /ip/{slug}/{itemId} | Vollständig: Preis, Bestand, Ratings, Seller, Beschreibung|
| Kategorieseite | /cp/{slug}/{catId} | Teilmenge: Name, Preis, Thumbnail, itemId |
| Suchergebnis | /search?q={query} | Teilmenge + Relevanz-Ranking |
| Search API (intern) | /api/search?query=... | JSON, aber stark geschützt |
Das Anti-Bot-Problem: Akamai + PerimeterX
Walmart setzt einen dualen Bot-Schutz ein:
- Akamai Bot Manager – fingerprintbasiert (Canvas, WebGL, AudioContext). Prüft TLS-Fingerabdruck, HTTP/2-Frames und Header-Reihenfolge.
- PerimeterX (jetzt HUMAN) – verhaltensbasiert. Analysiert Mausbewegung, Scroll-Tiefe, Klick-Intervalle über ein clientseitiges JavaScript-Skript (
_pxVid-Cookie).
Die Konsequenz: Ein einfacher requests.get()-Aufruf mit Datacenter-IP wird nach 3–5 Requests mit einem 403 oder einer CAPTCHA-Seite beantwortet. Selbst Headless-Browser (Puppeteer/Playwright) ohne PerimeterX-Evading-Plugins überleben selten mehr als 20 Requests von derselben IP.
Warum Residential Proxys erforderlich sind
Akamai klassifiziert IP-Adressen nach ASN-Typ. Datacenter-ASNs (OVH, DigitalOcean, AWS) erhalten sofort einen Bot-Score ≥ 90. Residential-IPs starten bei ~30 und werden nur bei auffälligem Verhalten hochgestuft. Mobile Proxys sind noch besser – Walmart erwartet signifikanten Traffic von Mobilgeräten.
Für einen stabilen Scraper benötigen Sie:
- Residential Proxys mit Geo-Targeting auf US-Städte
- IP-Rotation pro Request (nicht pro Session)
- Einen User-Agent, der zur Proxy-Kategorie passt (Mobile-UA für Mobile-Proxys)
Konfiguration mit ProxyHat:
# HTTP-Proxy mit US-Geo-Targeting und pro-Request-Rotation
export HTTP_PROXY=http://user-country-US:PASSWORD@gate.proxyhat.com:8080
# curl-Beispiel
curl -x "$HTTP_PROXY" \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
-H "Accept: text/html,application/xhtml+xml" \
"https://www.walmart.com/ip/Apple-AirPods-Pro/719073468"Sticky Sessions (für Multi-Page-Flows wie Cart-Additionen) setzen Sie über das Session-Flag im Username:
# Sticky Session – dieselbe IP für 10 Minuten
http://user-country-US-session-myssid123:PASSWORD@gate.proxyhat.com:8080Der einfachste Parse-Pfad: __NEXT_DATA__
Walmart rendert seine Artikelseiten mit Next.js. Das Framework injiziert den gesamten Seitenzustand als JSON in ein <script>-Tag mit der ID __NEXT_DATA__. Das bedeutet: Ein einziger HTML-Request liefert alle Produktdaten – kein Rendering, kein API-Reverse-Engineering.
Der Selektor:
// CSS-Selektor
script#__NEXT_DATA__
// XPath
//script[@id='__NEXT_DATA__']Das JSON enthält verschachtelt:
props.pageProps.initialData.data.product– KernproduktdatenpriceInfo– aktueller Preis, Listenpreis, WährungavailabilityState– Bestandsstatus (IN_STOCK,OUT_OF_STOCK,LIMITED)reviews– Durchschnittsrating, AnzahlsellerId,sellerName– Marketplace-VerkäufervariantCategoriesMap– Varianten (Größen, Farben)
Vorteile gegenüber der internen API:
- Kein zusätzlicher API-Call nötig – ein Request reicht
- Kein Auth-Token erforderlich
- Struktur ist stabiler als CSS-Klassen
- Funktioniert auch, wenn Walmart API-Endpunkte ändert
Python-Implementierung: Abruf + Parsing
Vollständiges Beispiel für den Abruf einer Walmart-Artikelseite über ProxyHat und die Extraktion aller relevanten Felder aus __NEXT_DATA__:
import requests
import json
from urllib.parse import urljoin
PROXY = "http://user-country-US:PASSWORD@gate.proxyhat.com:8080"
PROXIES = {"http": PROXY, "https": PROXY}
HEADERS = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/124.0.0.0 Safari/537.36"
),
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9",
"Accept-Language": "en-US,en;q=0.9",
}
def fetch_product(item_id: str) -> dict:
url = f"https://www.walmart.com/ip/{item_id}"
resp = requests.get(url, headers=HEADERS, proxies=PROXIES, timeout=15)
resp.raise_for_status()
from bs4 import BeautifulSoup
soup = BeautifulSoup(resp.text, "html.parser")
script_tag = soup.find("script", id="__NEXT_DATA__")
if not script_tag:
raise ValueError("__NEXT_DATA__ nicht gefunden – ggf. CAPTCHA-Seite")
next_data = json.loads(script_tag.string)
product = next_data["props"]["pageProps"]["initialData"]["data"]["product"]
return product
def extract_fields(product: dict) -> dict:
price_info = product.get("priceInfo", {})
current_price = price_info.get("currentPrice", {})
return {
"item_id": product.get("productId"),
"name": product.get("name"),
"brand": product.get("brand", ""),
"price": current_price.get("price", None),
"currency": current_price.get("currencyUnit", "USD"),
"list_price": price_info.get("originalPrice", {}).get("price"),
"availability": product.get("availabilityState", "UNKNOWN"),
"rating": product.get("averageRating"),
"review_count": product.get("numberOfReviews"),
"seller_id": product.get("sellerId"),
"seller_name": product.get("sellerName"),
"is_marketplace": product.get("sellerId") != "0",
"category_path": product.get("categoryPath", ""),
}
# Nutzung
product_data = fetch_product("719073468")
result = extract_fields(product_data)
print(json.dumps(result, indent=2))Beispielausgabe (gekürzt):
{
"item_id": "719073468",
"name": "Apple AirPods Pro",
"brand": "Apple",
"price": 189.00,
"currency": "USD",
"list_price": 249.00,
"availability": "IN_STOCK",
"rating": 4.5,
"review_count": 14238,
"seller_id": "0",
"seller_name": "Walmart.com",
"is_marketplace": false,
"category_path": "Electronics/Headphones"
}Walmart Marketplace: 3P-Seller vs. 1P-Katalog
Walmart ist ein hybrider Marktplatz. Etwa 60–70 % des Sortiments wird von Drittanbietern (3P) gelistet. Das beeinflusst Ihre Datenstrategie massiv:
Wie Sie 1P und 3P unterscheiden
- sellerId = "0" → Walmart selbst (1P). Preis ist der Walmart-Verkaufspreis, Bestand stammt aus Walmart-Warenhäusern.
- sellerId ≠ "0" → Marketplace-Verkäufer (3P). Preis und Verfügbarkeit werden vom Verkäufer gesteuert.
In der __NEXT_DATA__-Struktur finden Sie unter product.offerGroups alle verfügbaren Angebote zu einem Artikel. Jedes Angebot hat eine eigene sellerId, sellerName, Preis und Versandbedingungen.
Was das für CPG-Teams bedeutet
- Preismonitoring: Verfolgen Sie den günstigsten 3P-Angebotpreis vs. den 1P-Listenpreis – das ist Ihre MAP-Compliance-Kennzahl.
- Buybox-Analyse: Der Preis auf der Artikelseite ist der Buybox-Gewinner, nicht zwingend der günstigste. Extrahieren Sie alle
offerGroups, um die echte Preisspanne zu sehen. - Bestandsverfügbarkeit: 1P-Artikel nutzen Walmart-Fulfillment. 3P-Seller können längere Lieferzeiten haben – relevant für Out-of-Stock-Analysen.
def extract_offers(product: dict) -> list:
"""Alle Angebote (1P + 3P) extrahieren."""
offers = []
offer_groups = product.get("offerGroups", [])
for group in offer_groups:
for offer in group.get("offers", []):
seller_id = offer.get("sellerId", "unknown")
offers.append({
"seller_id": seller_id,
"seller_name": offer.get("sellerName", ""),
"price": offer.get("priceInfo", {}).get("currentPrice", {}).get("price"),
"is_1p": seller_id == "0",
"shipping_price": offer.get("shippingPrice", 0),
"delivery_date": offer.get("fulfillment", {}).get("deliveryDate", ""),
})
return offers
Rate-Limit-awarees Scheduling
Walmart hat keine öffentliche Rate-Limit-Dokumentation, aber empirische Tests zeigen klare Schwellen:
- ~30 Requests/Minute/IP – sichere Obergrenze für Residential Proxys
- ~100 Requests/Minute/IP – kurzfristig möglich, aber nach 5–10 Minuten folgt ein Temp-Ban (403)
- ~200+ Requests/Minute/IP – sofortiger Block
Strategie: Verteilung über Rotating Proxys + Token-Bucket
Die effizienteste Methode ist die Kombination aus IP-Rotation und Rate-Limiting auf Scraper-Seite:
import time
import random
from collections import defaultdict
# Token-Bucket: max 25 Requests pro Minute pro IP-Slot
RATE_LIMIT = 25 # requests/min (sicherer Puffer)
request_timestamps = defaultdict(list)
def rate_limited_fetch(url: str, session_id: str = None) -> requests.Response:
"""Fetch mit automatischem Rate-Limiting und Proxy-Rotation."""
# Neue Session = neue IP
if session_id is None:
session_id = f"sess-{random.randint(10000,99999)}"
proxy = f"http://user-country-US-session-{session_id}:PASSWORD@gate.proxyhat.com:8080"
proxies = {"http": proxy, "https": proxy}
# Rate-Limit prüfen
now = time.time()
timestamps = request_timestamps[session_id]
# Alte Timestamps entfernen
request_timestamps[session_id] = [t for t in timestamps if now - t < 60]
if len(request_timestamps[session_id]) >= RATE_LIMIT:
# Session wechseln statt zu warten
session_id = f"sess-{random.randint(10000,99999)}"
proxy = f"http://user-country-US-session-{session_id}:PASSWORD@gate.proxyhat.com:8080"
proxies = {"http": proxy, "https": proxy}
request_timestamps[session_id].append(time.time())
# Zufällige Verzögerung (1–4 Sekunden)
time.sleep(random.uniform(1.0, 4.0))
return requests.get(url, headers=HEADERS, proxies=proxies, timeout=15)
# Batch-Verarbeitung
def scrape_item_ids(item_ids: list[str]) -> list[dict]:
results = []
for i, item_id in enumerate(item_ids):
try:
product = fetch_product(item_id)
fields = extract_fields(product)
results.append(fields)
print(f"[{i+1}/{len(item_ids)}] {item_id} ✓")
except Exception as e:
print(f"[{i+1}/{len(item_ids)}] {item_id} ✗ {e}")
# Bei 403: Session resetten
if "403" in str(e):
time.sleep(random.uniform(30, 60))
return resultsWeitere Best Practices für Rate-Limiting
- Exponentielles Backoff: Bei 403/429 die Wartezeit verdoppeln – startend bei 30s, max 10min.
- Zeitfenster streuen: Scraping-Jobs auf 6–8 Stunden verteilen statt in 30 Minuten erledigen.
- Stichproben statt Vollzugriff: Für Preismonitoring reichen oft 2–3 Abrufe pro Tag pro Artikel.
- Retry-Logik mit Session-Rotation: Bei Fehler neue ProxyHat-Session starten statt dieselbe IP zu erzwingen.
Vergleich: Scraping-Ansätze für Walmart
| Methode | Datenqualität | Aufwand | Stabilität | Rate-Limit-Risiko |
|---|---|---|---|---|
__NEXT_DATA__ aus HTML | Hoch – alle Produktdaten | Niedrig – 1 Request | Mittel – Next.js-Struktur kann sich ändern | Mittel |
| Interne Search-API | Hoch – strukturiertes JSON | Hoch – Token/Headers nötig | Niedrig – Endpunkte ändern sich oft | Hoch |
| CSS/XPath-Scraping | Mittel – nur sichtbare Daten | Mittel | Niedrig – Klassen ändern sich häufig | |
| Headless Browser (Playwright) | Hoch – inkl. JS-Rendering | Hoch – langsam, ressourcenintensiv | Mittel | Hoch |
| Offizielle Walmart API | Hoch – stabil | Niedrig – aber Zugang schwer zu bekommen | Hoch | Niedrig |
Für die meisten CPG- und Retail-Intelligence-Anwendungen ist __NEXT_DATA__ der beste Kompromiss. Die interne API liefert zwar saubereres JSON, aber die Authentifizierung ist instabil und die Rate-Limits sind aggressiver.
Key Takeaways
- Immer
__NEXT_DATA__zuerst parsen – ein Request liefert alle Produktdaten ohne Rendering.- Residential Proxys sind Pflicht – Akamai blockiert Datacenter-IPs nach 3–5 Requests. ProxyHat mit
country-US-Flag liefert US-Residential-IPs.- Pro-Request-Rotation nutzen – neue Session pro Request, nicht pro Batch.
- Max 25 Requests/Minute/IP – sicherer Schwellwert. Bei 403 sofort Session wechseln.
- 3P vs. 1P unterscheiden –
sellerId == "0"ist Walmart selbst, alles andere ist Marketplace.- Stichproben statt Vollzugriff – 2–3 Abrufe/Tag/Artikel reichen für Preismonitoring.
Wenn Sie Walmart product data im Maßstab erfassen müssen, kombinieren Sie die __NEXT_DATA__-Technik mit ProxyHat Residential Proxys und einem sauberen Rate-Limit-Scheduler. Das Ergebnis: stabile 95 %+ Erfolgsquoten bei vertretbarem Aufwand.
Mehr zur Proxy-Konfiguration finden Sie in unserem Guide zu Proxy-Rotationsstrategien und den Web-Scraping-Use-Cases. Die aktuellen Tarife für Residential Proxys sehen Sie auf unserer Preisseite.






