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
In questo articolo

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-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 _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-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:

  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.

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.

Domande frequenti

Si può fare scrape di Walmart senza proxy residenziali?

Praticamente no. Walmart usa Akamai Bot Manager e PerimeterX che bloccano gli IP datacenter quasi istantaneamente. I proxy residenziali US sono il minimo indispensabile per ottenere success rate sopra il 70%. Per risultati ottimali (90%+), usa sticky session residenziali o proxy mobile.

Cos'è __NEXT_DATA__ su Walmart e perché è meglio del DOM parsing?

__NEXT_DATA__ è un oggetto JSON che Next.js inietta nell'HTML della pagina. Contiene tutti i dati prodotto (prezzi, inventario, venditori, recensioni) in formato strutturato. È molto più stabile del DOM parsing perché le classi CSS cambiano frequentemente, mentre la struttura JSON cambia raramente. Inoltre include dati non visibili sulla pagina renderizzata.

Come distinguo i venditori 3P dal venditore diretto Walmart (1P)?

Nell'oggetto sellers dentro __NEXT_DATA__, il venditore 1P di Walmart ha sellerId 'F55CDC31AB754BB68FE0B39041159D63' e sellerName 'Walmart.com'. Tutti gli altri sellerId sono venditori terzi (3P). È importante filtrare per venditore 1P quando monitori il prezzo di vendita Walmart per non confonderlo con prezzi di marketplace.

Qual è il rate limit di Walmart per lo scraping?

Walmart non pubblica limiti ufficiali. Dal testing empirico: ~30-50 richieste/ora per IP residenziale senza sessione, e ~100-200 richieste/ora con sticky session e cookie validi. Oltre questi limiti si riceve HTTP 429 o challenge anti-bot. La strategia consigliata è 100-120 richieste/ora con intervalli randomizzati di 25-45 secondi.

Come gestisco i blocchi di PerimeterX su Walmart?

Se ricevi una pagina senza __NEXT_DATA__, sei stato challengeato. Le soluzioni sono: (1) ruota la sessione proxy cambiando l'identificativo session- nell'username, (2) aggiungi ritardi umani tra le richieste (3-8 secondi), (3) assicurati che gli header HTTP siano completi e coerenti, (4) usa proxy mobile per la massima reputation IP. Non tentare di risolvere CAPTCHA automaticamente — ruota la sessione.

Monitora prezzi e concorrenti senza essere bloccato

Proxy residenziali affidabili per i dati e-commerce. Registrati e inizia a raccogliere dati puliti.

Inizia ora
← Torna al Blog