Scraping inteligente con IA: calificación automática de leads

Equipo AgentesNexo · redactado con asistencia de IA
··7 min lectura

El problema que resuelve este pipeline

Buena parte del tiempo de un equipo comercial en LATAM se va en tres actividades que no requieren criterio humano: buscar empresas objetivo, verificar que existen y tienen actividad real, y decidir si vale la pena contactarlas. Un agente IA puede hacer las tres en paralelo y a escala.

Este artículo documenta el pipeline que usamos en AgentesNexo para calificar leads de forma automática antes de que cualquier persona del equipo los vea.

Requisitos previos

  • Node.js 20+ con TypeScript
  • Playwright 1.45+ (scraping headless)
  • Acceso a la API de Claude (Sonnet para calificación masiva)
  • PostgreSQL o SQLite como base de datos
  • Variables de entorno: ANTHROPIC_API_KEY, DATABASE_URL, BRAVE_API_KEY

El pipeline funciona en local o como servicio. En producción lo corremos en un Mac mini M4 que actúa como servidor de agentes 24/7.

Paso 1: Extraer datos estructurados de directorios

El primer agente recorre directorios de empresas (Google Maps Business, Clutch, directorios de cámaras de comercio LATAM) y extrae campos estructurados usando Playwright más Claude como parser de HTML.

typescript
import { chromium } from 'playwright'
import Anthropic from '@anthropic-ai/sdk'

const client = new Anthropic()

async function extractBusinessData(url: string): Promise<BusinessRecord> {
  const browser = await chromium.launch({ headless: true })
  const page = await browser.newPage()

  await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 })
  const html = await page.content()
  await browser.close()

  const response = await client.messages.create({
    model: 'claude-sonnet-4-5',
    max_tokens: 1024,
    messages: [{
      role: 'user',
      content: #43a047">`Extrae del siguiente HTML los datos de la empresa en JSON:
{
  "name": string,
  "sector": string,
  "employees_range": "1-10|11-50|51-200|200+",
  "country": string,
  "website": string | null,
  "description": string (max 120 chars)
}
Solo devuelve el JSON, sin texto adicional.
HTML: ${html.slice(0, 8000)}`
    }]
  })

  return JSON.parse(response.content[0].text)
}

El ritmo de extracción depende de cada directorio y de las pausas que necesites para no ser bloqueado. Si necesitas escala, lanza 4-6 workers en paralelo con el mismo código.

Paso 2: Enriquecer con señales de compra recientes

Con los datos básicos extraídos, el segundo agente busca señales de actividad: contrataciones recientes, expansión, tecnologías en uso. Combinamos la API de Brave Search con el sitio web de la empresa.

typescript
async function enrichLead(record: BusinessRecord): Promise<EnrichedLead> {
  const searchQuery = #43a047">`site:${record.website} OR "${record.name}" contratacion tecnologia 2026`

  const searchRes = await fetch(
    #43a047">`https://api.search.brave.com/res/v1/web/search?q=${encodeURIComponent(searchQuery)}&count=5`,
    { headers: { 'X-Subscription-Token': process.env.BRAVE_API_KEY! } }
  )
  const results = await searchRes.json()
  const snippets = results.web?.results?.map((r: any) => r.description).join('\n') ?? ''

  const enrichment = await client.messages.create({
    model: 'claude-sonnet-4-5',
    max_tokens: 512,
    messages: [{
      role: 'user',
      content: #43a047">`Empresa: ${record.name} (${record.sector}, ${record.country})
Descripcion: ${record.description}
Actividad reciente: ${snippets}

Devuelve JSON:
{
  "has_tech_stack": boolean,
  "growth_signals": boolean,
  "budget_indicator": "low|medium|high",
  "notes": string (max 80 chars)
}`
    }]
  })

  return { ...record, enrichment: JSON.parse(enrichment.content[0].text) }
}

Agregar este paso le da a la calificación señales recientes que los datos estáticos del directorio no tienen.

Paso 3: Calificación automática contra el ICP

Este es el núcleo del sistema. Se define el Ideal Customer Profile una sola vez y el agente aplica el mismo criterio a cada lead, sin la fatiga que afecta a una persona entre el lead 1 y el lead 800.

typescript
const ICP_PROMPT = #43a047">`
Eres un agente de calificacion de leads para una empresa de automatizacion con IA en LATAM.
Calificamos empresas B2B con estas caracteristicas:
- 10 o mas empleados
- Sectores objetivo: salud, logistica, finanzas, retail, servicios profesionales
- Usan CRM, ERP u otras herramientas SaaS (indica presupuesto tecnologico)
- Muestran senales de crecimiento: contrataciones, expansion, nuevos productos

NO calificamos: freelancers, startups pre-revenue, gobierno, educacion publica.

Para cada lead devuelve JSON:
{
  "score": 0-100,
  "tier": "A|B|C|descarte",
  "reason": string (max 100 chars),
  "next_action": "call|email|linkedin|discard"
}`

async function qualifyLead(lead: EnrichedLead): Promise<QualifiedLead> {
  const response = await client.messages.create({
    model: 'claude-sonnet-4-5',
    max_tokens: 256,
    system: ICP_PROMPT,
    messages: [{ role: 'user', content: JSON.stringify(lead) }]
  })

  return { ...lead, qualification: JSON.parse(response.content[0].text) }
}

En producción vemos esta distribución típica: 15-20% tier A, 30-35% tier B y el resto entre C y descarte. El equipo comercial trabaja solo con A y B; los C se guardan para reclasificación en 30 días.

Paso 4: Pipeline orquestado con rate limiting

El pipeline final encadena los tres agentes, aplica rate limiting para no saturar las APIs y escribe resultados directamente en la base de datos o los envía al CRM vía webhook.

typescript
function chunks<T>(arr: T[], size: number): T[][] {
  return Array.from({ length: Math.ceil(arr.length / size) }, (_, i) =>
    arr.slice(i * size, i * size + size)
  )
}

async function runPipeline(urls: string[]) {
  const results: QualifiedLead[] = []

  for (const batch of chunks(urls, 10)) {
    const extracted = await Promise.all(batch.map(extractBusinessData))
    const enriched  = await Promise.all(extracted.map(enrichLead))
    const qualified = await Promise.all(enriched.map(qualifyLead))

    const prioritarios = qualified.filter(l => ['A', 'B'].includes(l.qualification.tier))
    await saveToCRM(prioritarios)   // tier A y B van al CRM
    await saveToDatabase(qualified) // todo a BD para auditoría

    results.push(...qualified)
    await new Promise(r => setTimeout(r, 1000)) // rate limit: 1s entre batches
  }

  return results
}

Costo: con los precios de lista de Claude Sonnet 4.5 (US$3 por millón de tokens de entrada y US$15 por millón de tokens de salida), el paso más caro es el primero, porque envía hasta 8.000 caracteres de HTML por empresa. Antes de escalar, mide el costo real con un lote de prueba.

Arquitectura del sistema

[Directorios web / CSV de leads crudos]
         |
[Agente 1: Scraping con Playwright + Claude como parser]
         |
[Agente 2: Enriquecimiento — Brave Search + señales de compra]
         |
[Agente 3: Calificación automática contra ICP]
         |
[BD PostgreSQL — todos los leads con score y tier]
         |
[CRM / WhatsApp / Email — solo tier A y B]

El sistema corre como servicio Node.js y se activa con un cron diario o por trigger manual. Un Mac mini M4 o un VPS modesto alcanza para correrlo; el límite real lo ponen los rate limits de las APIs y de los directorios.

Lo que aprendimos en producción

  1. El prompt del ICP es el activo más valioso. Dos horas refinando los criterios de calificación dan mejor resultado que una semana optimizando el scraper. Claude es consistente cuando el ICP queda preciso.
  1. El enriquecimiento con búsqueda web mejora la calificación. Sin este paso, la calificación trabaja solo con datos estáticos del directorio. Las señales de actividad reciente son el mejor predictor de interés.
  1. Tier B no es descarte. Un lead B de hoy puede ser A dentro de unos meses. Guardar todo en BD y reclasificar cada 30 días con el mismo agente cuesta poco y mantiene el pipeline lleno sin nuevo scraping.
  1. Rate limiting crítico: 10 paralelos + 1s de pausa. Sin sleep entre batches, Playwright genera timeouts y Claude devuelve errores 529. Con esa configuración el procesamiento se mantiene estable.
  1. Separar extracción de calificación. Si cambia el ICP, solo se reprocesa la calificación con los datos ya en BD. No hay que volver a ejecutar el scraper.

En resumen

  • Pipeline de 3 agentes para automatización con IA: scraping, enriquecimiento y calificación automática de leads
  • Reduce el trabajo manual de prospección antes del primer contacto humano
  • El costo depende sobre todo del HTML que se envía al modelo: mídelo con un lote de prueba
  • Tiempo de implementación: 1-2 días con el código de esta guía como base
  • El activo clave es el prompt del ICP definido con precisión
  • Escala por lotes sin cambiar el código

Si quieres implementar esto en tu negocio sin construirlo desde cero, en AgentesNexo lo hacemos por ti. Escríbenos en agentesnexo.com o por WhatsApp al +591 67564218

Si quieres implementar esto en tu negocio sin construirlo desde cero, en AgentesNexo lo hacemos por ti. Escríbenos en agentesnexo.com o por WhatsApp al +591 67564218

Articulos relacionados