Cómo Scrapear Walmart: Guía Práctica para Extraer Datos de Producto

Guía pragmática para scrapear datos de productos de Walmart: evita Akamai/PerimeterX, extrae __NEXT_DATA__, diferencia 1P vs 3P, y programa requests sin bloqueos usando proxies residenciales.

Cómo Scrapear Walmart: Guía Práctica para Extraer Datos de Producto

El dilema API vs. HTML en Walmart

Si tu equipo de inteligencia retail necesita datos de Walmart, te enfrentas a una decisión inmediata: ¿usar la API interna de Walmart o parsear el HTML? La API de Walmart (developer.walmart.com) ofrece endpoints estables para precios e inventario, pero requiere aprobación, tiene límites de tasa estrictos y no cubre datos de vendedores Marketplace. Por eso, la mayoría de equipos de CPG y retail intel terminan scrapeando.

El problema: Walmart protege su sitio con una de las stacks anti-bot más agresivas de la web. Scrapear Walmart sin la estrategia correcta significa captchas interminables, bloques de IP y datos incompletos. Esta guía te muestra cómo hacerlo de forma pragmática: qué extraer, cómo evadir los bloqueos, y cómo estructurar tu pipeline.

Estructura del catálogo de Walmart

Walmart organiza su catálogo en tres tipos de páginas principales. Entender la URL y la estructura de cada una es el primer paso para cualquier scraper.

Páginas de producto: /ip/{slug}/{itemId}

Cada producto tiene una URL canónica con el formato:

https://www.walmart.com/ip/Apple-AirPods-Pro-2nd-Gen/1752698499

El itemId (numérico al final) es el identificador estable. El slug del título puede cambiar, pero el itemId no. Siempre extrae y almacena el itemId como clave primaria. Puedes incluso omitir el slug y cargar directamente https://www.walmart.com/ip/1752698499 — Walmart redirige a la URL canónica.

Páginas de categoría

Las categorías usan el formato:

https://www.walmart.com/cp/electronics/3944
https://www.walmart.com/cp/food/976759

Cada página de categoría carga un JSON de productos paginados. Los enlaces de paginación usan el parámetro ?page=2. Las subcategorías se anidan: /cp/{nombre}/{categoryId}.

Páginas de búsqueda

Las búsquedas usan:

https://www.walmart.com/search?q=airpods+pro&sort=price_low

Parámetros útiles: sort (price_low, price_high, best_seller, rating), facet para filtros, y page para paginación. Cada página de búsqueda devuelve ~40 productos.

El muro anti-bot: Akamai + PerimeterX

Walmart despliega una defensa en dos capas que detecta y bloquea la mayoría de scrapers:

  • Akamai Bot Manager: fingerprinting a nivel de red (TLS fingerprint, orden de cabeceras HTTP, JA3 hash). Detecta librerías como requests o axios por su TLS fingerprint. También analiza patrones de tráfico por IP — demasiados requests desde un rango /24 levanta alertas.
  • PerimeterX (ahora HUMAN): fingerprinting del navegador. Inyecta scripts JavaScript que generan un token (_px3) basado en comportamiento del mouse, tiempo de renderizado, y capacidades del navegador. Sin este token, el servidor devuelve una página de captcha o un bloque 403.

Por qué necesitas proxies residenciales: Las IPs de datacenter son bloqueadas casi inmediatamente por Akamai. Walmart mantiene listas de ASN de datacenter (OVH, DigitalOcean, AWS, etc.) y desafía o bloquea esas IPs al primer request. Los proxies residenciales rotativos usan IPs de ISPs reales, lo que hace que cada request parezca de un usuario doméstico legítimo.

Umbral empírico: Walmart permite ~30-80 requests por IP residencial antes de mostrar captcha. Con IPs de datacenter, el bloqueo ocurre en 1-5 requests. Rotar IPs residenciales cada 20-30 requests mantiene tasas de éxito superiores al 95%.

Comparativa de tipos de proxy para Walmart

Tipo de proxyTasa de éxito (est.)Latencia promedioCosto por GBRecomendación
Datacenter5-15%Baja (~100ms)$0.50-1.00No viable para Walmart
Residencial rotativo90-97%Media (~800ms)$3-8Ideal para scraping masivo
Residencial sticky92-98%Media (~800ms)$3-8Ideal para sesiones largas
Móvil95-99%Alta (~1.5s)$8-15Máxima confianza, alto costo

El camino fácil: __NEXT_DATA__

Walmart renderiza sus páginas con Next.js, lo que significa que inyecta todo el estado de la aplicación en un tag <script id="__NEXT_DATA__"> dentro del HTML. Este JSON contiene todos los datos del producto: precio, inventario, ratings, vendedor, variantes, descripción — sin necesidad de parsear el DOM.

Selector CSS para encontrarlo:

script#__NEXT_DATA__

XPath equivalente:

//script[@id='__NEXT_DATA__']

Este es el JSON más completo y confiable de la página. Los selectores CSS del DOM (como [data-automation-id="product-price"]) cambian con frecuencia y son frágiles. __NEXT_DATA__ es estable porque es la fuente de datos interna de la aplicación.

Estructura del JSON

La ruta principal dentro del JSON es:

props.pageProps.initialData.data.product

Los campos clave que encontrarás:

  • priceInfo.currentPrice.price — precio actual
  • priceInfo.wasPrice — precio anterior (si hay descuento)
  • availabilityStatus — estado de inventario
  • averageRating — rating promedio
  • numberOfReviews — número de reseñas
  • sellerId — ID del vendedor
  • sellerName — nombre del vendedor
  • productType — tipo de producto

Ejemplo en Python: fetch + parse de __NEXT_DATA__

Aquí tienes un script completo que usa ProxyHat para obtener una página de producto y extraer los datos clave:

import requests
import json
from urllib.parse import quote

PROXY_URL = "http://user-country-US:PASSWORD@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,application/xml;q=0.9,*/*;q=0.8",
    "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/{item_id}"
    resp = requests.get(url, headers=HEADERS, proxies=PROXIES, timeout=30)
    resp.raise_for_status()
    return resp.text

def extract_next_data(html: str) -> dict:
    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html, "html.parser")
    script = soup.find("script", id="__NEXT_DATA__")
    if not script:
        raise ValueError("__NEXT_DATA__ no encontrado")
    return json.loads(script.string)

def parse_product(next_data: dict) -> dict:
    product = next_data["props"]["pageProps"]["initialData"]["data"]["product"]
    price_info = product.get("priceInfo", {})
    
    current = price_info.get("currentPrice", {}).get("price", None)
    was = price_info.get("wasPrice", {}).get("price", None) if price_info.get("wasPrice") else None
    
    return {
        "item_id": product.get("id"),
        "name": product.get("name"),
        "current_price": current,
        "was_price": was,
        "availability": product.get("availabilityStatus"),
        "avg_rating": product.get("averageRating"),
        "num_reviews": product.get("numberOfReviews"),
        "seller_id": product.get("sellerId"),
        "seller_name": product.get("sellerName"),
        "product_type": product.get("productType"),
    }

# Ejemplo de uso
if __name__ == "__main__":
    html = fetch_product("1752698499")
    next_data = extract_next_data(html)
    result = parse_product(next_data)
    print(json.dumps(result, indent=2, ensure_ascii=False))

Respuesta de ejemplo (truncada)

{
  "item_id": "1752698499",
  "name": "Apple AirPods Pro 2nd Generation",
  "current_price": 189.00,
  "was_price": 249.00,
  "availability": "IN_STOCK",
  "avg_rating": 4.5,
  "num_reviews": 12847,
  "seller_id": "0",
  "seller_name": "Walmart.com",
  "product_type": "1P"
}

Marketplace (3P) vs. catálogo 1P

Walmart opera dos modelos de venta simultáneamente, y los datos se estructuran de forma diferente para cada uno:

Productos 1P (Walmart como vendedor)

  • sellerId = "0" o "F55CBD31AB464BB8B3C59010B21C10E6" (Walmart.com)
  • Datos de inventario más confiables — availabilityStatus refleja almacenes de Walmart
  • Precios directamente de la base de datos de Walmart
  • Un solo precio por itemId

Productos 3P (vendedores Marketplace)

  • sellerId es un hash alfanumérico único por vendedor
  • Múltiples ofertas para el mismo itemId — necesitas iterar offers dentro del JSON
  • Los campos de inventario pueden ser menos confiables
  • Precios y envío varían por vendedor

Para extraer todas las ofertas de un producto 3P, busca la clave offers dentro de product:

def parse_all_offers(product: dict) -> list:
    offers = product.get("offers", [])
    results = []
    for offer in offers:
        results.append({
            "seller_id": offer.get("sellerId"),
            "seller_name": offer.get("sellerName"),
            "price": offer.get("priceInfo", {}).get("currentPrice", {}).get("price"),
            "availability": offer.get("availabilityStatus"),
            "shipping": offer.get("shippingOptions", [{}])[0].get("shippingPrice"),
        })
    return results

Dato clave: cuando un producto tiene tanto oferta 1P como 3P, Walmart muestra la oferta 1P por defecto en la página principal. Las ofertas 3P aparecen en la sección "More sellers" o dentro de offers en __NEXT_DATA__.

Programación con conciencia de rate limits

Walmart no publica sus rate limits, pero la experiencia empírica muestra estos umbrales:

  • Por IP residencial: ~30-80 requests antes de captcha. Varía por reputación del IP.
  • Por IP de datacenter: 1-5 requests antes del bloqueo.
  • Por sesión de navegador: PerimeterX rastrea el token de sesión. Rotar IP sin rotar cookie no funciona.
  • Velocidad de requests: Más de 2 requests/segundo desde una IP dispara alertas de Akamai.

Estrategia de scheduling recomendada

import time
import random
import requests

PROXY_URL = "http://user-country-US:PASSWORD@gate.proxyhat.com:8080"
PROXIES = {"http": PROXY_URL, "https": PROXY_URL}

def scrape_item_list(item_ids: list, max_retries: int = 3):
    results = []
    for i, item_id in enumerate(item_ids):
        # Rotar sesión cada 25 requests para evitar captcha
        session_id = f"session-walmart-{i // 25}"
        proxy = f"http://user-country-US-session-{session_id}:PASSWORD@gate.proxyhat.com:8080"
        proxies = {"http": proxy, "https": proxy}
        
        for attempt in range(max_retries):
            try:
                url = f"https://www.walmart.com/ip/{item_id}"
                resp = requests.get(url, headers=HEADERS, proxies=proxies, timeout=30)
                
                if resp.status_code == 403:
                    print(f"  Bloqueado en {item_id}, rotando sesión...")
                    time.sleep(random.uniform(5, 15))
                    continue
                
                resp.raise_for_status()
                next_data = extract_next_data(resp.text)
                product = parse_product(next_data)
                results.append(product)
                break
            except Exception as e:
                print(f"  Error en {item_id}: {e}")
                time.sleep(random.uniform(3, 8))
        
        # Delay aleatorio entre requests: 1.5-3.5 segundos
        delay = random.uniform(1.5, 3.5)
        time.sleep(delay)
        
        print(f"  [{i+1}/{len(item_ids)}] {item_id} OK")
    
    return results

Buenas prácticas para evitar bloqueos

  • Rotación de sesiones sticky: Usa sesiones de 20-30 requests antes de rotar. Las sesiones nuevas cada request levantan más sospechas que sesiones estables.
  • Delays aleatorios: Nunca uses un intervalo fijo. Agrega jitter entre 1.5 y 4 segundos.
  • Headers realistas: Incluye Accept, Accept-Language, Accept-Encoding. No uses el User-Agent por defecto de requests.
  • Geo-targeting: Si scrapeas Walmart US, usa IPs de EE.UU. Las IPs internacionales pueden redirigir a walmart.ca o mostrar contenido diferente. Con ProxyHat: user-country-US.
  • Manejo de errores: Si recibes un 403, no reintentes inmediatamente. Espera 5-15 segundos y rota la sesión.
  • Limita la concurrencia: No más de 2-3 threads por IP residencial. Si necesitas más throughput, usa más IPs, no más threads.

Cuándo usar la API de Walmart vs. scraping

La API oficial de Walmart tiene sentido si:

  • Solo necesitas precios e inventario de productos 1P
  • Tu volumen es bajo (<100K requests/mes)
  • Puedes esperar la aprobación de Walmart (días a semanas)
  • No necesitas datos de vendedores 3P

El scraping es necesario si:

  • Necesitas datos de vendedores Marketplace (precios, disponibilidad por vendedor)
  • Necesitas cobertura masiva (millones de SKUs)
  • Quieres datos de búsqueda (ranking, posición, filtros)
  • La API no ofrece los campos que necesitas
  • Necesitas velocidad — no puedes esperar aprobación

Para la mayoría de equipos de inteligencia retail, la respuesta es ambos: la API para datos estables de 1P, y scraping para cobertura 3P y datos de búsqueda que la API no proporciona.

Consideraciones legales y éticas

Antes de implementar cualquier scraper de Walmart, considera:

  • robots.txt: Walmart permite crawling de páginas de producto pero bloquea /search y /browse para ciertos bots. Revisa https://www.walmart.com/robots.txt regularmente.
  • Términos de servicio: Los ToS de Walmart prohíben scraping. Si tienes una relación comercial con Walmart, esto puede tener consecuencias. Evalúa el riesgo legal con tu equipo.
  • GDPR/CCPA: No extraigas datos personales (reseñas con nombres, ubicaciones). Limítate a datos de producto.
  • Rate limits razonables: No satures la infraestructura de Walmart. Un scraper bien diseñado no debería impactar la experiencia de otros usuarios.

Puntos clave

  • __NEXT_DATA__ es tu mejor amigo: No parsees el DOM. Extrae el JSON del tag script#__NEXT_DATA__ — es más completo, estable y fácil de procesar.
  • Proxies residenciales son obligatorios: Las IPs de datacenter son bloqueadas por Akamai en 1-5 requests. Usa proxies residenciales rotativos con sesiones sticky.
  • Diferencia 1P de 3P: Los productos 1P tienen sellerId = "0". Los productos 3P requieren iterar offers para obtener todos los vendedores.
  • Rotación inteligente: Rota sesiones cada 20-30 requests, no cada request. Usa delays aleatorios de 1.5-4 segundos.
  • El itemId es la clave: Usa el ID numérico como identificador estable, no el slug del título.
  • Geo-targeting importa: Usa IPs de EE.UU. para Walmart.com. IPs de otros países pueden mostrar contenido diferente.

Si necesitas proxies residenciales confiables para scrapear Walmart, ProxyHat ofrece rotación por país y sesión con tasas de éxito superiores al 95% contra Akamai. Configura tu primer proxy en minutos con gate.proxyhat.com:8080.

¿Listo para empezar?

Accede a más de 50M de IPs residenciales en más de 148 países con filtrado impulsado por IA.

Ver preciosProxies residenciales
← Volver al Blog