🎯 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

Agentes LangChain que Veem a Web Ao Vivo: Construindo Pipelines de Dados AI com Scrapeless

Isabella Garcia
Isabella Garcia

Web Data Collection Specialist

05-May-2026

Principais ConclusÔes:

  • Integração de LangChain de primeira mĂŁo. O pacote langchain-scrapeless no PyPI fornece cinco ferramentas prontas para uso — ScrapelessDeepSerpGoogleSearchTool, ScrapelessDeepSerpGoogleTrendsTool, ScrapelessUniversalScrapingTool, ScrapelessCrawlerCrawlTool e ScrapelessCrawlerScrapeTool — que podem ser usadas diretamente em um agente LangChain ou LangGraph. Sem encadeamentos de subprocessos, sem fiação personalizada de CDP.
  • O pipeline consiste em quatro etapas: Descobrir → Renderizar → Extrair → Armazenar. Pesquise no Google com a ferramenta deep-SERP, renderize qualquer URL com a ferramenta de raspagem universal (Scraping Browser por trĂĄs dos panos), extraia registros tipados com PydanticOutputParser e, opcionalmente, incorpore em um armazenamento vetorial para RAG a jusante.
  • O agente decide quando raspar. O create_agent do LangChain (runtime do LangGraph por trĂĄs dos panos) permite que o LLM escolha entre responder a partir do contexto anterior e chamar a ferramenta Scrapeless — o navegador em nuvem sĂł Ă© ativado quando dados novos sĂŁo realmente necessĂĄrios, o que mantĂ©m custo e latĂȘncia controlados em turnos de rotina.
  • Registros tipados, nĂŁo HTML bruto. Combinar ScrapelessUniversalScrapingTool (resposta em markdown) com um schema Pydantic transforma as pĂĄginas raspadas em registros validados de Produto / Artigo / ListaDeEmprego nos quais o cĂłdigo a jusante pode confiar sem a necessidade de colas de parsing extras.
  • Navegador em nuvem anti-detecção, proxies residenciais em mais de 195 paĂ­ses. O Scrapeless Scraping Browser lida com renderização em JavaScript, saĂ­da de proxy residencial e randomização de impressĂ”es digitais (UA, fuso horĂĄrio, WebGL, canvas) em cada sessĂŁo, de modo que o agente permaneça focado na lĂłgica em vez de na evasĂŁo.

Introdução: Pipelines de dados de IA que veem a web ao vivo

Um LLM bĂĄsico responde a partir de dados de treinamento. Para a maioria dos fluxos de trabalho agentais que realmente funcionam — inteligĂȘncia de preços competitiva, pesquisa de leads, monitoramento de mercado, ingestĂŁo estruturada de notĂ­cias, RAG sobre a web ao vivo — o modelo precisa ver a pĂĄgina agora mesmo. Limites de treinamento, paywalls, SPAs carregadas de forma lenta e renderização personalizada empurram a resposta para um territĂłrio que o LLM nunca viu, e a resposta cita um nĂșmero desatualizado ou recusa educadamente.

LangChain mais um navegador em nuvem Ă© a resposta padrĂŁo. O modelo raciocina; o navegador busca; o agente costura os dois juntos. O ponto de fricção que a maioria das equipes enfrenta estĂĄ abaixo do agente: proxies residenciais, renderização em JavaScript, impressĂŁo digital anti-detecção e ciclo de vida da sessĂŁo precisam ser resolvidos antes que o agente possa fazer algo Ăștil. Playwright direto sobre uma VPN residencial funciona para uma execução em laptop Ășnica; nĂŁo sobrevive a um cronograma de produção.

O Scrapeless Scraping Browser lida com essas quatro preocupaçÔes em nĂ­vel de plataforma, e o pacote langchain-scrapeless no PyPI os expĂ”e como ferramentas nativas do LangChain. Este post orienta a composição dessas ferramentas em um pipeline de dados de IA de quatro etapas Descobrir → Renderizar → Extrair → Armazenar, com um exemplo prĂĄtico de pesquisa competitiva, saĂ­da tipada em Pydantic, concorrĂȘncia controlada e ganchos de observabilidade. Para o mesmo primitivo sobre um protocolo diferente, consulte o post de integração MCP.


O que vocĂȘ pode construir

As cinco ferramentas enviadas pelo langchain-scrapeless cobrem os padrÔes mais comuns de pipeline de dados de IA:

  • InteligĂȘncia de preços competitiva. Pesquise uma categoria, renderize as pĂĄginas dos principais varejistas, extraia um registro tipado de Produto com preço, classificação e contagem de avaliaçÔes.
  • Monitoramento de SERP. Acompanhe o ranking de palavras-chave e a variação de snippets em diferentes regiĂ”es com ScrapelessDeepSerpGoogleSearchTool parametrizado por gl e hl.
  • Acompanhamento de tendĂȘncias de mercado. Extraia interest_over_time e consultas relacionadas com ScrapelessDeepSerpGoogleTrendsTool para dimensionamento de categorias (o acesso ao endpoint Trends depende do seu nĂ­vel de plano Scrapeless).
  • Extração de detalhes de produtos em escala. Insira uma lista de URLs no ScrapelessCrawlerScrapeTool e receba de volta markdown pronto para um extrator LLM.
  • Geração de leads a partir de diretĂłrios. Raspagem de sites de listas de negĂłcios com ScrapelessCrawlerCrawlTool, analise linhas de contato em registros tipados e remova duplicatas por domĂ­nio.
  • IngestĂŁo estruturada de notĂ­cias para RAG. Renderize pĂĄginas de editores para markdown limpo, extraia registros de Artigo, incorpore em um armazenamento vetorial do LangChain e consulte com cadeias aumentadas de recuperação.

Todos os seis pipelines compĂ”em os mesmos primitivos — buscar, renderizar, extrair, armazenar — e o exemplo prĂĄtico abaixo cobre toda a cadeia de ponta a ponta.


Por que o Scrapeless Scraping Browser

O Scrapeless Scraping Browser é um navegador em nuvem anti-detecção e personalizåvel, projetado para raspadores da web e agentes de IA. Para agentes do LangChain especificamente, ele traz:

  • Proxies residenciais em mais de 195 paĂ­ses — consultas geolocalizadas retornam as listagens que um usuĂĄrio local veria, e a rotação Ă© automĂĄtica em cada sessĂŁo.
  • Renderização JavaScript do lado da nuvem — Chromium completo com a pĂĄgina hidratada antes da extração, entĂŁo SPAs, feeds de rolagem infinita e painĂ©is carregados de forma preguiçosa sĂŁo alvos de primeira classe.
  • Fingerprinting anti-detecção em cada sessĂŁo — UA, fuso horĂĄrio, idioma, resolução de tela, WebGL e canvas sĂŁo randomizados por sessĂŁo, com uma API de impressĂŁo digital personalizada para identidades fixas quando a consistĂȘncia Ă© importante.
  • PersistĂȘncia de sessĂŁo na camada do navegador em nuvem via sessionTTL (60–900s) e sessionName — disponĂ­vel ao acessar diretamente o endpoint WSS; as chamadas da ferramenta langchain-scrapeless alocam uma nova sessĂŁo por invoke, o que Ă© o padrĂŁo certo para um pipeline de pesquisa.
  • Integração de primeira parte com LangChain — pip install langchain-scrapeless expĂ”e o navegador em nuvem como ferramentas nativas do LangChain; sem wrappers de subprocesso, sem encanamentos de CDP, sem serializadores personalizados.

Obtenha sua chave de API no plano gratuito em Scrapeless. A integração completa estå documentada em github.com/scrapeless-ai/langchain-scrapeless.

Reivindique seu plano gratuito e comece a extrair:

Junte-se Ă  vibrante comunidade do Scrapeless para reivindicar um plano gratuito de $5-10 e conectar-se com outros inovadores:

Comunidade Oficial do Discord do Scrapeless
Comunidade Oficial do Telegram do Scrapeless


Pré-requisitos

  • Python 3.10 ou mais recente.
  • Uma conta Scrapeless e chave de API — inscreva-se em Scrapeless e copie a chave em ConfiguraçÔes → Gerenciamento de Chaves de API.
  • Uma chave de API de modelo de chat — os exemplos abaixo utilizam OpenAI (OPENAI_API_KEY); o mesmo cĂłdigo de agente funciona com langchain-anthropic, langchain-google-genai, langchain-ollama ou qualquer modelo de chat do LangChain trocando a linha ChatOpenAI.
  • Familiaridade bĂĄsica com pip e venv.

Instalação

A configuração completa consiste em quatro sub-etapas. Cada uma Ă© verificĂĄvel de forma independente, para que vocĂȘ possa pausar e confirmar antes de seguir em frente.

1. Crie um venv e instale os pacotes

bash Copy
python -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate
pip install langchain langchain-scrapeless langchain-openai langgraph pydantic

langchain-scrapeless puxa langchain-core e o SDK Python do Scrapeless como dependĂȘncias transitivas. O pacote meta langchain fornece langchain.agents.create_agent, o moderno runtime de agente ReAct (nĂŁo depreciado); langgraph fornece o runtime subjacente CompiledStateGraph; langchain-openai Ă© o provedor de modelo de chat usado nos exemplos. Troque para langchain-anthropic ou outro provedor, se preferir.

2. Configure suas chaves de API

Exporte ambas as chaves para a sessĂŁo de shell atual:

bash Copy
export SCRAPELESS_API_KEY="sua_chave_api_aqui"
export OPENAI_API_KEY="sua_chave_openai_aqui"

Para uma instalação permanente, adicione as mesmas linhas em ~/.bashrc / ~/.zshrc, ou use um carregador .env (python-dotenv) e carregue o arquivo no inĂ­cio do processo. As ferramentas Scrapeless leem SCRAPELESS_API_KEY do ambiente automaticamente — vocĂȘ nĂŁo deve passĂĄ-la como um argumento de construtor.

3. Verifique a instalação

Um curto teste de fumaça que exerce a ferramenta de pesquisa e imprime um resultado de uma linha. A API do Scrapeless ocasionalmente retorna um 400 transitĂłrio na primeira chamada apĂłs uma inicialização a frio; o teste faz atĂ© trĂȘs tentativas para que uma Ășnica reexecução resgate esses casos:

python Copy
# verify.py
import time
from langchain_scrapeless import ScrapelessDeepSerpGoogleSearchTool

tool = ScrapelessDeepSerpGoogleSearchTool()
for attempt in range(3):
    try:
        result = tool.invoke({"q": "navegador de raspagem scrapeless",
                              "hl": "pt", "gl": "br"})
        print(str(result)[:300])
        break
    except ValueError as e:
        print(f"transitĂłrio (tentativa {attempt + 1}): {e}")
        time.sleep(3)

Execute: python verify.py. Uma execução bem-sucedida imprime uma string de resultados de pesquisa em poucos segundos. Se as trĂȘs tentativas levantarem ValueError com falhou com status 401, a chave da API estĂĄ faltando ou incorreta; verifique novamente echo $SCRAPELESS_API_KEY no mesmo shell. 400s persistentes apĂłs trĂȘs tentativas indicam que a conta pode nĂŁo ter acesso ao endpoint solicitado — veja a FAQ sobre erros transitĂłrios.

4. (Opcional) Fixe as versĂ”es das dependĂȘncias

Para construçÔes reprodutĂ­veis, fixe as versĂ”es que vocĂȘ testou. A combinação resolvida por pip install langchain langchain-scrapeless langchain-openai langgraph pydantic em um ambiente Python 3.12 limpo Ă©:

Copy
langchain==1.2.17
langchain-core==1.3.2
langchain-openai==1.2.1
langchain-scrapeless==0.1.3
langgraph==1.1.10
pydantic==2.13.3
scrapeless==1.1.1

Nota: os metadados do pacote langchain-scrapeless 0.1.3 declaram langchain-core <0.4.0, mas pip resolve para langchain-core 1.3.2 porque langchain-openai o requer; a importação em tempo de execução ainda funciona. Para evitar completamente o aviso do resolvedor, instale langchain-scrapeless e apenas o provedor de modelo de chat que vocĂȘ precisa (por exemplo, nĂŁo use langchain-openai e passe uma instĂąncia de ChatAnthropic em vez disso). LangGraph 1.0 foi lançado em outubro de 2025; a sĂ©rie 1.1.x Ă© a versĂŁo estĂĄvel atual.


Como vocĂȘ realmente usa isso: instrua seu agente

ApĂłs a instalação, vocĂȘ constrĂłi pipelines conversando com o agente — nĂŁo rederivando seletores CSS toda vez que o site-alvo muda seu DOM. O agente controla o loop de descobrir → renderizar → extrair e o LLM escolhe a ferramenta certa para cada interação.

Prompts que vocĂȘ pode colar

VocĂȘ digita O que o agente faz
"Encontre os 5 melhores måquinas de espresso portåteis e retorne nome, preço, avaliação como JSON." Busca no Google, renderiza os principais resultados, extrai um Product[] digitado.
"DĂȘ-me o interesse atual do Google Trends nos Ășltimos 12 meses para banco de dados vetorial nos EUA." Chama ScrapelessDeepSerpGoogleTrendsTool com data_type="interest_over_time" (requer acesso ao nĂ­vel de plano do ponto final de Trends).
"Navegue https://example.com/docs até a profundidade 2 e retorne markdown para cada pågina." Chama ScrapelessCrawlerCrawlTool com limit=....
"Renderize https://www.amazon.com/dp/B08N5WRWNW como markdown." Chama ScrapelessUniversalScrapingTool com response_type="markdown".
"Pesquise por startups sĂ©rie A em fintech 2026, liste as empresas e o tamanho de sua rodada de financiamento." Busca → renderiza → extrai digitado em cadeia automaticamente.
"Pegue a pĂĄgina inicial e a pĂĄgina de preços desses trĂȘs concorrentes de SaaS e resuma as diferenças." Multi-URL ScrapelessCrawlerScrapeTool → resumo do LLM.
"Observe esta pĂĄgina de carreiras da Greenhouse e me diga quais funçÔes correspondem a engenheiro de equipe ou infra." Renderiza → filtro de palavras-chave → linhas JSON.
"Quais sĂŁo os 10 melhores resultados orgĂąnicos para tutorial langchain scrapeless de uma saĂ­da do Reino Unido?" ScrapelessDeepSerpGoogleSearchTool com gl="uk", hl="en".

Exemplo prĂĄtico

VocĂȘ digita:

Encontre as 3 melhores mĂĄquinas de espresso portĂĄteis para viagem abaixo de $150. Para cada uma, retorne nome, preço, avaliação mĂ©dia, contagem de avaliaçÔes e trĂȘs recursos principais. Fixe a pesquisa a uma saĂ­da dos EUA.

O plano do agente (em linguagem simples):

  1. Chame ScrapelessDeepSerpGoogleSearchTool com q="melhores mĂĄquinas de espresso portĂĄteis abaixo de 150", gl="us", hl="en", num=5 para obter URLs de resultados orgĂąnicos.
  2. Para os 3 principais URLs de resultado, chame ScrapelessUniversalScrapingTool com response_type="markdown" para renderizar cada pĂĄgina como markdown limpo.
  3. Passe cada pågina renderizada ao LLM com um PydanticOutputParser vinculado a um esquema Product; rejeite qualquer pågina onde o parser não consiga extrair nome e preço.
  4. Agregue os trĂȘs registros Product em um array JSON e retorne.

O que vocĂȘ recebe de volta:

json Copy
[
  {
    "name": "Wacaco Nanopresso",
    "price": 79.95,
    "rating": 4.7,
    "review_count": 12483,
    "key_features": [
      "Operação manual por bomba de mão",
      "Até 18 bar de pressão de extração",
      "Compatível com café moído ou cåpsulas NS via adaptador"
    ],
    "url": "https://example.com/p/wacaco-nanopresso"
  },
  {
    "name": "Flair NEO Flex",
    "price": 119.00,
    "rating": 4.5,
    "review_count": 2104,
    "key_features": [
      "Acionamento por alavanca, sem eletricidade necessĂĄria",
      "PressĂŁo de nĂ­vel de espresso com porta-filtro sem fundo",
      "DestacĂĄvel para viagem"
    ],
    "url": "https://example.com/p/flair-neo-flex"
  },
  {
    "name": "Outin Nano",
    "price": 129.99,
    "rating": 4.6,
    "review_count": 5871,
    "key_features": [
      "Elemento de aquecimento embutido",
      "Ciclo de auto-limpeza",
      "Carregamento USB-C, ~3 minutos para aquecer"
    ],
    "url": "https://example.com/p/outin-nano"
  }
]
// O esquema reflete exatamente o que o parser da Etapa 4 emite. Os valores dos campos sĂŁo ilustrativos.

Formulando prompts

Formulação Efeito
"Use uma saĂ­da alemĂŁ (gl=de)." Fixa a regiĂŁo do proxy da ferramenta de busca; os resultados retornam o que um usuĂĄrio de Berlim veria.
"Busque como markdown." response_type="markdown" na ferramenta de raspagem universal — contexto LLM mais barato, mais estável em seletores do que HTML.
"Limite a navegação a 25 påginas." limit=25 na ScrapelessCrawlerCrawlTool.
"Ignore påginas que não tenham preço." O parser retorna None para campos ausentes; o agente filtra.
"Execute trĂȘs URLs em paralelo." Entrega ao padrĂŁo de concorrĂȘncia limitada na Etapa 6 abaixo.

As Etapas 1–6 abaixo sĂŁo a referĂȘncia interna. Leia-as uma vez para ver como o padrĂŁo de descobrir → renderizar → extrair → armazenar se compĂ”e; depois confie no agente para aplicĂĄ-lo a qualquer consulta que o operador entregar.


Arquitetura

Copy
┌──────────────────────────────────────────────────────────────────────┐
│  LangChain create_agent  (tempo de execução do LangGraph)             │
│                                                                      │
│  ┌───────────────────────┐     ┌────────────────────────────────┐    │
│  │  Modelo de chat       │ ──â–ș │  Ferramentas (langchain-scrapeless)  │    │
│  │  (OpenAI / Anthropic  │     │   ‱ DeepSerpGoogleSearch       │    │
│  │   / Gemini / Ollama)  │ ◄── │   ‱ UniversalScraping          │    │
│  └───────────────────────┘     │   ‱ CrawlerCrawl               │    │
│            â–Č                   │   ‱ CrawlerScrape              │    │
│            │                   │   ‱ DeepSerpGoogleTrends       │    │
│            │                   └──────────────┬─────────────────┘    │
│            │                                  │                      │
│            │           PydanticOutputParser   │                      │
│            │           (registros tipados)     â–Œ                      │
│            │                ┌──────────────────────────────────┐     │
│            │                │  Navegador em nuvem sem raspagem │     │
│            │                │  ‱ proxies residenciais (195+)    │     │
│            │                │  ‱ impressão digital anti-deteção │     │
│            │                │  ‱ renderização de JS Chromium     │     │
│            │                │  ‱ TTL de sessão 60–900s           │     │
│            │                └──────────────────────────────────┘     │
│            │                                  │                      │
│            └──────────────────────────────────┘                      │
│            registros tipados fluem de volta para o agente              │
└──────────────────────────────────────────────────────────────────────┘
                              │
                              ▌ (opcional)
                  ┌───────────────────────────┐
                  │  Armazenamento vetorial    │
                  │  (Chroma / PGVector / pgvector)  │
                  └───────────────────────────┘

TrĂȘs camadas, separação limpa: o LLM raciocina sobre a conversa, as ferramentas langchain-scrapeless envolvem o navegador em nuvem atravĂ©s de interfaces nativas do LangChain, e o navegador em nuvem lida com todas as preocupaçÔes que nĂŁo envolvem raciocĂ­nio. Cada camada pode ser trocada — modelo de chat, prompt, atĂ© mesmo a ferramenta subjacente — sem reescrever as outras.


Etapa 1 — Definir o esquema de saída tipado

Pydantic Ă© o elemento primĂĄrio que suporta a carga que transforma markdown raspado em algo que o cĂłdigo subsequente pode confiar. Defina o registro-alvo uma vez e vincule-o ao extrator LLM na Etapa 4.

python Copy
# schema.py
from typing import Optional
from pydantic import BaseModel, Field, HttpUrl

class Product(BaseModel):
    name: str = Field(...,
        description="Nome do produto. Sempre obrigatório — use o título da página ou H1 se não houver um nome claro do produto.")
    price: Optional[float] = Field(None, description="Preço numérico em USD; nulo se ausente")
    rating: Optional[float] = Field(None, description="Avaliação mĂ©dia, 0–5; nulo se ausente")
    review_count: Optional[int] = Field(None, description="NĂșmero de avaliaçÔes; nulo se ausente")
    key_features: list[str] = Field(default_factory=list, description="3–5 bullets de recursos curtos")
    url: HttpUrl = Field(..., description="URL canĂŽnica do produto")

Marque cada campo que pode estar ausente como Optional e defina listas vazias por padrĂŁo — intersticiais anti-bot, diferenças de layout regionais e nĂłs DOM hidratados preguiçosamente significam que as pĂĄginas rotineiramente omitem um ou dois campos, e um esquema nĂŁo opcional rejeita linhas que poderiam ser Ășteis. Mantenha name como obrigatĂłrio e forneça uma alternativa em sua descrição (tĂ­tulo da pĂĄgina, H1) para que o extrator nunca retorne null para o campo obrigatĂłrio; essa Ășnica dica permite que o esquema absorva pĂĄginas barulhentas sem levantar erros.


Etapa 2 — Descobrir com ScrapelessDeepSerpGoogleSearchTool

A ferramenta deep-SERP retorna resultados orgĂąnicos do Google para uma consulta, parametrizada por idioma (hl), paĂ­s (gl) e contagem de resultados (num). É o elemento primĂĄrio de descoberta — a busca amplia o universo de URLs antes que vocĂȘ comprometa qualquer orçamento de renderização por pĂĄgina.

python Copy
# discover.py
from langchain_scrapeless import ScrapelessDeepSerpGoogleSearchTool

search = ScrapelessDeepSerpGoogleSearchTool()
results = search.invoke({
    "q": "melhores mĂĄquinas de espresso portĂĄteis 2026 abaixo de 150",
    "hl": "pt",
    "gl": "br",
    "num": 5,
})
print(results)

hl controla o idioma do resultado e gl controla o paĂ­s de saĂ­da — eles sĂŁo as alavancas regionais. Para monitoramento de SERP em diferentes regiĂ”es, execute a mesma consulta com diferentes valores de gl (us, de, jp, br) e compare as listas de resultados. Respostas transitĂłrias de ValueError (HTTP 400/503 encapsuladas pela ferramenta) ou de TimeoutError sĂŁo normais no alto volume de consultas; envolva a chamada com o decorador de tentativas da Etapa 6 antes de escalar.


Etapa 3 — Renderizar com ScrapelessUniversalScrapingTool

A ferramenta universal de scraping Ă© o Scrapeless Scraping Browser. Ela aceita uma URL e retorna a pĂĄgina renderizada como markdown (ou HTML, ou captura de tela). O Markdown Ă© o formato mais barato para alimentar um extrator LLM — ele remove anĂșncios, navegação e estilos inline, deixando apenas o conteĂșdo sobre o qual a pĂĄgina realmente trata.

python Copy
# render.py
from langchain_scrapeless import ScrapelessUniversalScrapingTool

scrape = ScrapelessUniversalScrapingTool()
markdown = scrape.invoke({
    "url": "https://example.com/p/wacaco-nanopresso",
    "response_type": "markdown",
})
print(markdown[:600])

Cada invoke aloca uma nova sessĂŁo de navegador na nuvem, que Ă© a configuração padrĂŁo ideal para um pipeline de pesquisa — sessĂ”es novas por URL sĂŁo mais simples e mais resilientes ao estado anti-bot de cada sessĂŁo. (A reutilização de sessĂŁo entre chamadas via sessionName Ă© um recurso a nĂ­vel de CDP; se o seu fluxo de trabalho precisar de cookies quentes e estado de login entre pĂĄginas, acesse diretamente o navegador na nuvem via o endpoint WSS em vez de usar esta ferramenta do LangChain.) A ferramenta tambĂ©m aceita response_type="html" quando vocĂȘ precisa executar seus prĂłprios seletores, response_type="plaintext" para o contexto LLM mais barato, ou response_type="png" / "jpeg" para pipelines de regressĂŁo visual.


Etapa 4 — Extrair com PydanticOutputParser

O esquema definido na Etapa 1 conecta-se diretamente a uma cadeia da Linguagem de Expressão LangChain (LCEL), que recebe o markdown renderizado e retorna um Product tipado. O parser injeta o esquema JSON no prompt e valida a resposta do LLM em relação a ele.

python Copy
# extract.py
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

from schema import Product

parser = PydanticOutputParser(pydantic_object=Product)

prompt = ChatPromptTemplate.from_messages([
    ("system",
     "VocĂȘ extrai registros de produtos de pĂĄginas web renderizadas.\n"
     "A saĂ­da deve corresponder estritamente a este esquema:\n{format_instructions}"),
    ("human",
     "URL de origem: {url}\n\nMarkdown renderizado:\n{markdown}\n\n"
     "Retorne um registro de Produto. O campo `name` Ă© obrigatĂłrio — use o tĂ­tulo da pĂĄgina "
     "ou H1 se nĂŁo houver um nome de produto claro. Defina SOMENTE os campos opcionais "
     "(preço, classificação, contagem de avaliaçÔes) como nulos quando ausentes."),
]).partial(format_instructions=parser.get_format_instructions())

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
extract_chain = prompt | llm | parser

product = extract_chain.invoke({"url": "https://example.com/p/wacaco-nanopresso",
                                "markdown": "<markdown renderizado da Etapa 3>"})
print(product.model_dump())

Falhas do parser sĂŁo raras quando trĂȘs coisas sĂŁo vĂĄlidas: (1) cada campo incerto Ă© marcado como Optional[...], (2) o prompt informa explicitamente ao modelo que apenas os campos opcionais podem ser null, e (3) cada campo obrigatĂłrio tem uma alternativa em sua descrição (por exemplo, name usa o tĂ­tulo da pĂĄgina ou H1). Com esses trĂȘs aspectos em vigor, o contrato nulo do esquema gerencia campos opcionais ausentes, e a instrução do prompt impede que o LLM nulifique campos obrigatĂłrios em pĂĄginas ruidosas — assim, a cadeia opera de forma limpa sem qualquer invĂłlucro de retry.


Etapa 5 — Compor em um create_agent

O agente conecta as trĂȘs ferramentas e permite que o LLM decida qual delas chamar em resposta a qualquer mensagem do usuĂĄrio. langchain.agents.create_agent Ă© a execução canĂŽnica a partir do langchain 1.2 (substitui o langgraph.prebuilt.create_react_agent obsoleto e usa a mesma grafo de estado LangGraph por trĂĄs).

python Copy
# agent.py
from langchain.agents import create_agent
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langchain_scrapeless import (
    ScrapelessDeepSerpGoogleSearchTool,
    ScrapelessUniversalScrapingTool,
    ScrapelessCrawlerCrawlTool,
)
from tenacity import (retry, stop_after_attempt,
                       wait_exponential, retry_if_exception_type)

# Ferramentas de primeira linha subjacentes
_search   = ScrapelessDeepSerpGoogleSearchTool()
_scrape   = ScrapelessUniversalScrapingTool()
_crawl    = ScrapelessCrawlerCrawlTool()

# O decorador de retry que o agente verĂĄ em cada chamada de ferramenta.
# Ferramentas Scrapeless expÔem erros de API transitórios como ValueError, então esse é o filtro.
_retry = retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=2, max=15),
    retry=retry_if_exception_type(ValueError),
)

def _check(payload: str) -> str:
    # O navegador na nuvem Ă s vezes retorna HTTP 200 com um JSON ERR_ incorporado.
    # Exiba como ValueError para que o decorador @_retry acima entre em ação.
    if isinstance(payload, str) and payload.startswith('{"statusCode"') and "ERR_" in payload:
        raise ValueError(f"Erro do navegador na nuvem: {payload[:200]}")
    return payload

@tool
@_retry
def google_search(q: str, hl: str = "en", gl: str = "us", num: int = 5) -> str:
    """Procure no Google e retorne os principais resultados orgĂąnicos como JSON."""
    return _check(str(_search.invoke({"q": q, "hl": hl, "gl": gl, "num": num})))
python Copy
@ferramenta
@_tentar
def renderizar_pagina(url: str) -> str:
    """Renderiza uma URL com o navegador em nuvem Scrapeless e retorna markdown limpo."""
    return _verificar(_raspar.invocar({"url": url, "response_type": "markdown"}))

@ferramenta
@_tentar
def rastrear_site(url: str, limite: int = 10) -> str:
    """Rastreia um site atĂ© um nĂșmero limitado de pĂĄginas, retornando markdown para cada pĂĄgina."""
    return _verificar(str(_rastrear.invocar({"url": url, "limite": limite})))

llm = ChatOpenAI(modelo="gpt-4o-mini", temperatura=0)

prompt_do_sistema = (
    "VocĂȘ Ă© um agente de pesquisa que constrĂłi conjuntos de dados de produtos tipados. "
    "Dada uma consulta de categoria, vocĂȘ: "
    "(1) chama google_search para as principais URLs orgĂąnicas, "
    "(2) chama renderizar_pagina com cada URL promissora, "
    "(3) extrai um registro de Produto por pĂĄgina usando o esquema, "
    "(4) retorna um array JSON de registros. "
    "Defina gl='us' e hl='en' a menos que o usuårio peça o contrårio. "
    "Se uma pågina não tiver um preço, omita a linha do array final."
)

agente = criar_agente(llm,
                      [google_search, renderizar_pagina, rastrear_site],
                      prompt_do_sistema=prompt_do_sistema)

for pedaço in agente.stream(
    {"messages": [("humano",
                   "Encontre os 3 melhores fabricantes de espresso portĂĄteis abaixo de $150 "
                   "e retorne nome, preço, classificação, contagem_de_avaliaçÔes, principais_recursos, url.")]},
    modo_stream="valores",
):
    pedaço["messages"][-1].imprimir_bonito()

O empilhamento de decoradores @ferramenta + @tentar Ă© a peça fundamental para a produção. As ferramentas langchain-scrapeless envolvem erros subjacentes ScrapelessErrors como ValueError antes de levantar, entĂŁo respostas 400/503 transitĂłrias dentro do loop de chamada de ferramentas do agente sobem como ValueError. Sem a tentativa, uma Ășnica falha transitĂłria derruba toda a execução do agente; com o empilhamento de decoradores, cada chamada de ferramenta tenta atĂ© trĂȘs vezes com retrocesso exponencial e com jitter antes de desistir. (O Runnable.with_retry() do langchain-core retorna um RunnableRetry, que criar_agente nĂŁo aceita — a função decorada com @ferramenta acima Ă© o caminho que produz uma verdadeira StructuredTool dentro do shell de tentativa.)

agente.stream(..., modo_stream="valores") emite o estado completo da mensagem em cada etapa, para que o operador possa observar as chamadas de ferramentas do agente e o raciocĂ­nio intermediĂĄrio em tempo real. Para uma Ășnica resposta final, troque por agente.invoke(...). Para passar o Product[] tipado da Etapa 4 atravĂ©s do agente, exponha a cadeia de extração como outra função @ferramenta e adicione-a Ă  lista — o agente a chamarĂĄ apĂłs cada renderização. O README do langchain-scrapeless ainda demonstra from langgraph.prebuilt import create_react_agent; esse caminho funciona, mas emite um aviso LangGraphDeprecatedSinceV10, entĂŁo a importação moderna acima Ă© o caminho recomendado.


Etapa 6 — Fortalecimento da produção

Um script de pesquisa que funciona em trĂȘs URLs em um notebook nĂŁo sobrevive a trinta mil. Quatro padrĂ”es de fortalecimento transformam o pipeline acima em algo que um agendador pode executar de forma nĂŁo assistida.

ConcurrĂȘncia limitada

python Copy
# render_concorrente.py
import asyncio
from langchain_scrapeless import ScrapelessUniversalScrapingTool

raspar = ScrapelessUniversalScrapingTool()
SEM = asyncio.Semaphore(3)            # limite de 3 renderizaçÔes concisas por host

async def renderizar(url: str) -> str:
    async with SEM:
        return await raspar.ainvocar({"url": url, "response_type": "markdown"})

async def renderizar_tudo(urls: list[str]) -> list[str]:
    return await asyncio.gather(*(renderizar(u) for u in urls))

TrĂȘs renderizaçÔes concisas por host Ă© o ponto ideal — alto o suficiente para amortizar o custo de aquecimento por sessĂŁo, baixo o suficiente para permanecer abaixo dos limites de taxa por IP da maioria dos sites. Limite no nĂ­vel do host, nĂŁo globalmente; dez hosts diferentes com trĂȘs trabalhadores cada Ă© aceitĂĄvel, dez trabalhadores todos atingindo o mesmo varejista nĂŁo Ă©.

Tentar em erros transitĂłrios

python Copy
# tentar.py
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
from langchain_scrapeless import ScrapelessUniversalScrapingTool

raspar = ScrapelessUniversalScrapingTool()

def _levantar_em_erro_embutido(payload: str) -> str:
    # O Scrapeless Ă s vezes retorna HTTP 200 com um corpo JSON descrevendo um
    # erro interno do lado do navegador (reset de tĂșnel, ERR_CONNECTION_RESET, ...).
    # A ferramenta não levanta isso; superficializa como ValueError para que a tentativa entre em ação.
    if payload.startswith('{"statusCode"') and "ERR_" in payload:
        raise ValueError(f"Erro do navegador em nuvem: {payload[:200]}")
    return payload

@retry(
    stop=stop_after_attempt(4),
    wait=wait_exponential(multiplier=1, min=2, max=20),
    retry=retry_if_exception_type((ValueError, TimeoutError)),
)
def renderizar_com_tentativa(url: str) -> str:
    return _levantar_em_erro_embutido(
        raspar.invocar({"url": url, "response_type": "markdown"}))

A sessĂŁo do navegador de raspagem ocasionalmente falha em duas formas distintas: uma falha de camada HTTP 400/503 (o wrapper langchain-scrapeless levanta isso como ValueError) e um HTTP 200 com um corpo JSON descrevendo um erro do lado do navegador, como ERR_TUNNEL_CONNECTION_FAILED (o wrapper nĂŁo levanta — o JSON de erro retorna como uma string). O guardiĂŁo _raise_on_embedded_error acima captura a segunda forma e a converte em ValueError, para que a mesma polĂ­tica de repetição se aplique. Com ambas as formas cobertas, o retrocesso exponencial com quatro tentativas cobre o percentil alto dos 90 sem enterrar falhas genuĂ­nas (bloqueios anti-bot, 404s) sob repetiçÔes. Para chamadas dirigidas por agentes, use a pilha de decoradores @tool + @retry da Etapa 5 — as funçÔes de wrapper lĂĄ devem aplicar a mesma verificação _raise_on_embedded_error antes de retornar.

Observabilidade com LangSmith

bash Copy
export LANGCHAIN_TRACING_V2=true
export LANGCHAIN_API_KEY="seu_langsmith_key"
export LANGCHAIN_PROJECT="agente_de_pesquisa_scrapeless"

Com essas trĂȘs variĂĄveis de ambiente definidas, cada chamada de ferramenta, cada chamada de LLM e cada falha de parser aparece no LangSmith com tempo, custo e o prompt exato que o modelo viu. Para uma execução em produção, esta Ă© a mudança de maior alavancagem Ășnica — transforma "o agente fez algo estranho" em uma trilha clicĂĄvel.

Persistir em um armazenamento vetorial

python Copy
# embed.py
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain_core.documents import Document

docs = [Document(page_content=p.model_dump_json(), metadata={"url": str(p.url)})
        for p in products]
vs = Chroma.from_documents(docs, OpenAIEmbeddings(), persist_directory=".chroma")

O armazenamento vetorial Ă© a quarta etapa opcional do pipeline. Ele compensa quando o agente Ă© um serviço RAG de longa duração que responde a perguntas subsequentes sobre o conjunto de dados; Ă© exagero para um script de pesquisa pontual. Escolha o armazenamento com base na preferĂȘncia de operaçÔes — langchain_chroma para arquivos locais, langchain_postgres para um Postgres gerenciado + pgvector, langchain_pinecone para um banco de dados vetorial hospedado.


O que vocĂȘ recebe de volta

json Copy
[
  {
    "name": "Wacaco Nanopresso",
    "price": 79.95,
    "rating": 4.7,
    "review_count": 12483,
    "key_features": [
      "Operação manual com bomba de mão",
      "Até 18 barras de pressão de extração",
      "Compatível com café moído ou cåpsulas NS via adaptador"
    ],
    "url": "https://example.com/p/wacaco-nanopresso"
  },
  {
    "name": "Flair NEO Flex",
    "price": 119.00,
    "rating": 4.5,
    "review_count": 2104,
    "key_features": [
      "Acionamento por alavanca, sem necessidade de eletricidade",
      "PressĂŁo de nĂ­vel de espresso com portafiltre sem fundo",
      "DesmontĂĄvel para viagem"
    ],
    "url": "https://example.com/p/flair-neo-flex"
  },
  {
    "name": "Outin Nano",
    "price": 129.99,
    "rating": 4.6,
    "review_count": 5871,
    "key_features": [
      "Elemento de aquecimento embutido",
      "Ciclo de auto-limpeza",
      "Carregamento USB-C, aquecimento em ~3 minutos"
    ],
    "url": "https://example.com/p/outin-nano"
  }
]
// O esquema reflete exatamente o que o parser da Etapa 4 emite. Os valores dos campos sĂŁo amostras ilustrativas.

Algumas observaçÔes honestas sobre o que esperar quando isso é executado na web ao vivo:

  • O tempo de hidratação varia por site. A ferramenta de raspagem universal aguarda domcontentloaded por padrĂŁo; para SPAs que hidratam preços por meio de um segundo XHR, a marcação pode chegar antes que o preço seja renderizado. Re-renderize uma vez com um breve atraso ou recue para response_type="html" e um seletor personalizado se o campo for consistentemente nulo.
  • Campos opcionais permanecem opcionais. Algumas pĂĄginas de produtos omitem avaliaçÔes explĂ­citas ou contagens de revisĂŁo, especialmente em sites diretos ao consumidor. Trate valores null como informativos em vez de um modo de falha e filtre a jusante.
  • A extração de LLM domina a latĂȘncia. No total, o pipeline leva aproximadamente 1–3 segundos para a chamada de busca, 2–4 segundos por renderização de pĂĄgina e 3–5 segundos por extração de LLM. A concorrĂȘncia nas etapas de renderização e extração Ă© a maior alavanca.
  • Interstitials anti-bot aparecem como ValueError. Quando um site carrega um desafio da Cloudflare ou Akamai que o navegador em nuvem nĂŁo consegue completar de forma transparente, o wrapper langchain-scrapeless levanta ValueError em vez de retornar silenciosamente uma pĂĄgina de espaço reservado. O decorador de repetição captura os casos transitĂłrios; os casos persistentes sĂŁo melhor tratados alargando a impressĂŁo digital ou fixando uma regiĂŁo de proxy diferente.
  • A etapa do armazenamento vetorial Ă© opcional. Para um pipeline de pesquisa que retorna registros tipados para um consumidor a jusante, pule totalmente. Adicione-o quando o mesmo conjunto de dados responder a vĂĄrias perguntas a jusante ao longo do tempo.

FAQ

Preciso de um proxy residencial?
Sim, para qualquer site com proteção anti-bot significativa, que é a maioria dos varejistas, marketplaces e pontos finais de SERP. ScrapelessUniversalScrapingTool e as ferramentas de deep-SERP passam pelo pool de proxies residenciais do Scrapeless por padrão; o parùmetro gl na ferramenta de busca fixa o país de saída.

E quanto a erros transitĂłrios como 400 ou 503?
As ferramentas langchain-scrapeless exibem erros de API transitĂłrios como ValueError (o ScrapelessError subjacente Ă© encapsulado antes de ser re-lançado). Para chamadas diretas, use o decorador tenacity da Etapa 6 com retry_if_exception_type=(ValueError, TimeoutError). Para chamadas impulsionadas por agentes dentro de create_agent, envolva cada ferramenta com a pilha de decoradores @tool + @retry da Etapa 5 — isso produz um verdadeiro StructuredTool que o agente aceita e aplica a polĂ­tica de re-tentativa em cada chamada de ferramenta. Sem um destes, um Ășnico 400 transitĂłrio quebra toda a execução do agente.

Um site retorna Acesso Negado. E agora?
Primeiro, tente novamente com o decorador da Etapa 6. Se a pĂĄgina bloquear persistentemente, amplie a sessĂŁo mudando gl para um paĂ­s diferente ou adicione um breve await asyncio.sleep(...) entre as tentativas para deixar o estado da sessĂŁo esfriar. Para sites com bloqueios consistentes em nĂ­vel de IP, entre em contato com o suporte do Scrapeless para confirmar se o bloqueio Ă© a nĂ­vel de plataforma e nĂŁo a nĂ­vel de conta.

Os seletores continuam quebrando. Como sobrevivo à rotação do DOM?
Use response_type="markdown" no ScrapelessUniversalScrapingTool em vez de analisar HTML com seletores CSS. Markdown colapsa o chrome de navegação e a maior parte da deriva de layout, entĂŁo o extrator LLM na Etapa 4 vĂȘ uma representação estĂĄvel do conteĂșdo mesmo quando o DOM subjacente muda.

Quantos trabalhadores concorrentes por host?
TrĂȘs Ă© o teto documentado para execuçÔes estĂĄveis. Limite ao nĂ­vel do host (asyncio.Semaphore(3) na Etapa 6); trabalhadores em diferentes hosts podem ser executados independentemente.

Posso usar isso sem LangGraph?
Sim. ScrapelessUniversalScrapingTool().invoke({...}) Ă© uma chamada simples — chame-a de qualquer script Python, rota FastAPI ou tarefa Celery. LangGraph adiciona o loop agĂȘncial por cima, mas as ferramentas em si sĂŁo agnĂłsticas em relação ao framework.

Posso trocar OpenAI por Claude, Gemini ou um modelo local?
Sim. Substitua ChatOpenAI(model="gpt-4o-mini") por ChatAnthropic(model="claude-sonnet-4-6"), ChatGoogleGenerativeAI(model="gemini-2.5-pro"), ChatOllama(model="llama3.1"), ou qualquer outro modelo de chat LangChain. A lista tools, o prompt e o parser permanecem inalterados.

Como adiciono memĂłria de mĂșltiplas interaçÔes?
Passe um MemorySaver checkpointer para create_agent(llm, tools, checkpointer=MemorySaver()) e forneça um thread_id em cada invocação. LangGraph persiste o estado da conversa entre as interaçÔes, para que o agente possa se referir a buscas anteriores sem reexecutå-las.

Onde vejo os rastros das requisiçÔes?
Defina LANGCHAIN_TRACING_V2=true, LANGCHAIN_API_KEY e LANGCHAIN_PROJECT (Etapa 6). Cada chamada de ferramenta, chamada LLM e execução de parser aparece no LangSmith com tempos, custos e o prompt exato — a Ășnica mudança de observabilidade com maior impacto para uma implementação em produção.

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