🎯 Um navegador em nuvem personalizável e anti-detecção alimentado por Chromium desenvolvido internamente, projetado para rastreadores web e agentes de IA. 👉Experimente agora
De volta ao blog

Como Extrair Dados de Produtos da Home Depot Com um Navegador de Agente

Ava Wilson
Ava Wilson

Expert in Web Scraping Technologies

04-May-2026

Principais Conclusões:

  • Uma busca PDP, esquema de produto completo. O HTML da PDP da Home Depot incorpora um bloco JSON-LD Product no 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 URL Nao. 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 html primeiro contra âncoras semânticas (data-testid, aria-label, [itemprop], ids semânticos), depois escreva seletores eval contra o que realmente é renderizado. Nomes de classe se alteram; âncoras semânticas não.

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-id em 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 ao grep é 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 Copy
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 Copy
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 Copy
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.json ou SCRAPELESS_API_KEY e 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 seletores eval com base no que está realmente renderizado.
  • Armadilhas de espera para Home Depotwait 1500 entre open e qualquer espera de seletor para evitar a corrida da sessão fria chrome://new-tab-page/; wait '<review-anchor>' contra um elemento de cartão de revisão em vez de um networkidle geral, 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 (@e1 do snapshot -i).
  • Trabalhadores CLI em paralelo — encadeamento de && em um único shell, nomes de sessão exclusivos, ≤3 trabalhadores simultâneos por host.
  • Erros comunseval retorna valores entre aspas JSON, open sai 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):

  1. Resolva 204279858 para o URL canônico do PDP /p/<slug>/204279858.
  2. Crie uma sessão de egressão dos EUA (--proxy-country US); tente novamente uma vez em os error 10054 / 503 transientes.
  3. Abra o URL do PDP, espere 1500, depois espere 'h1' para evitar a corrida de sessão fria.
  4. eval contra script[type="application/ld+json"] para analisar o esquema Product incorporado e retornar as 10 principais avaliações + agregados.
  5. 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 Copy
{
  "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.


Crie uma sessão de egressão dos EUA.

bash Copy
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 Copy
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 Copy
PRODUCT_ID="204279858"

Sure, here is the translation to Portuguese:

plaintext Copy
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.

Copy
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 Copy
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 Copy
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 Copy
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 / @e25 no trecho são marcadores — Scrapeless snapshot -i atribui 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 de get html — estes correspondem às convenções gerais de data-testid da Home Depot, mas devem ser ajustados para o que a página renderiza.

bash Copy
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 Copy
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 Copy
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);

Copy
return JSON.stringify({ productId, productUrl: location.href, reviewsReturned: reviews.length, reviews });

})()
'

Copy
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 Copy
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 Copy
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 Copy
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. Consulte skill-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 Copy
// 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. Um wait contra 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 execute get html novamente 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] e aria-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-testid no 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.
Copy
- **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.

Artigos mais populares

Catálogo