Perché Fare Scrape di Walmart È Diverso da Qualsiasi Altro E-Commerce
Se hai mai provato a fare scrape di Walmart, sai già il problema: le prime 50 richieste funzionano, poi improvvisamente ricevi 403 Forbidden o una pagina vuota con un challenge JavaScript. Walmart protegge il suo catalogo con una delle stack anti-bot più aggressive del retail mondiale — e questo cambia completamente l'approccio.
La scelta fondamentale è tra API endpoints strutturati (veloci, stabili, ma pesantemente sorvegliati) e pagine HTML renderizzate (più lente, ma con dati embeddati che bypassano gran parte della protezione). Per Walmart, la strategia vincente è un ibrido: usare l'HTML per estrarre il JSON nascosto in __NEXT_DATA__, con proxy residenziali per evitare il blocco. Vediamo come.
Struttura del Catalogo Walmart: URL, Pagine e Pattern
Prima di scrivere una riga di codice, devi capire come Walmart organizza il suo catalogo. I tre punti di ingresso principali sono:
Pagine Prodotto — /ip/{slug}/{itemId}
Ogni prodotto ha un URL nel formato https://www.walmart.com/ip/{slug}/{itemId}. Il itemId è un identificatore numerico univoco — è l'unica parte obbligatoria. Lo slug è solo SEO e può cambiare; l'itemId no. Esempio:
https://www.walmart.com/ip/Clorox-Disinfecting-Wipes/55469841
Se lo slug è errato ma l'itemId è corretto, Walmart reindirizza allo slug giusto. Questo significa che nei tuoi scraper puoi usare solo l'itemId per costruire URL validi.
Pagine Categoria
Le categorie seguono il pattern https://www.walmart.com/cp/{category-slug}/{categoryId}. Le sottocategorie nidificano sotto lo stesso pattern. Per enumerare prodotti in una categoria, puoi paginare con il query parameter ?page={n}.
Ricerca
L'endpoint di ricerca è https://www.walmart.com/search?q={query}&page={n}. I filtri disponibili includono sort, rating, price_range, brand, e availability. Per il price monitoring, il filtro availability=online è essenziale per escludere prodotti out-of-stock.
Lo Stack Anti-Bot di Walmart: Akamai + PerimeterX
Walmart utilizza due sistemi anti-bot in combinazione:
- Akamai Bot Manager — analizza fingerprint del browser, comportamento della sessione, e reputation dell'IP. Genera cookie
_abckche devono essere risolti con challenge crittografiche. - PerimeterX (ora HUMAN) — si attiva su pattern di navigazione sospetti. Inietta script
_px3che raccolgono segnali lato client. Se il punteggio supera la soglia, ricevi un CAPTCHA o un blocco silenzioso.
Perché i proxy residenziali sono obbligatori? Perché Akamai assegna un rischio altissimo agli IP datacenter. Un IP datacenter viene challengeato immediatamente; un IP residenziale statunitense passa il primo filtro. Questo non significa che i proxy residenziali siano sufficienti da soli — servono anche session management e header corretti — ma senza di essi non inizi nemmeno la partita.
| Tipo Proxy | Success Rate su Walmart | Latenza Media | Note |
|---|---|---|---|
| Datacenter | 5–15% | 50–100 ms | Bloccato quasi subito da Akamai |
| Residenziale (rotazione per-request) | 70–85% | 800–1500 ms | Ideal per bulk scraping catalogo |
| Residenziale (sticky session) | 85–92% | 600–1200 ms | Necessario per completare challenge JS |
| Mobile | 90–97% | 1000–2500 ms | Massima reputation IP, costo più alto |
Regola pratica: usa proxy residenziali con sticky session per navigare le pagine prodotto (devi risolvere la challenge JS nella stessa sessione). Usa rotazione per-request per enumerare categorie e ricerche dove non ti serve mantenere stato.
Il Tesoro Nascosto: __NEXT_DATA__
Walmart usa Next.js per il rendering. Next.js inietta nell'HTML un tag <script id="__NEXT_DATA__"> contenente tutti i dati della pagina in formato JSON — prezzi, inventario, varianti, venditore, recensioni, tutto. Questo è il percorso di parsing più semplice e più stabile.
Perché è meglio che parsare il DOM?
- Struttura stabile: il JSON cambia molto meno frequentemente delle classi CSS o degli XPath.
- Dati completi: include informazioni che non sono nemmeno visibili nella pagina renderizzata (es. costi di spedizione per ogni venditore, quantità inventario).
- Nessun rendering necessario: se ottieni l'HTML (con curl o requests), hai già tutto. Non ti serve Selenium o Playwright per questo specifico dato.
Il selettore per estrarlo è semplice:
# CSS Selector
script#__NEXT_DATA__
# XPath
//script[@id='__NEXT_DATA__']/text()
Struttura del JSON
Il JSON segue la struttura tipica di Next.js. Il percorso ai dati prodotto è:
props.pageProps.initialData.data
Sotto questo path troverai oggetti come:
product— nome, brand, description, itemId, categoriaproduct.priceInfo— prezzo corrente, prezzo originale, currencyproduct.availability— status inventario (in stock, out of stock, limited)product.sellers— lista venditori 3P con prezzo e spedizioneproduct.reviewStatistics— rating medio, numero recensioni, distribuzione per stelle
Ecco un esempio troncato di risposta:
{
"product": {
"itemId": "55469841",
"name": "Clorox Disinfecting Wipes, 75 ct",
"brand": "Clorox",
"priceInfo": {
"currentPrice": { "price": 8.97, "currencyUnit": "USD" },
"originalPrice": { "price": 11.97 }
},
"availability": "IN_STOCK",
"sellers": [
{ "sellerId": "F55CDC31AB754BB68FE0B39041159D63",
"sellerName": "Walmart.com",
"price": 8.97 }
],
"reviewStatistics": {
"averageOverallRating": "4.5",
"totalReviewCount": 12847
}
}
}
Esempio Python: Fetch e Parse di __NEXT_DATA__
Questo script usa ProxyHat con sticky session residenziale, fetcha una pagina prodotto Walmart, estrae il JSON da __NEXT_DATA__, e restituisce un dizionario strutturato con prezzo, inventario, rating e venditori.
import requests
import json
import re
from urllib.parse import quote
PROXY_USER = "your_user-country-US-sid-walmart1"
PROXY_PASS = "your_password"
PROXY_URL = f"http://{PROXY_USER}:{PROXY_PASS}@gate.proxyhat.com:8080"
PROXIES = {"http": PROXY_URL, "https": PROXY_URL}
HEADERS = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/125.0.0.0 Safari/537.36"
),
"Accept": "text/html,application/xhtml+xml",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
}
def fetch_product(item_id: str) -> dict:
url = f"https://www.walmart.com/ip/product/{item_id}"
resp = requests.get(url, headers=HEADERS, proxies=PROXIES, timeout=30)
resp.raise_for_status()
match = re.search(
r'<script id="__NEXT_DATA__"[^>]*>(.*?)</script>',
resp.text,
re.DOTALL
)
if not match:
raise ValueError("__NEXT_DATA__ non trovato nella pagina")
data = json.loads(match.group(1))
product = data["props"]["pageProps"]["initialData"]["data"]["product"]
return {
"item_id": product["itemId"],
"name": product.get("name", ""),
"brand": product.get("brand", ""),
"current_price": product["priceInfo"]["currentPrice"]["price"],
"original_price": product["priceInfo"]
.get("originalPrice", {})
.get("price"),
"availability": product.get("availability", "UNKNOWN"),
"avg_rating": float(
product.get("reviewStatistics", {})
.get("averageOverallRating", 0)
),
"review_count": int(
product.get("reviewStatistics", {})
.get("totalReviewCount", 0)
),
"sellers": [
{
"name": s.get("sellerName", ""),
"price": s.get("price"),
"is_walmart": s.get("sellerId", "")
== "F55CDC31AB754BB68FE0B39041159D63",
}
for s in product.get("sellers", [])
],
}
# Esempio di utilizzo
result = fetch_product("55469841")
print(json.dumps(result, indent=2))
Punti chiave del codice:
- La sessione sticky (
sid-walmart1) mantiene i cookie_abckrisolti attraverso richieste successive. - Il geotargeting
country-USè obbligatorio — Walmart serve contenuti diversi per IP non-US e alcuni prezzi sono nascosti. - Se
__NEXT_DATA__non è presente, sei stato challengeato. Controlla il response body per script PerimeterX.
Walmart Marketplace: Venditori 3P vs Catalogo 1P
Walmart opera sia come rivenditore diretto (1P) sia come marketplace per venditori terzi (3P). Per i team di intelligence retail, questa distinzione è critica.
Come Identificare 1P vs 3P
- 1P (Walmart come venditore): il
sellerIdèF55CDC31AB754BB68FE0B39041159D63e ilsellerNameèWalmart.com. Il prezzo è il prezzo di vendita Walmart. - 3P (venditore terzo): qualsiasi altro
sellerId. Il nome del venditore appare nella pagina prodotto e nell'oggettosellersdentro__NEXT_DATA__.
Perché Importa per il Price Monitoring
Se monitori solo il prezzo "principale" sulla pagina prodotto, potresti stare leggendo il prezzo di un venditore 3P che sta facendo buy-box takeover — non il prezzo di Walmart. Per il competitive intelligence CPG, devi:
- Estrarre tutti i venditori dall'array
sellers. - Filtrare per
is_walmart = Trueper ottenere il prezzo 1P. - Registrare il prezzo del buy-box winner (primo nell'array) per capire chi ha la visibilità.
- Tracciare i prezzi 3P per identificare violazioni MAP (Minimum Advertised Price).
Ecco come estrarre e classificare i venditori:
def classify_sellers(product_data: dict) -> dict:
"""Classifica venditori 1P e 3P da dati prodotto."""
sellers = product_data.get("sellers", [])
walmart_seller_id = "F55CDC31AB754BB68FE0B39041159D63"
first_party = [s for s in sellers if s["sellerId"] == walmart_seller_id]
third_party = [s for s in sellers if s["sellerId"] != walmart_seller_id]
buy_box = sellers[0] if sellers else None
buy_box_is_1p = (
buy_box["sellerId"] == walmart_seller_id if buy_box else False
)
return {
"total_sellers": len(sellers),
"first_party_price": first_party[0]["price"] if first_party else None,
"third_party_min_price": min(s["price"] for s in third_party) if third_party else None,
"third_party_max_price": max(s["price"] for s in third_party) if third_party else None,
"buy_box_seller": buy_box["name"] if buy_box else None,
"buy_box_is_1p": buy_box_is_1p,
"buy_box_price": buy_box["price"] if buy_box else None,
}
Scheduler Rate-Limit Aware
Walmart non pubblica i suoi rate limit, ma dal testing empirico, ecco le soglie osservate:
- IP residenziale, senza sessione: ~30–50 richieste/ora prima del soft-block (challenge JS).
- IP residenziale, con sticky session e cookie validi: ~100–200 richieste/ora prima del rate limit HTTP 429.
- IP datacenter: 0–5 richieste prima del blocco permanente.
Il tuo scheduler deve rispettare questi limiti. Ecco un pattern robusto:
import time
import random
import logging
from datetime import datetime, timedelta
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("walmart_scheduler")
# Limite conservativo: 120 richieste/ora per sessione residenziale
MAX_REQUESTS_PER_HOUR = 120
REQUEST_INTERVAL_MIN = 25 # secondi
REQUEST_INTERVAL_MAX = 45 # secondi
RETRY_BACKOFF_BASE = 60 # secondi, raddoppia ad ogni retry
MAX_RETRIES = 3
def respectful_request(url: str, session_id: str, attempt: int = 0) -> dict:
"""Esegue una richiesta con backoff e rate limiting."""
proxy_user = f"your_user-country-US-sid-{session_id}"
proxy_url = f"http://{proxy_user}:your_password@gate.proxyhat.com:8080"
proxies = {"http": proxy_url, "https": proxy_url}
try:
resp = requests.get(url, headers=HEADERS, proxies=proxies, timeout=30)
if resp.status_code == 429:
wait = RETRY_BACKOFF_BASE * (2 ** attempt) + random.uniform(5, 30)
logger.warning(f"Rate limited. Aspetto {wait:.0f}s (tentativo {attempt+1})")
time.sleep(wait)
return respectful_request(url, session_id, attempt + 1)
if resp.status_code == 403:
logger.error("Bloccato da anti-bot. Rotazione sessione necessaria.")
new_session = f"{session_id}_{int(time.time())}"
return respectful_request(url, new_session, 0)
resp.raise_for_status()
return {"status": "ok", "html": resp.text}
except requests.RequestException as e:
if attempt >= MAX_RETRIES:
logger.error(f"Max retries raggiunto per {url}: {e}")
return {"status": "failed", "error": str(e)}
wait = RETRY_BACKOFF_BASE * (2 ** attempt)
time.sleep(wait)
return respectful_request(url, session_id, attempt + 1)
def scrape_product_list(item_ids: list[str], batch_size: int = 100):
"""Scrape una lista di prodotti con rate limiting."""
results = []
for i, item_id in enumerate(item_ids):
url = f"https://www.walmart.com/ip/product/{item_id}"
session_id = f"batch{ i // batch_size }"
result = respectful_request(url, session_id)
if result["status"] == "ok":
# Parse __NEXT_DATA__ qui...
results.append(result)
# Intervallo randomizzato tra richieste
wait = random.uniform(REQUEST_INTERVAL_MIN, REQUEST_INTERVAL_MAX)
logger.info(f"Prossima richiesta tra {wait:.1f}s")
time.sleep(wait)
return results
Strategie di Scheduling per Scala
Per monitorare migliaia di prodotti giornalmente:
- Partiziona gli SKU per priorità: prodotti top-selling ogni ora, long-tail ogni 6-12 ore.
- Usa sessioni diverse per categorie: questo distribuisce il rischio di blocco per categoria.
- Cache i risultati: se il prezzo non è cambiato, non rielaborare. Salva un hash dell'ultimo response.
- Monitora il success rate: se scende sotto il 70%, riduci la frequenza o cambia pool di proxy.
Gestione degli Errori e Pattern Anti-Detection
Anche con proxy residenziali, Walmart può challengearti. Ecco come gestire i casi più comuni:
- Pagina vuota senza
__NEXT_DATA__: Sei stato challengeato. La risposta contiene script PerimeterX. Soluzione: ruota la sessione proxy e riprova con un nuovosession-*nell'username. - Redirect a pagina di verifica: Akamai ha rilevato comportamento bot. Soluzione: aggiungi un ritardo umano (3-8 secondi) tra le richieste e assicurati che gli header siano completi e coerenti.
- Dati prodotto parziali: A volte Walmart omette il prezzo o l'inventario per IP non-US. Soluzione: usa sempre
country-USnel tuo proxy username. - CAPTCHA image-based: Significa che la tua sessione è stata marcata come sospetta. Soluzione: scarta la sessione e inizia una nuova con IP residenziale diverso.
Considerazioni Legali ed Etiche
Prima di procedere con qualsiasi attività di scraping su Walmart, considera:
- Il robots.txt di Walmart vieta esplicitamente lo scraping su molti path. Verifica sempre le policy correnti.
- I Termini di Servizio di walmart.com proibiscono l'accesso automatizzato senza autorizzazione.
- Il CPG price monitoring per prodotti che vendi direttamente su Walmart può avere giustificazioni competitive, ma consulta sempre il tuo legale.
- Rispetta i limiti di frequenza — non sovraccaricare i server di Walmart è sia etico che pragmatico (riduce i blocchi).
- Se operi nell'UE, considera le implicazioni GDPR se raccogli dati di recensioni che contengono informazioni personali.
Se hai bisogno di proxy residenziali ottimizzati per il retail scraping, ProxyHat offre piani flessibili con geotargeting US e sticky session che funzionano con Walmart. Per casi d'uso specifici come il price monitoring o il web scraping, consulta le nostre guide dedicate.






