Agentes LangChain que Veem a Web Ao Vivo: Construindo Pipelines de Dados AI com Scrapeless
Web Data Collection Specialist
Principais Conclusões:
- Integração de LangChain de primeira mão. O pacote
langchain-scrapelessno PyPI fornece cinco ferramentas prontas para uso —ScrapelessDeepSerpGoogleSearchTool,ScrapelessDeepSerpGoogleTrendsTool,ScrapelessUniversalScrapingTool,ScrapelessCrawlerCrawlTooleScrapelessCrawlerScrapeTool— 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
PydanticOutputParsere, opcionalmente, incorpore em um armazenamento vetorial para RAG a jusante. - O agente decide quando raspar. O
create_agentdo 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 deProduto/Artigo/ListaDeEmpregonos 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
Produtocom 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
ScrapelessDeepSerpGoogleSearchToolparametrizado porglehl. - Acompanhamento de tendências de mercado. Extraia
interest_over_timee consultas relacionadas comScrapelessDeepSerpGoogleTrendsToolpara 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
ScrapelessCrawlerScrapeToole 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) esessionName— disponível ao acessar diretamente o endpoint WSS; as chamadas da ferramentalangchain-scrapelessalocam uma nova sessão porinvoke, o que é o padrão certo para um pipeline de pesquisa. - Integração de primeira parte com LangChain —
pip install langchain-scrapelessexpõ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 comlangchain-anthropic,langchain-google-genai,langchain-ollamaou qualquer modelo de chat do LangChain trocando a linhaChatOpenAI. - Familiaridade básica com
pipevenv.
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
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
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
# 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 é:
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):
- Chame
ScrapelessDeepSerpGoogleSearchToolcomq="melhores máquinas de espresso portáteis abaixo de 150",gl="us",hl="en",num=5para obter URLs de resultados orgânicos. - Para os 3 principais URLs de resultado, chame
ScrapelessUniversalScrapingToolcomresponse_type="markdown"para renderizar cada página como markdown limpo. - Passe cada página renderizada ao LLM com um
PydanticOutputParservinculado a um esquemaProduct; rejeite qualquer página onde o parser não consiga extrairnomeepreço. - Agregue os três registros
Productem um array JSON e retorne.
O que você recebe de volta:
json
[
{
"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
┌──────────────────────────────────────────────────────────────────────┐
│ 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
# 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
# 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
# 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
# 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
# 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
@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
# 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
# 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
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
# 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
[
{
"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
domcontentloadedpor 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 pararesponse_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
nullcomo 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 wrapperlangchain-scrapelesslevantaValueErrorem 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.



