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-session-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 (
session-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-session-{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.
Key Takeaways
- Usa sempre proxy residenziali US per Walmart. I proxy datacenter vengono bloccati quasi istantaneamente da Akamai.
- Parsa
__NEXT_DATA__invece del DOM — è più stabile, più completo, e non richiede browser headless. - Distingui 1P da 3P usando il
sellerIdper evitare di confrontare prezzi di venditori diversi. - Usa sticky session per mantenere i cookie anti-bot risolti tra richieste consecutive.
- Limita a 100-120 richieste/ora per sessione con intervalli randomizzati per evitare rate limiting.
- Monitora il success rate e riduci la frequenza se scende sotto il 70%.
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.






