API ou HTML? A escolha que define sua estratégia de scraping no Walmart
Se você está lendo este artigo, provavelmente já tentou raspar dados do Walmart e bateu de frente com um bloqueio Akamai. A primeira pergunta que toda equipe de inteligência de varejo deve responder é: vou consumir endpoints de API ou parsear o HTML renderizado?
A resposta curta para o Walmart: HTML com extração do JSON embutido em __NEXT_DATA__ é o caminho mais confiável. O Walmart não oferece uma API pública para catálogo, e os endpoints internos que alimentam o front-end são protegidos por tokens de sessão vinculados ao navegador. Tentar chamar essas APIs diretamente exige engenharia reversa constante — um jogo de gato-e-rato que consome mais recursos do que vale.
A abordagem pragmática: carregue a página via proxy residencial, extraia o bloco <script id="__NEXT_DATA__"> do HTML e parseie o JSON. Você obtém exatamente os mesmos dados que a API interna serviria, sem precisar reverse-engineerar headers dinâmicos.
Estrutura do catálogo do Walmart — URLs que você precisa conhecer
O Walmart organiza seu catálogo em três superfícies principais. Cada uma tem um padrão de URL previsível:
Páginas de produto (item pages)
Formato: https://www.walmart.com/ip/{slug}/{itemId}
- slug — texto SEO do nome do produto (pode ser qualquer coisa, o servidor ignora)
- itemId — identificador numérico único; é o único campo obrigatório
Exemplo real: https://www.walmart.com/ip/Apple-AirPods-Pro-2nd-Generation/1752637026
O itemId é a chave primária. Se você trocar o slug por texto aleatório, a página carrega normalmente. Isso significa que você pode construir URLs diretamente a partir de uma lista de IDs.
Páginas de categoria
Formato: https://www.walmart.com/c/{categorySlug}
Categorias de topo como electronics, home, toys têm páginas que listam subcategorias e produtos em destaque. São úteis para descoberta de IDs, mas a paginação via infinite scroll é carregada por chamadas AJAX que exigem sessões autenticadas.
Páginas de busca
Formato: https://www.walmart.com/search?q={query}&sort={sortType}&page={pageNum}
Parâmetros úteis:
sort=price_low,sort=price_high,sort=best_seller,sort=ratingpage=1,page=2… — paginação simplesaffinityOverride=default— remove personalização de resultados
Akamai + PerimeterX: por que você precisa de proxies residenciais
O Walmart emprega uma stack anti-bot em duas camadas:
| Camada | Tecnologia | O que detecta | Consequência |
|---|---|---|---|
| Edge / CDN | Akamai Bot Manager | Assinatura de TLS (JA3), comportamento de conexão, IPs de datacenter conhecidos | HTTP 403 sem corpo, ou challenge page |
| Aplicação | PerimeterX (agora HUMAN) | Fingerprint de navegador, mouse/scroll events, inconsistências de JS | Captcha interativo ou bloco silencioso |
Na prática, isso significa que:
- IPs de datacenter são bloqueados na borda — você nem chega ao PerimeterX. O Akamai rejeita ranges de IPs de provedores como AWS, GCP, DigitalOcean antes mesmo de avaliar o request.
- Proxies de datacenter rotativos funcionam por minutos — até o Akamai catalogar o range. Depois, taxa de sucesso cai para <5%.
- Proxies residenciais com rotação por request são o único caminho viável para scraping em escala. Cada request sai de um IP diferente associado a um ISP residencial real.
Regra prática: se você está raspando mais de ~200 páginas/hora no Walmart, proxies residenciais não são opcionais — são infraestrutura.
Para sessões que exigem persistência (como adicionar ao carrinho ou verificar estoque por CEP), use sessões sticky com proxies residenciais, mantendo o mesmo IP por 10–30 minutos.
__NEXT_DATA__: o caminho mais fácil para extrair dados de produto
O Walmart usa Next.js no front-end. O framework injeta um bloco <script id="__NEXT_DATA__" type="application/json"> no HTML com todo o estado da página serializado como JSON. Isso inclui:
- Preço atual e histórico recente
- Status de estoque (in-stock, out-of-stock, limited)
- Avaliações (rating médio, contagem de reviews)
- Dados do vendedor (Walmart 1P ou Marketplace 3P)
- Opções de envio e estimativas de entrega
- Variações (tamanhos, cores, modelos)
O seletor CSS para localizar o bloco:
script#__NEXT_DATA__
O XPath equivalente:
//script[@id='__NEXT_DATA__']/text()
Exemplo truncado da estrutura do JSON:
{
"props": {
"pageProps": {
"initialData": {
"data": {
"product": {
"id": "1752637026",
"name": "Apple AirPods Pro 2nd Generation",
"priceInfo": {
"currentPrice": { "price": 189.00, "currencyUnit": "USD" },
"priceRange": "189.00"
},
"availabilityStatus": "IN_STOCK",
"averageRating": 4.6,
"numberOfReviews": 15234,
"sellerId": "0",
"sellerName": "Walmart.com",
"shipMethods": [...],
"variants": [...]
}
}
}
}
}
}
Compare isso com parsear o HTML renderizado manualmente — você teria que lidar com classes CSS que mudam semanalmente, seletores frágeis e dados espalhados por diferentes componentes React. Com __NEXT_DATA__, você acessa a fonte estruturada diretamente.
Python: buscando e parseando __NEXT_DATA__ com proxies residenciais
Abaixo, um script completo que faz request via proxy residencial ProxyHat, extrai e parseia o __NEXT_DATA__ e retorna um dicionário limpo com os campos mais relevantes.
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/126.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, session_id: str = None) -> dict:
"""Busca página de produto e extrai __NEXT_DATA__."""
url = f"https://www.walmart.com/ip/product/{item_id}"
# Usa sessão sticky se fornecida, senão rotação por request
if session_id:
proxy = f"http://user-country-US-session-{session_id}:PASSWORD@gate.proxyhat.com:8080"
proxies = {"http": proxy, "https": proxy}
else:
proxies = PROXIES
resp = requests.get(url, headers=HEADERS, proxies=proxies, timeout=30)
resp.raise_for_status()
from bs4 import BeautifulSoup
soup = BeautifulSoup(resp.text, "html.parser")
script_tag = soup.find("script", id="__NEXT_DATA__")
if not script_tag:
raise ValueError("__NEXT_DATA__ não encontrado na página")
data = json.loads(script_tag.string)
return data
def parse_product(next_data: dict) -> dict:
"""Extrai campos-chave do JSON __NEXT_DATA__."""
try:
product = next_data["props"]["pageProps"]["initialData"]["data"]["product"]
except KeyError:
raise ValueError("Estrutura inesperada em __NEXT_DATA__")
price_info = product.get("priceInfo", {})
current = price_info.get("currentPrice", {})
return {
"item_id": product.get("id"),
"name": product.get("name"),
"price": current.get("price"),
"currency": current.get("currencyUnit", "USD"),
"availability": product.get("availabilityStatus"),
"rating": product.get("averageRating"),
"review_count": product.get("numberOfReviews"),
"seller_id": product.get("sellerId"),
"seller_name": product.get("sellerName"),
"is_marketplace": product.get("sellerId", "0") != "0",
}
# --- Uso ---
if __name__ == "__main__":
raw = fetch_product("1752637026")
result = parse_product(raw)
print(json.dumps(result, indent=2))
Pontos importantes sobre este script:
- O
country-USno username do proxy garante IP residencial americano — essencial, pois o Walmart redireciona tráfego internacional e exibe preços/catálogos diferentes. - O header
User-Agentdeve ser de um navegador desktop recente. Não use strings genéricas comopython-requests/2.x. - O
Accept-Encoding: gzip, deflate, bré necessário porque o Akamai usa compressão Brotli como fingerprint.
Exemplo com curl
curl -x http://user-country-US:PASSWORD@gate.proxyhat.com:8080 \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
-H "Accept: text/html,application/xhtml+xml" \
-H "Accept-Language: en-US,en;q=0.9" \
"https://www.walmart.com/ip/product/1752637026"
Marketplace (3P) vs catálogo 1P — como diferenciar
O Walmart opera dois modelos de venda:
- 1P (first-party) — o Walmart é o vendedor.
sellerId = "0"esellerName = "Walmart.com". Preços e estoque são controlados diretamente pelo Walmart. - 3P (Marketplace) — vendedores terceiros.
sellerIdé um número não-zero. O produto pode ter múltiplos offers de diferentes vendedores.
No __NEXT_DATA__, a diferença aparece em dois lugares:
- No campo
sellerIddo objeto principal do produto. - No array
offers(quando presente), que lista todos os vendedores Marketplace para o mesmo item.
Para equipes de inteligência de varejo, isso é crucial:
- Monitorar apenas 1P dá a visão da estratégia de precificação do próprio Walmart.
- Incluir 3P revela a competitividade de preço no Marketplace — essencial para marcas CPG que vendem via distribuidores.
- Produtos 3P podem ter preços artificialmente baixos (loss leaders) que distorcem análises se não forem filtrados.
def classify_seller(product_data: dict) -> dict:
"""Classifica se o produto é 1P ou 3P e extrai offers."""
seller_id = product_data.get("seller_id", "0")
is_3p = seller_id != "0"
result = {
"is_marketplace": is_3p,
"seller_name": product_data.get("seller_name"),
"seller_id": seller_id,
}
# Se houver offers múltiplos no JSON bruto
raw_offers = product_data.get("_raw", {}).get("offers", [])
if raw_offers:
result["offer_count"] = len(raw_offers)
result["lowest_offer_price"] = min(
float(o.get("priceInfo", {}).get("currentPrice", {}).get("price", float("inf")))
for o in raw_offers
)
return result
Agendamento com consciência de rate limits
O Walmart não publica limites de taxa oficiais. Por observação empírica, estes são os thresholds que provocam bloqueios:
| Superfície | Limite observado | Consequência ao exceder |
|---|---|---|
| Páginas de produto | ~50 requests/min por IP | Captcha PerimeterX após 2-3 minutos sustained |
| Páginas de busca | ~20 requests/min por IP | 403 silencioso ou redirect para challenge page |
| APIs internas (AJAX) | ~10 requests/min por IP | Bloqueio imediato — fingerprint diferente de browser |
Estratégia de agendamento recomendada:
- Rotação de IP por request — cada request usa um IP residencial diferente via ProxyHat. Isso distribui o volume entre milhares de IPs.
- Delay entre requests — mesmo com rotação, adicione 2–5 segundos de delay aleatório. Isso reduz a chance de padrões de acesso serem detectados no nível do datacenter.
- Janelas de coleta — para monitoramento de preço, colete em intervalos de 1–4 horas. Para verificação de estoque, intervalos de 15–30 minutos são suficientes.
- Backoff exponencial — ao receber 403 ou captcha, aplique backoff de 30s → 60s → 120s antes de tentar com novo IP.
import time
import random
import requests
from datetime import datetime
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",
"Accept": "text/html,application/xhtml+xml",
"Accept-Language": "en-US,en;q=0.9",
}
def scrape_with_backoff(item_ids: list[str], max_retries: int = 3):
results = []
for i, item_id in enumerate(item_ids):
url = f"https://www.walmart.com/ip/product/{item_id}"
for attempt in range(max_retries):
try:
resp = requests.get(url, headers=HEADERS, proxies=PROXIES, timeout=30)
if resp.status_code == 200:
# Processar __NEXT_DATA__ como no exemplo anterior
results.append({"item_id": item_id, "status": "success"})
break
elif resp.status_code == 403:
wait = 30 * (2 ** attempt) + random.uniform(0, 10)
print(f"403 para {item_id}, aguardando {wait:.0f}s...")
time.sleep(wait)
else:
print(f"Status {resp.status_code} para {item_id}")
break
except requests.RequestException as e:
print(f"Erro em {item_id}: {e}")
time.sleep(10)
# Delay entre requests para evitar padrões detectáveis
delay = random.uniform(2.0, 5.0)
time.sleep(delay)
return results
# Executar coleta
item_ids = ["1752637026", "5432876543", "9876543210"]
scrape_with_backoff(item_ids)
Comparação: proxies residenciais vs datacenter vs mobile para Walmart
| Característica | Datacenter | Residencial | Mobile |
|---|---|---|---|
| Taxa de sucesso no Walmart | <5% | 85–95% | 90–98% |
| Velocidade média | Rápido (<1s) | Moderado (1–3s) | Lento (2–5s) |
| Custo por GB | Menor | Médio | Maior |
| Ideal para | Testes unitários | Scraping em escala | Mobile-specific ou máxima stealth |
| Risco de bloqueio | Altíssimo | Baixo | Muito baixo |
Para a maioria dos casos de uso em Walmart product data, proxies residenciais com rotação por request oferecem o melhor equilíbrio entre custo e confiabilidade. Proxies mobile são reservados para cenários específicos onde o tráfego mobile precisa ser simulado — por exemplo, verificar preços exibidos no app mobile vs desktop.
Considerações éticas e legais
Antes de escalar qualquer operação de scraping no Walmart, considere:
- robots.txt — o Walmart proíbe scraping de
/ip/e/searchno robots.txt. Respeitar ou não depende do seu caso de uso e jurisdição. - Termos de uso — os ToS do Walmart proíbem acesso automatizado. Violar ToS pode ter consequências legais dependendo da sua localização.
- GDPR/CCPA — se você coleta dados de usuários (reviews com PII, por exemplo), está sujeito a regulamentações de privacidade.
- Rate limits — mesmo com proxies residenciais, respeite limites razoáveis. Não tente DDoS o Walmart.
A recomendação: colete apenas os dados necessários para sua análise, armazene o mínimo possível, e nunca redistribua dados protegidos por direitos autorais (imagens de produto, descrições completas) sem autorização.
Principais conclusões
- Use
__NEXT_DATA__— não parseie HTML manualmente. O JSON embutido dá acesso a todos os dados de produto de forma estruturada e estável.- Proxies residenciais são obrigatórios — o Akamai bloqueia IPs de datacenter na borda. Sem IP residencial, você nem chega ao conteúdo.
- Diferencie 1P de 3P —
sellerId = "0"é Walmart direto. Qualquer outro ID é Marketplace. Misturar os dois distorce análises de preço.- Rate limiting empático — 50 req/min/IP para produtos, 20 para busca. Use rotação + delays aleatórios.
- Backoff exponencial — ao receber 403, não insista. Aplique backoff e tente com IP diferente.
Se sua equipe precisa de Walmart proxy confiável para raspagem em escala, a ProxyHat oferece proxies residenciais com geo-targeting por país e cidade, rotação por request e sessões sticky — exatamente o que você precisa para contornar Akamai e PerimeterX. Para mais detalhes sobre casos de uso, veja nosso guia de web scraping com proxies.






