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
En este artículo

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.

Preguntas frecuentes

¿Es legal scrapear Walmart?

Scrapear datos públicos de productos (precios, disponibilidad, ratings) generalmente se considera legal en EE.UU. bajo la doctrina de acceso público. Sin embargo, los Términos de Servicio de Walmart prohíben el scraping, lo que podría constituir una violación contractual. No extraigas datos personales (reseñas con nombres). Consulta con tu equipo legal antes de proyectos a gran escala.

¿Por qué Walmart me bloquea con captcha o error 403?

Walmart usa Akamai Bot Manager y PerimeterX (HUMAN) para detectar tráfico automatizado. Las causas más comunes son: usar IPs de datacenter (bloqueadas casi de inmediato), no rotar sesiones, enviar requests demasiado rápido, o usar librerías HTTP con TLS fingerprints conocidos. La solución: usa proxies residenciales rotativos, sesiones sticky de 20-30 requests, y delays aleatorios.

¿Qué es __NEXT_DATA__ y por qué debería usarlo?

__NEXT_DATA__ es un objeto JSON que Next.js inyecta en el HTML de cada página. Contiene todos los datos del producto (precio, inventario, ratings, vendedor) en formato estructurado. Es mucho más estable y completo que parsear el DOM con selectores CSS, que cambian con cada rediseño de la página.

¿Cómo diferencio productos 1P de vendedores 3P en Walmart?

En __NEXT_DATA__, los productos 1P (vendidos por Walmart) tienen sellerId = "0" o el hash de Walmart. Los productos 3P tienen un sellerId único alfanumérico. Para ver todas las ofertas de un producto 3P, busca la clave "offers" dentro del objeto product — contiene un array con cada vendedor, su precio y disponibilidad.

¿Cuántos requests puedo hacer antes de que Walmart me bloquee?

Con proxies residenciales, puedes hacer entre 30 y 80 requests por IP antes de encontrar un captcha. Con IPs de datacenter, el bloqueo ocurre en 1-5 requests. La recomendación es rotar la sesión (IP + cookies) cada 20-30 requests y mantener delays aleatorios de 1.5 a 4 segundos entre requests.

Monitorea precios y competidores sin que te bloqueen

Proxies residenciales fiables para datos de e-commerce. Regístrate y empieza a obtener datos limpios.

Empezar
← Volver al Blog