Come Fare Scrape di Walmart: Guida Pratica per Estrarre Dati Prodotto

Scopri come estrarre prezzi, inventario e dati venditore da Walmart gestendo Akamai e PerimeterX con proxy residenziali, parsando __NEXT_DATA__ e scheduler rate-limit aware.

Come Fare Scrape di Walmart: Guida Pratica per Estrarre Dati Prodotto

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 _abck che devono essere risolti con challenge crittografiche.
  • PerimeterX (ora HUMAN) — si attiva su pattern di navigazione sospetti. Inietta script _px3 che 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 ProxySuccess Rate su WalmartLatenza MediaNote
Datacenter5–15%50–100 msBloccato quasi subito da Akamai
Residenziale (rotazione per-request)70–85%800–1500 msIdeal per bulk scraping catalogo
Residenziale (sticky session)85–92%600–1200 msNecessario per completare challenge JS
Mobile90–97%1000–2500 msMassima 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, categoria
  • product.priceInfo — prezzo corrente, prezzo originale, currency
  • product.availability — status inventario (in stock, out of stock, limited)
  • product.sellers — lista venditori 3P con prezzo e spedizione
  • product.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 _abck risolti 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 è F55CDC31AB754BB68FE0B39041159D63 e il sellerName è 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'oggetto sellers dentro __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:

  1. Estrarre tutti i venditori dall'array sellers.
  2. Filtrare per is_walmart = True per ottenere il prezzo 1P.
  3. Registrare il prezzo del buy-box winner (primo nell'array) per capire chi ha la visibilità.
  4. 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:

  1. Partiziona gli SKU per priorità: prodotti top-selling ogni ora, long-tail ogni 6-12 ore.
  2. Usa sessioni diverse per categorie: questo distribuisce il rischio di blocco per categoria.
  3. Cache i risultati: se il prezzo non è cambiato, non rielaborare. Salva un hash dell'ultimo response.
  4. 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 nuovo session-* 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-US nel 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 sellerId per 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.

Pronto per iniziare?

Accedi a oltre 50M di IP residenziali in oltre 148 paesi con filtraggio AI.

Vedi i prezziProxy residenziali
← Torna al Blog