Como Extrair Dados de Produtos da Home Depot Com um Navegador de Agente
Expert in Web Scraping Technologies
Principais Conclusões:
- Uma busca PDP, esquema de produto completo. O HTML da PDP da Home Depot incorpora um bloco JSON-LD
Productno lado do servidor — nome, marca, sku, modelo, gtin, imagem, descrição, ofertas (preço, moeda, disponibilidade), classificação agregada e as 10 melhores avaliações. Esse é o caminho rápido: sem espera pela hidratação, sem shell React para lutar. - Campos hidratados preenchem o restante. Bandeiras de venda, opções de cumprimento (envio / retirada / entrega), texto de disponibilidade por loja e o carrossel de avaliações na PDP chegam após a execução do JavaScript. Scrapeless Scraping Browser — o navegador em nuvem pronto para agentes usado neste guia — os renderiza em um navegador em nuvem real, assim uma única chamada de CLI retorna o DOM preenchido.
- O estoque por loja é o diferenciador. Configurar uma loja da Home Depot através do modal de seleção de localização retorna disponibilidade específica da loja e ETAs de retirada ao lado dos campos padrão da PDP — um sinal que não é exposto através de APIs de busca genéricas.
- Busca e categoria em escala. As páginas de listagem
/s/<query>e/b/<category>paginam via o deslocamento de URLNao. O mesmo padrão Descobrir → Extrair que alimenta a raspagem da PDP retorna cartões paginados com productId, título, marca, preço, classificação, contagem de avaliações, imagem e URL canônica da PDP. - Esquema de avaliação limpo. O extrator por avaliação emite uma forma plana e semântica —
id,title,text,rating,badges,reviewer.name,time,original_source.name,images[].link,total_positive_feedback,total_negative_feedback— que se mapeia diretamente para pipelines típicos de inteligência de avaliações sem renomeação. - Saída de proxy dos EUA é obrigatória — 100%. A Home Depot é um site de varejo exclusivo dos EUA.
--proxy-country USé obrigatório em cada sessão. - Use URLs de produto canônicas. O alvo confiável da PDP é
/p/<slug>/<productId>(com o slug); URLs apenas com ID como/p/<productId>retornaram a página de erro genérica da Home Depot na verificação. O alvo da página de avaliações é/p/reviews/<slug>/<productId>/<page>. - Descobrir → Extrair. Leia o DOM ao vivo com
get htmlprimeiro contra âncoras semânticas (data-testid,aria-label,[itemprop], ids semânticos), depois escreva seletoresevalcontra o que realmente é renderizado. Nomes de classe se alteram; âncoras semânticas não.
Introdução: raspagem de dados de produtos da Home Depot com um navegador de agente
A Home Depot é o maior varejista de melhorias para o lar dos EUA em receita, com um catálogo público abrangendo eletrodomésticos, ferramentas, materiais de construção e serviços. Para equipes de precificação competitiva, monitores de conformidade de MAP, proprietários de marcas vendendo através da Home Depot, pipelines de inventário e pesquisadores de produtos orientados por avaliações, a PDP, busca/categorias e as superfícies de inventário por loja são os dados que impulsionam o pipeline.
O catálogo é hidratado do lado do cliente. Uma típica página de detalhes do produto chega como uma fina shell HTML com um bloco JSON-LD Product renderizado no servidor, e o React preenche o restante assim que o pacote é carregado — preço de venda, promoção atual, opções de cumprimento, disponibilidade na loja, o histograma de classificação e o carrossel de avaliações da PDP são todos preenchidos após a renderização. A raspagem puramente HTTP retorna a shell; os dados vivem um ciclo de renderização depois.
Este post percorre um fluxo de trabalho baseado em terminal em cima do Scrapeless Scraping Browser — um navegador em nuvem pronto para agentes que lida com renderização JavaScript, saída de proxy residencial e estado vinculado à sessão para verificações de estoque por loja. As etapas 1-8 abaixo cobrem a extração completa da PDP (caminho rápido JSON-LD + campos hidratados), paginação de busca/categorias, o fluxo de seleção de localização que desbloqueia a disponibilidade específica da loja, e o pipeline de avaliações (top-10 do JSON-LD mais paginação, ordenação e filtragem do DOM renderizado).
Cada etapa passa pelo CLI scrapeless-scraping-browser documentado em skill-dev/SKILL.md. A ênfase é na experiência do navegador-agente: descreva a forma dos dados do produto que você precisa em linguagem natural e deixe a habilidade dirigir o CLI por trás das cenas.
Para o mesmo padrão Descobrir → Extrair em outro varejista, veja o post do raspador da Amazon.
O Que Você Pode Fazer Com Isso
- Precificação competitiva. Acompanhe preços, bandeiras de venda e textos de promoção em SKUs concorrentes em um ritmo contínuo; Alerta sobre violações de MAP.
- Monitoramento de conformidade de MAP. Proprietários de marcas acompanham os preços de vendedores de terceiros em todo o catálogo e sinalizam listagens abaixo do MAP para fiscalização.
- Inteligência de inventário e cumprimento. Sinais de estoque por loja e ETAs de retirada através do fluxo de seleção de localização expõem disponibilidade regional que APIs de busca genéricas não fazem.
- Ingestão de catálogo. Listagens de busca e categoria alimentam pipelines a montante com um esquema de produto normalizado (productId, título, marca, preço, classificação, contagem de avaliações, imagem, URL canônica).
- Inteligência de avaliações. Análise de sentimento, agrupamento de reclamações, rastreamento de proporção de compradores verificados, coleta de evidência fotográfica — o esquema plano por avaliação se encaixa em pipelines de avaliação existentes.
- Monitoramento de marca. Acompanhe as avaliações de marcas de primeira parte e concorrentes em várias categorias para detectar ataques de avaliação no lançamento ou regredições de qualidade sustentadas.
- Desenvolvimento de produtos. Transforme temas negativos repetidos de avaliações em insumos para o roadmap, documentos de suporte, mudanças em peças de reposição ou melhorias na página de listagem.
Por que o Scrapeless Scraping Browser
O Scrapeless Scraping Browser é um navegador em nuvem personalizável e à prova de detecções, projetado para crawlers da web e agentes de IA. Para o Home Depot especificamente, ele traz:
- Proxies residenciais dos EUA via
--proxy-country US— necessário para o Home Depot. - Renderização JavaScript do lado da nuvem para que preço, cumprimento, disponibilidade da loja, o carrossel de avaliações, dropdowns de classificação, filtros de fotos e a barra de paginação cheguem preenchidos em vez de como a shell React.
- Persistência de sessão via
--session-idem toda a coreografia de snapshot → clique → preenchimento para o localizador de loja, fluxos de classificação/filtro e paginação, de modo que cookies e estado aplicado permaneçam consistentes entre comandos de acompanhamento dentro de uma chamada de shell encadeada. - Impressão digital à prova de detecções em cada sessão, para que PDPs e páginas de listagem renderizem de forma idêntica ao tráfego orgânico.
- Uma única interface de CLI — cada operação necessária para as etapas de descoberta, extração e paginação (
open,wait,snapshot,eval,get,click,fill,cookies) está a uma chamada de CLI de distância.
Obtenha sua chave de API no plano gratuito ao se inscrever no Scrapeless e junte-se à nossa comunidade oficial. A interface completa de CLI está documentada em skill-dev/SKILL.md; a página Soluções de Proxy cobre o plano de proxy residencial que suporta o navegador em nuvem.
Comunidade Oficial do Discord do Scrapeless
Comunidade Oficial do Telegram do Scrapeless
Pré-requisitos
- Node.js 18 ou mais recente.
- Uma conta e chave de API do Scrapeless — inscreva-se em app.scrapeless.com.
jq(opcional, para análise de JSON em scripts de shell — uma alternativa portátil aogrepé mostrada abaixo).- Familiaridade básica com o terminal.
Instalação
As receitas abaixo são executadas na CLI do scrapeless-scraping-browser. A configuração é feita em três etapas — tanto usuários de CLI quanto usuários de agentes de IA precisam da #1 e #2; usuários de agentes de IA fazem também a #3.
1. Instale o pacote CLI
bash
npm install -g scrapeless-scraping-browser
Isso fornece o binário scrapeless-scraping-browser que cada etapa deste post chama. A habilidade não traz seu próprio runtime — ela carrega padrões de comando em seu agente de IA, mas o CLI deve ser instalado primeiro.
2. Configure sua chave de API
Obtenha seu token em app.scrapeless.com, e então armazene-o onde o CLI possa lê-lo:
bash
scrapeless-scraping-browser config set apiKey seu_token_api_aqui
scrapeless-scraping-browser config get apiKey # verifique
Usando um agente de IA? As instruções da habilidade informam ao agente que a autenticação é necessária antes de qualquer chamada de sessão. Se a chave da API não estiver configurada quando o agente acessar pela primeira vez o CLI, ele solicitará e executará o comando config set apiKey … para você.
O arquivo de configuração está localizado em ~/.scrapeless/config.json com acesso restrito ao usuário atual, tem prioridade sobre a variável de ambiente e é portátil entre agentes e runners de CI. Para pipelines de CI, prefira:
bash
export SCRAPELESS_API_KEY=seu_token_api_aqui
3. Instale a habilidade Scrapeless em seu agente de IA
Esta é uma etapa separada da etapa 1. A etapa 1 instalou o binário CLI — o runtime que seu agente invoca. A habilidade é o que ensina seu agente como invocá-lo corretamente (seletor, esperas, padrões de re-tentativa, o fluxo de descobrir → extrair). São duas coisas diferentes, e você precisa de ambas.
A habilidade é uma pasta contendo SKILL.md + skill.json + references/. A fonte canônica é o scrapeless-ai/scrapeless-agent-browser → skills/scraping-browser-skill repositório no GitHub.
Para instalá-la no Claude Code, Cursor, VS Code + GitHub Copilot, OpenAI Codex CLI ou Gemini CLI, siga o guia de instalação do Scrapeless AI Agent — ele contém os comandos de cópia e colagem por agente (bash e Windows PowerShell). Recarregue seu agente após a instalação para que a habilidade se torne ativa.
O que a habilidade carrega no contexto operacional do seu agente logo de início:
- Autenticação — verifica
~/.scrapeless/config.jsonouSCRAPELESS_API_KEYe solicita que você o defina se ausente. - Descobrir → Extrair fluxo de trabalho — leia o DOM ao vivo com
get html "<região>"primeiro, identifique âncoras estáveis (data-testid,aria-label, ids semânticos,[itemprop='review']), e então escreva seletoresevalcom base no que está realmente renderizado. - Armadilhas de espera para Home Depot —
wait 1500entreopene qualquer espera de seletor para evitar a corrida da sessão friachrome://new-tab-page/;wait '<review-anchor>'contra um elemento de cartão de revisão em vez de umnetworkidlegeral, porque o Home Depot continua disparando beacons preguiçosos que nunca se estabilizam. - Sintaxe do seletor — quando usar seletores CSS vs referências de acessibilidade (
@e1dosnapshot -i). - Trabalhadores CLI em paralelo — encadeamento de
&&em um único shell, nomes de sessão exclusivos, ≤3 trabalhadores simultâneos por host. - Erros comuns —
evalretorna valores entre aspas JSON,opensai com valor não zero em navegação bem-sucedida, sessões terminam quando a conexão é encerrada. - Referência completa de comandos — cada flag para
new-session,open,wait,eval,get,click,fill,snapshot,cookies,recording,stop.
4. Verifique se a habilidade está configurada
Antes da primeira raspagem real do Home Depot, teste a instalação com um prompt seguro:
"Usando a habilidade Scrapeless, abra https://example.com e me diga o título da página."
O agente deve criar uma sessão Scrapeless, navegar e responder com "Exemplo de Domínio". Se essas duas palavras voltarem, a habilidade está carregada, a chave da API está definida e o navegador em nuvem é acessível.
Se falhar:
| Sintoma | Causa provável | Solução |
|---|---|---|
| "Não tenho uma ferramenta/habilidade para fazer isso" | Habilidade não carregada nesta sessão do agente | Reinstale via o guia de instalação da habilidade e recarregue o agente |
Autenticação falhou / 401 |
Chave da API não definida | Execute novamente scrapeless-scraping-browser config set apiKey <token> (Passo de instalação 2) |
comando não encontrado |
Binário CLI ausente no PATH | Execute novamente o Passo de instalação 1 |
ERR_TUNNEL_CONNECTION_FAILED no Home Depot |
O pool de proxy não tinha IP residencial disponível no momento da alocação | Crie uma nova sessão — mantenha --proxy-country US e tente novamente em breve |
Travamentos / vai para chrome://new-tab-page/ |
Corrida de espera da sessão fria | Peça ao agente para tentar novamente — a habilidade sabe inserir wait 1500 entre open e a próxima espera |
| Home Depot retorna a página de erro genérica | Saída não dos EUA, URL só com ID ou sinal de impressão digital de sessão transitória | Confirme --proxy-country US, use uma URL canônica /p/<slug>/<productId>, crie uma nova sessão, tente novamente |
Sessão Scrapeless foi encerrada e não pode ser reconectada em cada chamada de nova sessão |
O daemon local armazenou em cache um ID de sessão agora encerrada e continua tentando se reconectar a ela | Mate o daemon e limpe seu arquivo pid, então crie uma nova sessão: Stop-Process -Id (Get-Content "$env:USERPROFILE\.scrapeless-scraping-browser\default.pid") -Force; Remove-Item "$env:USERPROFILE\.scrapeless-scraping-browser\default.pid","$env:USERPROFILE\.scrapeless-scraping-browser\default.port" -Force (PowerShell no Windows). No Linux/macOS o caminho é ~/.scrapeless-scraping-browser/. |
Como você realmente usa isso: incentive seu agente
Após a instalação, você raspa dados de produtos do Home Depot conversando com seu agente — não copiando e colando bash. A habilidade carrega seletores, esperas, classificadores de retry e o padrão descobrir → extrair no contexto do agente, então um prompt de linguagem natural de uma linha é suficiente para obter JSON estruturado de volta.
Prompts que você pode colar
| Você diz ao seu agente | O que você recebe de volta |
|---|---|
| "Obtenha o esquema completo do produto para o produto 204279858 do Home Depot (preço, marca, modelo, imagem, disponibilidade)." | Esquema JSON-LD do produto da Etapa 2 + payload de preço/atendimento hidratado da Etapa 3 |
| "Rastreie o preço + sinal de venda para este PDP do Home Depot." | preço, preçoAnterior, emPromoção, promoção, moeda |
| "Pesquise no Home Depot por 'furadeira sem fio' e retorne as primeiras 5 páginas de resultados." | Cartões de listagem paginados: productId, título, marca, preço, classificação, contagem de revisões, imagem, productUrl |
| "Verifique a disponibilidade do produto 204279858 no CEP 90015." | loja, textoDisponibilidade, pickupEta, contagemEstoque (payload por loja) |
| "Para esses 20 IDs de produtos, obtenha preço + disponibilidade por loja no CEP 33101." | Um JSON de dados de produto por ID com o payload de atendimento por loja |
| "Resolva o ID do produto Home Depot 326716329 para sua URL canônica de revisões, depois retorne o JSON de revisões." | URL canônica mais resumo de nível de produto e array de revisões |
"Obtenha as primeiras 100 revisões do Home Depot a partir desta URL /p/reviews/..., do mais novo para o mais antigo." |
Revisões paginadas com metadados de página e índice por página |
| "Obtenha apenas as revisões de foto para este produto do Home Depot." | Revisões filtradas por images.length > 0 |
| "Para o produto 204279858, liste apenas as revisões de compradoras verificadas." | Revisões filtradas pelo crachá de compradora verificada |
| "Obtenha as revisões mais novas e depois ordene por contagem de úteis, retorne as 30 principais." | Revisões após o fluxo de ordenação da UI, classificadas por votos úteis |
| "Abra a página do produto, defina o CEP da loja 90015 e, em seguida, colete as avaliações do contexto da loja." | Avaliações coletadas de uma sessão de navegador configurada para a loja (veja a Etapa 5) |
Exemplo trabalhado: obter avaliações para o produto 204279858 (furadeira DEWALT)
Você digita:
"Obtenha título, texto, avaliação e nome do revisor para as principais avaliações do produto 204279858 da Home Depot. Retorne JSON."
O plano do agente (em inglês simples):
- Resolva
204279858para o URL canônico do PDP/p/<slug>/204279858.- Crie uma sessão de egressão dos EUA (
--proxy-country US); tente novamente uma vez emos error 10054/ 503 transientes.- Abra o URL do PDP,
espere 1500, depoisespere 'h1'para evitar a corrida de sessão fria.evalcontrascript[type="application/ld+json"]para analisar o esquemaProductincorporado e retornar as 10 principais avaliações + agregados.- Se mais de 10 avaliações forem necessárias, navegue para
/p/reviews/<slug>/204279858/<page>e execute o extrator de DOM renderizado (Etapa 6) por página.
O que você recebe de volta (resultado ilustrativo, trechos do corpo cortados para comprimento):
json
{
"productId": "204279858",
"productUrl": "https://www.homedepot.com/p/DEWALT-20V-MAX-Cordless-1-2-in-Drill-Driver-2-20V-1-3Ah-Batteries-Charger-and-Bag-DCD771C2/204279858",
"productName": "DEWALT 20V MAX Cordless 1/2 in. Drill/Driver, (2) 20V 1.3Ah Batteries, Charger and Bag DCD771C2",
"brand": "DEWALT",
"sku": "1000014677",
"modelNumber": "DCD771C2",
"overallRating": 4.7,
"totalReviews": 11168,
"reviewsReturned": 10,
"reviews": [
{
"title": "Ótima ferramenta!",
"text": "Estou usando isso há duas semanas e vale cada centavo. A qualidade é excepcionalmente nítida...",
"rating": 5,
"reviewer": { "name": "kevein" },
"time": null,
"original_source": { "name": "homedepot.com" }
},
{
"title": null,
"text": "Comprei a furadeira/driver Dewalt 20v max para substituir uma antiga furadeira sem fio Craftsman...",
"rating": 5,
"reviewer": { "name": "DIYer_Bill" },
"time": null,
"original_source": { "name": "homedepot.com" }
}
// ... 8 mais avaliações com o mesmo formato
]
}
time volta como null porque a Home Depot não inclui datePublished nos objetos de avaliação JSON-LD — os timestamps estão presentes no DOM da página de avaliações renderizada, portanto, recupere-os via o caminho da Etapa 6 se os timestamps das avaliações forem necessários.
Essa é toda a interface voltada para o usuário para este scraping. O bash, seletores e esperas nas Etapas 1-8 abaixo são o que a habilidade faz o agente executar — você não precisa digitar nenhum deles.
Moldando prompts: como controlar o que vem de volta
| Formulação | Efeito |
|---|---|
| "…retornar JSON" / "…como CSV" | Formato de saída |
| "…campos: título, texto, avaliação, somente hora" | Restringe os campos que o agente extrai |
| "…top 25" / "…pelas páginas 1–10" | Profundidade da paginação |
| "…novas primeiro" / "…menor avaliação primeiro" | Aciona o fluxo de classificação da UI |
| "…apenas avaliações com fotos" | Aciona o filtro de avaliações com fotos |
| "…apenas compradores verificados" | Filtra as avaliações extraídas por selo |
| "…salvar em reviews.jsonl" | Escreve uma avaliação por linha em arquivo |
| "…então resuma as 5 principais reclamações" | Encadeia uma passagem de análise pós-extração |
| "…defina o CEP da loja 90015 primeiro" | Aciona o fluxo de localizador de loja antes de fazer scraping |
Esse é o fluxo de trabalho. As Etapas 1–8 abaixo são a referência interna — leia-as uma vez para ver como o padrão de descobrir → extrair se compõe; então confie em seu agente para aplicar isso.
Etapa 1 — Conectar-se ao Navegador de Scraping Scrapeless
Crie uma sessão de egressão dos EUA.
bash
SESSION=$(scrapeless-scraping-browser new-session \
--name "dados-do-produto-homedepot" \
--ttl 1800 \
--recording true \
--proxy-country US \
--json | jq -r '.data.taskId')
echo "Sessão: $SESSION"
Fallback portátil sem jq:
bash
SESSION=$(scrapeless-scraping-browser new-session \
--name "dados-do-produto-homedepot" --ttl 1800 --recording true \
--proxy-country US --json \
| grep -oE '"taskId":"[^"]*"' | cut -d'"' -f4)
A alocação de proxy residencial ocasionalmente retorna um 503 transiente na primeira tentativa — tente novamente uma vez. Se ERR_TUNNEL_CONNECTION_FAILED aparecer, a piscina de proxy não tem IP disponível no momento da alocação; crie uma nova sessão e tente novamente em breve.
Etapa 2 — Caminho rápido: extrair esquema do produto + 10 principais avaliações do JSON-LD
O HTML do PDP da Home Depot incorpora um bloco JSON-LD Product do lado do servidor — nome, marca, sku, modelo, gtin, imagem, descrição, ofertas (preço, moeda, disponibilidade), avaliação agregada e as 10 principais avaliações — tudo isso sem esperar pela hidratação. Campos compatíveis com Schema.org que o produto não possui (por exemplo, disponibilidade, vendedor, preçoVálidoAté, mpn, cor) retornam null e devem ser tratados como anuláveis.
Abra o PDP canônico (com o slug — URLs apenas com ID retornam uma página de erro genérica) e eval o analisador JSON-LD:
bash
PRODUCT_ID="204279858"
Sure, here is the translation to Portuguese:
plaintext
PRODUCT_SLUG="DEWALT-20V-MAX-Cordless-1-2-in-Drill-Driver-2-20V-1-3Ah-Batteries-Charger-and-Bag-DCD771C2"
PDP_URL="https://www.homedepot.com/p/$PRODUCT_SLUG/$PRODUCT_ID"
scrapeless-scraping-browser --session-id $SESSION open "$PDP_URL"
# Breve pausa para que a próxima espera não seja resolvida na pré-aquecida
# chrome://new-tab-page/ antes que a navegação se comprometa.
scrapeless-scraping-browser --session-id $SESSION wait 1500
# Aguarde pelo título do produto H1 — os blocos JSON-LD são renderizados no servidor em
# o mesmo HTML estático e são garantidamente presentes assim que o H1 está visível.
# (Não espere no `script[type="application/ld+json"]` diretamente — `wait` padrão
# para o estado "visível", e elementos `<script>` nunca são visíveis.)
scrapeless-scraping-browser --session-id $SESSION wait 'h1'
scrapeless-scraping-browser --session-id $SESSION eval '
(() => {
const ld = [...document.querySelectorAll("script[type=\"application/ld+json\"]")]
.map((s) => { try { return JSON.parse(s.textContent); } catch { return null; } })
.filter(Boolean);
const product = ld.find((o) => o["@type"] === "Product");
if (!product) return JSON.stringify({ error: "sem Product JSON-LD nesta página" });
const productId =
product.productID || product.sku ||
location.pathname.match(/\/(\d+)(?:\/\d+)?(?:[/?#]|$)/)?.[1] || null;
const offers = Array.isArray(product.offers) ? product.offers[0] : product.offers || null;
const offerPrice = offers
? (offers.price ?? offers.priceSpecification?.price ?? offers.lowPrice ?? null)
: null;
const availability = offers?.availability
? String(offers.availability).replace(/^https?:\/\/schema\.org\//, "")
: null;
const images = []
.concat(product.image || [])
.flat()
.filter(Boolean);
const reviews = (Array.isArray(product.review) ? product.review : product.review ? [product.review] : [])
.map((r) => ({
title: r.headline || null,
text: r.reviewBody || null,
rating: Number(r.reviewRating?.ratingValue) || null,
reviewer: { name: r.author?.name || null },
time: r.datePublished || null,
original_source: { name: "homedepot.com" },
}));
return JSON.stringify({
productId,
productUrl: location.href,
productName: product.name,
brand: product.brand?.name || product.brand || null,
sku: product.sku || null,
modelNumber: product.model || null,
gtin: product.gtin13 || product.gtin14 || product.gtin12 || product.gtin8 || product.gtin || null,
mpn: product.mpn || null,
category: product.category || null,
description: product.description || null,
color: product.color || null,
image: images[0] || null,
images,
offers: {
price: offerPrice != null ? Number(offerPrice) : null,
currency: offers?.priceCurrency || null,
availability,
priceValidUntil: offers?.priceValidUntil || null,
itemCondition: offers?.itemCondition
? String(offers.itemCondition).replace(/^https?:\/\/schema\.org\//, "")
: null,
seller: offers?.seller?.name || null,
},
overallRating: Number(product.aggregateRating?.ratingValue) || null,
totalReviews: Number(product.aggregateRating?.reviewCount) || null,
reviewsReturned: reviews.length,
reviews,
});
})()
'
Esta única busca PDP retorna o esquema de produto completo renderizado pelo servidor — productId, nome, marca, sku, modelo, gtin, imagem, descrição, ofertas (preço, moeda, disponibilidade), classificação agregada — além das 10 principais avaliações em uma forma semântica e plana (id, título, texto, avaliação, nome do revisor, tempo, nome da fonte original, imagens[].link, total_feedback_positivo, total_feedback_negativo). Retorna de forma confiável sem depender do shell React para hidratar.
Alguns campos são condicionais. O JSON-LD fornece o que o schema.org marca o produto — gtin, mpn, cor, preçoVálidoAté, vendedor estão presentes na maioria dos itens do catálogo, mas não em todos (trate-os como anuláveis). Para a detecção de sinalização de venda, texto de promoção atual e disponibilidade por loja, estes vivem no DOM renderizado em vez de JSON-LD — a Etapa 3 cobre essa superfície. Para o histograma de classificação por estrela (contagens de 5★/4★/3★/2★/1★), a região do histograma é renderizada inline na página de avaliações coberta na Etapa 6.
Etapa 3 — Campos hidratados do PDP (preço, cumprimento, imagem, especificações)
A passagem JSON-LD da Etapa 2 retorna o esquema de produto estático. Substituições de preço de venda, texto de promoção atual, opções de cumprimento (envio para casa, retirada na loja, entrega agendada), o distintivo de estoque, a galeria da imagem principal e as tabelas de especificações/características vivem no DOM renderizado e chegam após a hidratação do React. Direcione o fluxo de descoberta → extração na mesma PDP, contra âncoras semânticas que sobrevivem à rotação de nome de classe.
Execute um passe de descoberta `get html` contra a região de preço do PDP primeiro, confirme os nomes de âncoras ativos, depois aperte os seletores `eval` para o que a página realmente exibe. Os nomes de classe mudam; padrões de `data-testid` e `aria-label` são a superfície estável.
```bash
# A sessão ainda está no PDP da Etapa 2. Aguarde a região de preço renderizar,
# depois examine o HTML circundante para confirmar âncoras.
scrapeless-scraping-browser --session-id $SESSION wait '[data-testid*="price" i], [class*="price" i], [data-component*="Price"]'
scrapeless-scraping-browser --session-id $SESSION get html '[data-testid*="price" i], [data-testid*="fulfillment" i]'
A partir do HTML descoberto, as âncoras que duram mais tempo são [data-testid*="price"], [data-testid*="fulfillment"], [data-testid*="availability"], e aria-labels nos botões de cumprimento (aria-label*="Enviar para Casa", aria-label*="Retirada na Loja", aria-label*="Entrega Agendada"). Em seguida, eval o extrator:
bash
scrapeless-scraping-browser --session-id $SESSION eval '
(() => {
const text = (el) => (el ? el.textContent.replace(/\s+/g, " ").trim() : null);
const number = (s) => {
if (!s) return null;
const m = String(s).replace(/,/g, "").match(/-?\d+(?:\.\d+)?/);
return m ? Number(m[0]) : null;
};
const priceText =
text(document.querySelector("[data-testid*=\"price\" i]")) ||
text(document.querySelector("[class*=\"price\" i]"));
const wasPriceText = text(document.querySelector(
"[data-testid*=\"was-price\" i], [class*=\"was-price\" i], [data-testid*=\"strike\" i]"
));
const onSale = !!wasPriceText && priceText !== wasPriceText;
const promotion = text(document.querySelector("[data-testid*=\"promo\" i], [data-testid*=\"saving\" i]"));
const fulfillment = [...document.querySelectorAll(
"[data-testid*=\"fulfillment\" i] button, [aria-label*=\"Enviar\" i], [aria-label*=\"Retirada\" i], [aria-label*=\"Entrega\" i]"
)].map((btn) => {
const label = btn.getAttribute("aria-label") || text(btn);
return label ? { option: label, available: !btn.matches("[disabled], [aria-disabled=\"true\"]") } : null;
}).filter(Boolean);
const availabilityText = text(document.querySelector(
"[data-testid*=\"availability\" i], [data-testid*=\"in-stock\" i]"
));
const heroImg = document.querySelector(
"img[data-testid*=\"main-image\" i], [data-testid*=\"image-gallery\" i] img, picture img"
);
const features = [...document.querySelectorAll(
"[data-testid*=\"feature\" i] li, [class*=\"feature\" i] li, [data-testid*=\"highlights\" i] li"
)].map(text).filter(Boolean).slice(0, 20);
const specs = Object.fromEntries(
[...document.querySelectorAll("[data-testid*=\"specs\" i] tr, [class*=\"specs\" i] tr")]
.map((row) => {
const cells = [...row.querySelectorAll("th, td")].map(text);
return cells.length >= 2 ? [cells[0], cells.slice(1).join(" ")] : null;
})
.filter(Boolean)
);
return JSON.stringify({
productUrl: location.href,
price: number(priceText),
priceText,
wasPrice: number(wasPriceText),
onSale,
promotion,
currency: "USD",
fulfillment,
availabilityText,
image: heroImg?.currentSrc || heroImg?.src || null,
features,
specs,
});
})()
'
Para a maioria dos pipelines de inteligência de preços, execute tanto a Etapa 2 quanto a Etapa 3 contra a mesma sessão: a Etapa 2 captura o esquema JSON-LD canônico (productId, brand, sku, model, gtin, image, offers), a Etapa 3 captura as substituições hidratadas (bandeira de venda, promoção, opções de cumprimento, badge de estoque, especificações/funcionalidades). As duas cargas se fundem de forma limpa em productUrl.
Etapa 4 — Busca e listagens de categorias
Home Depot expõe a busca em /s/<query> e páginas de categorias em /b/<category-slug>. Ambas paginam através do deslocamento da URL Nao (?Nao=24, ?Nao=48, …). Cada página exibe ~24 cartões de produtos.
bash
QUERY="furadeira sem fio"
# Codifique espaços na consulta (Home Depot usa codificação padrão de URL)
SEARCH_URL="https://www.homedepot.com/s/${QUERY// /%20}"
scrapeless-scraping-browser --session-id $SESSION open "$SEARCH_URL"
# As páginas de busca têm uma renderização em duas fases: um esqueleto de espaço reservado React com um
# único cartão monta primeiro, depois a grade real de ~24 cartões se preenche. Um simples
# `wait '[data-testid*="product-pod" i]'` pode resolver no espaço reservado, então
# o extrator roda contra uma grade vazia. Aguarde pela grade real
# (≥ 5 cartões) em vez de qualquer correspondência única.
scrapeless-scraping-browser --session-id $SESSION wait 3000
scrapeless-scraping-browser --session-id $SESSION eval \
'document.querySelectorAll("[data-testid*=\"product-pod\" i], [data-pod-position]").length >= 5'
scrapeless-scraping-browser --session-id $SESSION eval '
(() => {
const text = (el) => (el ? el.textContent.replace(/\s+/g, " ").trim() : null);
const number = (s) => {
if (!s) return null;
```javascript
const m = String(s).replace(/,/g, "").match(/-?\d+(?:\.\d+)?/);
return m ? Number(m[0]) : null;
};
const cards = [...document.querySelectorAll("[data-testid*=\"product-pod\" i], [data-pod-position]")];
const results = cards.map((c) => {
const link = c.querySelector("a[href*=\"/p/\"]");
const href = link?.getAttribute("href");
const productId = href?.match(/\/(\d{6,})(?:[/?#]|$)/)?.[1] || null;
const titleEl = c.querySelector("[data-testid*=\"product-title\" i], [class*=\"product-title\" i], h2, h3");
const ratingEl = c.querySelector("[aria-label*=\"em\" i]");
const reviewCountEl = c.querySelector("[data-testid*=\"review-count\" i], [class*=\"review-count\" i]");
const priceEl = c.querySelector("[data-testid*=\"price\" i], [class*=\"price\" i]");
const brandEl = c.querySelector("[data-testid*=\"brand\" i], [class*=\"brand\" i]");
const img = c.querySelector("img");
return {
productId,
productUrl: href ? new URL(href, location.origin).href : null,
title: text(titleEl),
brand: text(brandEl),
price: number(text(priceEl)),
priceText: text(priceEl),
rating: number(ratingEl?.getAttribute("aria-label")),
reviewCount: number(text(reviewCountEl)),
image: img?.currentSrc || img?.src || null,
};
}).filter((r) => r.productId || r.productUrl);
const offset = Number(new URLSearchParams(location.search).get("Nao") || 0);
return JSON.stringify({
query: location.href,
page: Math.floor(offset / 24) + 1,
pageSize: results.length,
results,
});
})()
'
Loop de Paginação para as primeiras cinco páginas:
bash
for offset in 0 24 48 72 96; do
scrapeless-scraping-browser --session-id $SESSION open "$SEARCH_URL?Nao=$offset"
scrapeless-scraping-browser --session-id $SESSION wait 1500
scrapeless-scraping-browser --session-id $SESSION wait '[data-testid*="product-pod" i], [data-pod-position]'
scrapeless-scraping-browser --session-id $SESSION eval '/* extrator de listagens iguais */' \
> "search-page-$((offset / 24 + 1)).json"
done
Para uma maior distribuição em dezenas de consultas, crie novas sessões para cada consulta e limite a concorrência a ≤3 trabalhadores por host (veja a nota de trabalhadores paralelos do Passo 8). As páginas de categoria (/b/<category-slug>) aceitam o mesmo deslocamento Nao e o mesmo extrator.
Passo 5 — Verificação de estoque por loja (CEP)
A característica distintiva da Home Depot é a disponibilidade por loja: definir uma loja da Home Depot através do modal de seleção de localização retorna o estoque e o texto de ETA de retirada específicos da loja na mesma página de detalhes do produto (PDP), juntamente com opções de atendimento nacional. O mesmo fluxo também pode mudar a ordenação de revisões "Mais Útil" para votos úteis regionais, então uma sessão com a loja definida cobre ambos os casos de uso de inventário e contexto de revisão.
As referências de acessibilidade
@e15/@e22/@e25no trecho são marcadores — Scrapelesssnapshot -iatribui referências dinamicamente por renderização de página. Capture o instantâneo, encontre as linhas rotuladas "Alterar Loja", o campo de entrada do CEP e o botão Pesquisar / Confirmar, e substitua os números reais@e<n>antes de executar a sequência de cliques. Confirme os seletores[data-testid*="store-locator" i]/[data-testid*="store-name" i]contra o DOM ao vivo com uma passagem deget html— estes correspondem às convenções gerais dedata-testidda Home Depot, mas devem ser ajustados para o que a página renderiza.
bash
CEP="90015"
# 1. Abra a PDP e mostre as referências de acessibilidade para o seletor de localização.
scrapeless-scraping-browser --session-id $SESSION open \
"https://www.homedepot.com/p/$PRODUCT_SLUG/$PRODUCT_ID"
scrapeless-scraping-browser --session-id $SESSION wait 1500
scrapeless-scraping-browser --session-id $SESSION wait '[data-testid*="store-locator" i], [aria-label*="store" i]'
scrapeless-scraping-browser --session-id $SESSION snapshot -i > /tmp/pdp-refs.txt
# 2. Clique em "Alterar Loja" / "Minha Loja" → preencha o CEP → confirme.
# Ajuste as referências @e<n> abaixo conforme o que o instantâneo retorna.
scrapeless-scraping-browser --session-id $SESSION click @e15 # Alterar Loja
scrapeless-scraping-browser --session-id $SESSION wait 800
scrapeless-scraping-browser --session-id $SESSION fill @e22 "$CEP" # Entrada CEP
scrapeless-scraping-browser --session-id $SESSION click @e25 # Pesquisar / Confirmar
scrapeless-scraping-browser --session-id $SESSION wait 1500
scrapeless-scraping-browser --session-id $SESSION wait '[data-testid*="fulfillment" i], [data-testid*="availability" i]'
# 3. Extraia a disponibilidade por loja + o distintivo da loja.
scrapeless-scraping-browser --session-id $SESSION eval '
(() => {
const text = (el) => (el ? el.textContent.replace(/\s+/g, " ").trim() : null);
return JSON.stringify({
productUrl: location.href,
store: text(document.querySelector("[data-testid*=\"store-name\" i], [data-testid*=\"my-store\" i]")),
zip: "'$CEP'",
pt
availabilityText: texto(document.querySelector("[data-testid*=\"availability\" i], [data-testid*=\"in-stock\" i]")),
pickupEta: texto(document.querySelector("[data-testid*=\"pickup\" i], [aria-label*=\"Pickup\" i]")),
stockCount: texto(document.querySelector("[data-testid*=\"stock-count\" i], [class*=\"stock-count\" i]")),
});
})()
'
O estado da loja é preservado em cookies pelo restante da sessão — chamadas subsequentes (a avaliação de campos hidratados da Etapa 3, o extrator de resenhas renderizadas da Etapa 6, o fluxo de classificação da Etapa 7) retornam a visualização regional sem precisar reabrir o modal.
**Fallback definido por cookies.** Quando o modal do seletor de localização não é acessível (bloqueador de pop-up, variante A/B, WAF transitório), o mesmo resultado está disponível via cookies — `THD_LOCSTORE` / `THD_PERSIST` carregam o id da loja e o CEP. Defina-os via `scrapeless-scraping-browser cookies set` e o próximo `open` retorna o PDP de contexto da loja sem o fluxo de cliques.
---
## Etapa 6 — Avaliações 11+ via o carrossel renderizado
Quando o fluxo de trabalho precisa de mais do que os 10 principais do JSON-LD (corpo completo de avaliações, sort/filter aplicados, intervalo de datas personalizado), o widget de avaliação renderizado é a superfície. As avaliações estão em `/p/reviews/<slug>/<productId>/<page>` e dentro do carrossel on-PDP; ambos renderizam através do mesmo micro-frontend de avaliações de produtos paginados em `assets.thdstatic.com/experiences/paginated-product-reviews/*`.
Conduza o fluxo descobrir → extrair:
```bash
REVIEWS_URL="https://www.homedepot.com/p/reviews/$PRODUCT_SLUG/$PRODUCT_ID/1"
scrapeless-scraping-browser --session-id $SESSION open "$REVIEWS_URL"
scrapeless-scraping-browser --session-id $SESSION wait 1500
# networkidle NÃO se estabiliza no Home Depot — aguarde pelos âncoras do cartão de avaliação.
scrapeless-scraping-browser --session-id $SESSION wait "#reviews, [itemprop='review'], [data-testid*='review' i]"
# Dê uma olhada no HTML da região de avaliações para confirmar os âncoras
scrapeless-scraping-browser --session-id $SESSION get html "#reviews, [data-component*='Review' i]"
Do HTML retornado, identifique os âncoras estáveis: os cartões de avaliação geralmente estão sob [itemprop='review'], [data-testid*='review' i], ou estrutura repetida <article>/<li>; widgets de estrelas carregam um aria-label como "5 de 5"; nomes de revisores ficam perto de padrões de cabeçalho repetidos; distintivos de comprador verificado expõem conteúdo de texto estável; botões de paginação carregam rótulos acessíveis.
Então eval a extração:
bash
scrapeless-scraping-browser --session-id $SESSION eval '
(() => {
const texto = (el) => (el ? el.textContent.replace(/\s+/g, " ").trim() : null);
const numero = (v) => {
if (v == null) return null;
const m = String(v).replace(/,/g, "").match(/-?\d+(?:\.\d+)?/);
return m ? Number(m[0]) : null;
};
const unico = (xs) => [...new Set(xs.filter(Boolean))];
const productId =
location.pathname.match(/\/(\d+)(?:\/\d+)?(?:[/?#]|$)/)?.[1] || null;
const root =
document.querySelector("#reviews") ||
document.querySelector("[data-component*=\"Review\" i]") ||
document.querySelector("[data-testid*=\"review\" i]") ||
document.body;
const cards = [...root.querySelectorAll(
"[itemprop=\"review\"], [data-testid*=\"review\" i], article, li"
)].filter((c) => {
const bodyStr = texto(c) || "";
const hasRating = !!c.querySelector("[aria-label*=\"out of\" i]");
const looksReviewish = /(verificado|recomendar|útil|avaliação|comprado)/i.test(bodyStr);
return bodyStr.length > 40 && (hasRating || looksReviewish);
});
const reviews = cards.map((c) => {
const bodyStr = texto(c) || "";
const ratingLabel =
c.querySelector("[aria-label*=\"out of\" i]")?.getAttribute("aria-label") ||
texto(c.querySelector("[data-testid*=\"rating\" i]"));
const dateEl = c.querySelector("time, [datetime], [data-testid*=\"date\" i]");
const helpfulText = unico(
[...c.querySelectorAll("button, [aria-label*=\"útil\" i]")]
.map((el) => el.getAttribute("aria-label") || texto(el))
).join(" ");
return {
id: c.getAttribute("id") || c.getAttribute("data-review-id") || null,
title: texto(c.querySelector("[data-testid*=\"title\" i], h3, h4")),
text: texto(c.querySelector("[data-testid*=\"body\" i], p")),
rating: numero(ratingLabel),
isRecommended: /recomendar/i.test(bodyStr)
? !/não recomendar|não recomendaria/i.test(bodyStr) : null,
badges: unico(
[...c.querySelectorAll("[data-testid*=\"badge\" i], [class*=\"badge\" i]")].map(texto)
.concat(bodyStr.match(/Comprador Verificado|Comprador Verificado|Pro|Escolha da Equipe|Incentivado/gi) || [])
),
reviewer: { name: texto(c.querySelector("[data-testid*=\"author\" i], [class*=\"author\" i]")) },
time: dateEl?.getAttribute("datetime") || texto(dateEl),
original_source: { name: "homedepot.com" },
imagens: único([...c.querySelectorAll("img")].map((i) => i.currentSrc || i.src)).map((link) => ({ link })),
total_feedback_positivo: número(helpfulText.match(/(\d+)\s*(?:pessoas\s*)?(?:consideraram\s*)?(?:útil|sim|para cima)/i)?.[1]),
total_feedback_negativo: número(helpfulText.match(/(\d+)\s*(?:não útil|não|para baixo)/i)?.[1]),
verificado: /verificado/i.test(bodyStr),
};
}).filter((r) => r.texto || r.titulo);
return JSON.stringify({ productId, productUrl: location.href, reviewsReturned: reviews.length, reviews });
})()
'
O extrator de DOM renderizado é o caminho para a paginação de avaliações (Passo 8), aplicando ordenação/filtro (Passo 7), e qualquer vez que o fluxo de trabalho precisar de avaliações 11+. Trate campos ausentes como `null` ou `[]` em vez de descartar a chave — isso mantém os pipelines a montante estáveis quando o Home Depot rotaciona um modelo de cartão de avaliação.
> **Esquema por avaliação:** `id`, `titulo`, `texto`, `classificação`, `insígnias`, `nome_reviewer` (aninhado), `tempo`, `nome_original_source` (aninhado), `imagens[].link` (array de objetos), `total_feedback_positivo`, `total_feedback_negativo`, além dos extras Scrapeless `isRecommended` (booleano derivado do texto da avaliação) e `verificado` (booleano derivado do texto da insígnia). Mantenha campos ausentes como `null` ou `[]` para que os consumidores a montante permaneçam estáveis entre as rotações do DOM.
Se a passagem de avaliação renderizada retornar zero cards mesmo após a espera, o micro-front-end não foi totalmente hidratado; tente a espera novamente, tente com uma nova sessão se `Acesso Negado` retornou, ou recorra ao JSON-LD dos 10 melhores do Passo 2 mais o caminho de paginação GraphQL (`/federation-gateway/graphql?opname=reviewSentiments`) para consultas mais profundas.
---
## Passo 7 — Ordenar e filtrar avaliações pela interface
O Home Depot expõe opções de ordenação (mais recente, maior classificação, menor classificação, mais útil, apenas avaliações com fotos) na página de avaliações. Dirija esses controles dentro da mesma sessão persistente — o instantâneo → a coreografia de clique preserva cookies e estado aplicado.
> Os refs `@e34` / `@e35` / `@e36` / `@e41` no trecho são espaços reservados — o instantâneo `scrapeless -i` atribui refs dinamicamente por renderização de página. Capture o instantâneo primeiro, encontre as linhas para os botões de dropdown de ordenação e filtragem, e substitua os números reais antes de executar a sequência de clique.
```bash
# Superfície de referências de acessibilidade para controles interativos
scrapeless-scraping-browser --session-id $SESSION snapshot -i > /tmp/reviews-refs.txt
# Identifique os refs do botão de dropdown de ordenação / filtro do instantâneo, em seguida, clique.
# Em uma página típica de avaliações do Home Depot, esses aparecem como algo como:
# @e34 [botão] "Ordenar por: Mais Útil"
# @e35 [botão] "Apenas Fotos"
# @e36 [botão] "Comprador Verificado"
# Ajuste os números @ref abaixo para o que o instantâneo retornar.
scrapeless-scraping-browser --session-id $SESSION click @e34 # abrir dropdown de ordenação
scrapeless-scraping-browser --session-id $SESSION wait 800
scrapeless-scraping-browser --session-id $SESSION snapshot -i > /tmp/sort-options.txt
scrapeless-scraping-browser --session-id $SESSION click @e41 # opção "Mais Recente"
scrapeless-scraping-browser --session-id $SESSION wait 1500
scrapeless-scraping-browser --session-id $SESSION wait "#reviews, [data-component*='Review' i]"
# Reexecutar a extração após a região de avaliação ser renderizada novamente
scrapeless-scraping-browser --session-id $SESSION eval '/* mesmo corpo de extração de avaliação do Passo 6 */'
Para uma passagem apenas de fotos, clique no filtro de avaliação visível; a região de avaliação renderiza novamente com o conjunto filtrado, e o mesmo extrator no Passo 6 retorna apenas avaliações com fotos. Se a página não expõe um filtro de fotos para um determinado produto, extraia todas as avaliações visíveis e faça um pós-filtro em images.length > 0.
Passo 8 — Paginar através das avaliações
O Home Depot pagina avaliações através de /p/reviews/<slug>/<productId>/<page>. Dois padrões funcionam:
Padrão A — dirigir o controle visível "Próximo" dentro da mesma sessão, útil quando o estado de ordenação/filtro foi aplicado via Passo 7 e deve persistir entre as páginas:
bash
for page in $(seq 1 5); do
scrapeless-scraping-browser --session-id $SESSION eval '/* extrair esquema de avaliação */' \
> "reviews-page-$page.json"
# Clique no controle de paginação "Próximo" visível
scrapeless-scraping-browser --session-id $SESSION eval '
(() => {
const next = [...document.querySelectorAll("a, button")]
.find((el) => /próximo/i.test(el.getAttribute("aria-label") || el.textContent || ""));
if (!next) return false;
next.scrollIntoView({ block: "center" });
next.click();
return true;
})()
'
scrapeless-scraping-browser --session-id $SESSION wait 1500
scrapeless-scraping-browser --session-id $SESSION wait "#reviews, [data-component*='Review' i]"
done
Padrão B — paginação de URL direta com uma nova sessão por página, útil para extração paralela em larga escala:
bash
PRODUCT_ID="326716329"
SLUG="NextWall-31-35-sq-ft-Off-White-Faux-Shiplap-Vinyl-Paintable-Peel-and-Stick-Wallpaper-Roll-PP10000"
for page in $(seq 1 10); do
bash
SID=$(scrapeless-scraping-browser new-session \
--name "hd-reviews-p$page" --ttl 300 --proxy-country US --json \
| jq -r '.data.taskId')
URL="https://www.homedepot.com/p/reviews/$SLUG/$PRODUCT_ID/$page"
scrapeless-scraping-browser --session-id $SID open "$URL"
scrapeless-scraping-browser --session-id $SID wait 1500
scrapeless-scraping-browser --session-id $SID wait "#reviews, [data-component*='Review' i]"
scrapeless-scraping-browser --session-id $SID eval '/* review-extraction body */' \
> "reviews-page-$page.json"
scrapeless-scraping-browser stop $SID
done
Uma sessão nova por página, não uma sessão longa. Reutilizar um --session-id é, em teoria, mais eficiente, mas na prática as sessões Scrapeless começam a retornar chrome://new-tab-page/ em branco ou terminam após cerca de 3 solicitações de página hidratada. Sessões de curto TTL por página são mais confiáveis, fáceis de paralelizar e triviais de entender.
Executando trabalhadores paralelos? O daemon padrão
~/.scrapeless-scraping-browser/é compartilhado entre processos no mesmo host — vários trabalhadores podem sequestrar as sessões uns dos outros, mesmo quando cada um passa seu próprio--session-id. O que funciona: encadeie cada chamada CLI para um trabalho como uma única invocação de&&(atômico do ponto de vista do daemon — outros trabalhadores não podem se intercalar entre seus passos), use nomes de sessão únicos (a porta é hash a partir do nome) e limite a concorrência em cerca de 3 trabalhadores por host. Para maior distribuição, shard entre hosts. Consulteskill-dev/SKILL.md"Agentes CLI Paralelos" para o padrão completo.
Remova duplicatas entre páginas por id quando presente; volte a usar reviewer.name + time + title + text quando nenhum ID for exposto. Avaliações fixadas e widgets patrocinados às vezes se repetem entre páginas.
O Que Você Recebe de Volta
O Scraping Browser retorna um DOM ao vivo — o esquema de extração é o que quer que o chamador escreva na etapa eval. Para uma única passagem de página de avaliações com o template de descobrir → extrair do Passo 6, os campos que retornam hoje são assim:
json
// O esquema reflete exatamente o que o eval do Passo 6 emite.
// Os valores dos campos são amostras ilustrativas, não um instantâneo congelado de nenhum produto hoje.
{
"productId": "326716329",
"productUrl": "https://www.homedepot.com/p/reviews/<slug>/326716329/1",
"reviewsReturned": 1,
"reviews": [
{
"id": "abc123",
"title": "Instalação fácil e qualidade sólida",
"text": "O produto instalou-se de forma limpa e funcionou como esperado.",
"rating": 5,
"isRecommended": true,
"badges": ["Comprador Verificado"],
"reviewer": { "name": "ClienteHomeDepot" },
"time": "2024-04-04",
"original_source": { "name": "homedepot.com" },
"images": [],
"total_positive_feedback": 8,
"total_negative_feedback": 0,
"verified": true
}
]
}
O eval do Passo 6 emite productId, productUrl, reviewsReturned e reviews[]. O histograma de avaliação (overallRating, ratings[] por estrela), totalReviews agregado e metadados de nível de produto são emitidos pelo eval JSON-LD do Passo 2 — extraia tanto o Passo 2 quanto o Passo 6 na mesma sessão se um fluxo de trabalho precisar de agregado + corpus juntos. O extrator do Passo 6 pode ser estendido com uma passagem de histograma contra [data-testid*="histogram" i] / [aria-label*="stars" i] linhas, mas isso não está no corpo do eval mostrado acima; adicione explicitamente quando precisar.
Algumas observações honestas sobre essa saída, que vale a pena saber antes de executar em escala:
- Tempos de hidratação. O micro-frontend de avaliações em
assets.thdstatic.com/experiences/paginated-product-reviews/*pinta em ondas: o chrome da página e o H1 primeiro, em seguida, o histograma e os cartões de avaliação. Umwaitcontra um anexo de cartão de avaliação (Passo 6) é o que controla a extração. Se a passagem de descoberta retornar apenas o chrome da página, espere um pouco mais e executeget htmlnovamente antes de apertar os seletores — os cartões geralmente estão a um ciclo de renderização de distância. - Estabilidade do seletor.
[itemprop='review'],[data-testid*='review' i]earia-label*='out of'widgets de estrelas são os ancoradouros de maior duração. Nomes de classe que começam com prefixos hash rotacionam entre implantações. - Presença de histograma. O histograma de avaliação é renderizado inline na página de avaliações, mas usa formas diferentes de DOM entre categorias de produtos. Trate sua ausência como anulável em vez de tentar novamente — para produtos com muito poucas avaliações, pode não ser renderizado de forma alguma.
- Emblema de comprador verificado. Duas superfícies estáveis: um span com estilo de emblema contendo o texto literal "Comprador Verificado" e um
data-testidno wrapper do emblema. Combine qualquer um deles. - Fotos. Avaliações com fotos anexadas expõem elementos
<img>dentro do cartão de avaliação; avaliações sem fotos simplesmente não têm filhos<img>—images.length > 0é um filtro limpo.
- **Interstitials do WAF.** Algumas alocações vão para a página de `Acesso Negado` da Home Depot (`bodyLen` ≪ 1000, sem marcadores de revisão). O script no Passo 6 deve detectar essa página (por exemplo, `if (/Access Denied|Error Page/i.test(document.title)) throw`) e o chamador deve tentar novamente com uma sessão nova.
## Reivindique seu plano gratuito e comece a raspar:
Junte-se à vibrante comunidade do Scrapeless para reivindicar um **plano gratuito** de $5-10 e conectar-se com outros inovadores:
[Comunidade Oficial do Scrapeless no Discord](https://discord.gg/stFPK2xKHY)
[Comunidade Oficial do Scrapeless no Telegram](https://t.me/+uELlyZh2JGw1M2Ux)
---
## FAQ
**Q1: Preciso de um proxy para raspar a Home Depot?**
Sim — 100%. A Home Depot é um site de varejo dos EUA e exibe uma página de erro genérica em egressos não dos EUA. `--proxy-country US` é obrigatório em cada sessão.
**Q2: Onde ficam as opções de preço e disponibilidade — JSON-LD ou DOM renderizado?**
Ambos. O JSON-LD envia o `offers.price`, `offers.priceCurrency` e `offers.availability` canônicos (Passo 2). Sobrescritas de preço em promoção, texto de promoção atual, o selo de disponibilidade por loja, e os botões de conclusão (Enviar para Casa / Retirada / Entrega) são preenchidos após o React hidratar e são extraídos do DOM renderizado (Passo 3).
**Q3: Como eu raspo o estoque por loja?**
Acione o modal do seletor de localização: abra o PDP, clique em "Trocar Loja", preencha o CEP, confirme. Chamadas de avaliação subsequentes na mesma sessão retornarão a visão regional. Veja o Passo 5 para o resumo completo → clique → choreografia de preenchimento. O fallback de configuração de cookie (`THD_LOCSTORE` / `THD_PERSIST`) é documentado ao final do Passo 5 para ambientes onde o modal não é acessível.
**Q4: Por que minha sessão às vezes retorna `ERR_TUNNEL_CONNECTION_FAILED`?**
O pool de proxies não tinha IP residencial disponível no momento da alocação. Crie uma nova sessão e tente novamente em breve.
**Q5: Por que a página às vezes retorna `Acesso Negado`?**
O WAF da Home Depot ocasionalmente desafia uma nova alocação. Crie uma nova sessão e tente novamente. O extractor do Passo 6 deve detectar o título da página de erro e lançar uma exceção para que o chamador possa tentar novamente com uma nova sessão.
**Q6: Como as páginas de busca e categoria paginam?**
Através do deslocamento da URL `Nao` (`?Nao=24`, `?Nao=48`, …) com 24 cartões por página. O Passo 4 cobre o extrator de listagem e o loop de paginação. Páginas de entrada de categoria (`/b/<slug>`) aceitam o mesmo deslocamento.
**Q7: Posso raspar apenas avaliações com fotos?**
Sim. Primeiro, experimente o filtro de avaliação com foto visível (Passo 7). Se esse controle estiver ausente para um determinado produto, extraia todas as avaliações visíveis e filtre posteriormente com `images.length > 0`.
**Q8: Como consigo as avaliações mais novas ou as de menor classificação?**
Use os controles de classificação da UI dentro da mesma sessão persistente — a choreografia de captura no Passo 7. Clique no controle relevante, aguarde a região de avaliação ser re-renderizada, e então execute novamente a extração `eval`.
**Q09: O que acontece quando a Home Depot muda o DOM?**
Execute novamente a passagem de descoberta: `get html "<region>"`, identifique os ancoradouros estáveis atuais (`data-testid`, `aria-label`, `[itemprop]`, ids semânticos) e ajuste os seletores `eval`. Não use nomes de classe hash como seletores permanentes.
**Q10: Quantos produtos posso coletar em uma sessão?**
Para confiabilidade, mantenha uma sessão do Scrapeless para uma raspagem lógica pequena (alguns PDPs ou algumas páginas de busca). Para trabalhos maiores, crie novas sessões por tarefa e mantenha a concorrência ≤ 3 por host (nota sobre trabalhadores paralelos no Passo 8). Distribua entre hosts para maior fan-out.
**Q11: Isso pode ser executado sem um agente de IA?**
Sim. Os comandos da CLI nos Passos 1–8 funcionam de ponta a ponta como bash. O fluxo de trabalho controlado pelo agente (habilidade + prompts em linguagem natural) é o caminho recomendado porque a habilidade transporta o padrão de descoberta → extração, as pegadinhas da espera e as regras de trabalhadores paralelos para que o prompt permaneça em uma linha.
**Q12: Por que usar uma URL canônica `/p/<slug>/<productId>` em vez de um ID de produto vazio?**
URLs somente com ID vazio como `/p/<productId>` retornaram a página de erro genérica da Home Depot na verificação. A URL canônica do PDP `/p/<slug>/<productId>` e a URL canônica de avaliações `/p/reviews/<slug>/<productId>/<page>` são os alvos confiáveis do navegador; resolva os IDs dos produtos para essas formas antes de abrir a página.
Na Scorretless, acessamos apenas dados disponíveis ao público, enquanto cumprem estritamente as leis, regulamentos e políticas de privacidade do site aplicáveis. O conteúdo deste blog é apenas para fins de demonstração e não envolve atividades ilegais ou infratoras. Não temos garantias e negamos toda a responsabilidade pelo uso de informações deste blog ou links de terceiros. Antes de se envolver em qualquer atividade de raspagem, consulte seu consultor jurídico e revise os termos de serviço do site de destino ou obtenha as permissões necessárias.



