CAPSOLVER
Blog
Cómo resolver Captcha en Maxun con integración de CapSolver

Cómo resolver Captcha en Maxun con integración de CapSolver

Logo of CapSolver

Adélia Cruz

Neural Network Developer

21-Jan-2026

En la extracción de datos web, Maxun está ganando atención como una plataforma de código abierto y sin código que simplifica cómo los equipos recopilan datos de la web. Sus flujos de trabajo basados en robots y SDK permiten a desarrolladores y usuarios no técnicos crear y mantener pipelines de raspado sin esfuerzo de ingeniería.

Eso dicho, muchos sitios web del mundo real están protegidos por CAPTCHAs, que a menudo se convierten en el principal cuello de botella durante la extracción de datos. CapSolver funciona bien junto con Maxun al manejar estos desafíos a nivel de infraestructura. Con CapSolver en su lugar, los robots de Maxun pueden continuar operando en páginas protegidas por CAPTCHA de manera más confiable, combinando facilidad de uso con capacidades prácticas de raspado listas para producción.


¿Qué es Maxun?

Maxun es una plataforma de extracción de datos web de código abierto y sin código que permite a los usuarios entrenar robots para raspar sitios web sin escribir código. Cuenta con un constructor visual de robots, un SDK potente para control programático y admite implementaciones en la nube y autohospedadas.

Características clave de Maxun

  • Constructor de robots sin código: Interfaz visual para entrenar robots de extracción sin programación
  • SDK potente: SDK de TypeScript/Node.js para ejecutar robots de forma programática
  • Múltiples modos de extracción: Capacidad de extraer, raspar, navegar y buscar
  • Selectores inteligentes: Detección automática de elementos y generación de selectores inteligentes
  • Nube y autohospedado: Implementar en Maxun Cloud o en su propia infraestructura
  • Soporte de proxy: Rotación y gestión integrada de proxies
  • Ejecuciones programadas: Automatizar la ejecución de robots con programación basada en cron

Clases principales del SDK

Clase Descripción
Extract Crear flujos de trabajo de extracción de datos estructurados con LLM o selectores CSS
Scrape Convertir páginas web en Markdown, HTML o capturas de pantalla limpias
Crawl Descubrir y raspar múltiples páginas automáticamente usando sitemaps y enlaces
Search Realizar búsquedas en la web y extraer contenido de los resultados (DuckDuckGo)

¿Qué hace que Maxun sea diferente?

Maxun puentes el vacío entre la simplicidad sin código y la flexibilidad del desarrollador:

  • Visual + Código: Entrenar robots visualmente, luego controlarlos de forma programática mediante el SDK
  • Arquitectura basada en robots: Plantillas de extracción reutilizables y compartibles
  • TypeScript nativo: Construido para aplicaciones modernas de Node.js
  • De código abierto: Transparencia total y desarrollo basado en la comunidad

¿Qué es CapSolver?

CapSolver es un servicio líder para resolver CAPTCHAs que proporciona soluciones basadas en inteligencia artificial para superar diversos desafíos de CAPTCHA. Con soporte para múltiples tipos de CAPTCHA y tiempos de respuesta rápidos, CapSolver se integra sin problemas en flujos de trabajo automatizados.

Tipos de CAPTCHA soportados

CapSolver ayuda a los flujos de trabajo de automatización a manejar la mayoría de los CAPTCHAs y desafíos de verificación comunes durante la extracción de datos y la automatización del navegador, incluyendo:

¿Por qué integrar CapSolver con Maxun?

Al crear robots de Maxun que interactúan con sitios web protegidos—ya sea para extracción de datos, monitoreo de precios o investigación de mercado—los desafíos de CAPTCHA se convierten en un obstáculo significativo. Estos son los motivos por los que la integración es importante:

  1. Extracción de datos ininterrumpida: Los robots pueden completar sus misiones sin intervención manual
  2. Operaciones escalables: Manejar desafíos de CAPTCHA en múltiples ejecuciones concurrentes de robots
  3. Flujo de trabajo sin interrupciones: Resolver CAPTCHAs como parte de su pipeline de extracción
  4. Costo efectivo: Pagar solo por CAPTCHAs resueltos con éxito
  5. Altas tasas de éxito: Alta precisión líder en la industria para todos los tipos de CAPTCHA soportados

Instalación

Requisitos previos

Instalación del SDK de Maxun

bash Copy
# Instalar SDK de Maxun
npm install maxun-sdk

# Instalar dependencias adicionales para la integración con CapSolver
npm install axios

Instalación con Docker (Maxun autohospedado)

bash Copy
# Clonar el repositorio de Maxun
git clone https://github.com/getmaxun/maxun.git
cd maxun

# Iniciar con Docker Compose
docker-compose up -d

Configuración del entorno

Crear un archivo .env con su configuración:

env Copy
CAPSOLVER_API_KEY=your_capsolver_api_key
MAXUN_API_KEY=your_maxun_api_key

# Para Maxun Cloud (app.maxun.dev)
MAXUN_BASE_URL=https://app.maxun.dev/api/sdk

# Para Maxun autohospedado (por defecto)
# MAXUN_BASE_URL=http://localhost:8080/api/sdk

Nota: La configuración baseUrl es requerida al usar Maxun Cloud. Las instalaciones autohospedadas usan por defecto http://localhost:8080/api/sdk.


Creando un servicio de CapSolver para Maxun

Aquí hay un servicio TypeScript reutilizable que integra CapSolver con Maxun:

Servicio básico de CapSolver

typescript Copy
import axios, { AxiosInstance } from 'axios';

interface TaskResult {
  gRecaptchaResponse?: string;
  token?: string;
  cookies?: Array<{ name: string; value: string }>;
  userAgent?: string;
}

interface CapSolverConfig {
  apiKey: string;
  timeout?: number;
  maxAttempts?: number;
}

class CapSolverService {
  private client: AxiosInstance;
  private apiKey: string;
  private maxAttempts: number;

  constructor(config: CapSolverConfig) {
    this.apiKey = config.apiKey;
    this.maxAttempts = config.maxAttempts || 60;

    this.client = axios.create({
      baseURL: 'https://api.capsolver.com',
      timeout: config.timeout || 30000,
      headers: { 'Content-Type': 'application/json' },
    });
  }

  private async createTask(taskData: Record<string, unknown>): Promise<string> {
    const response = await this.client.post('/createTask', {
      clientKey: this.apiKey,
      task: taskData,
    });

    if (response.data.errorId !== 0) {
      throw new Error(`Error de CapSolver: ${response.data.errorDescription}`);
    }

    return response.data.taskId;
  }

  private async getTaskResult(taskId: string): Promise<TaskResult> {
    for (let attempt = 0; attempt < this.maxAttempts; attempt++) {
      await this.delay(2000);

      const response = await this.client.post('/getTaskResult', {
        clientKey: this.apiKey,
        taskId,
      });

      const { status, solution, errorDescription } = response.data;

      if (status === 'ready') {
        return solution as TaskResult;
      }

      if (status === 'failed') {
        throw new Error(`Tarea fallida: ${errorDescription}`);
      }
    }

    throw new Error('Tiempo de espera agotado esperando la solución del CAPTCHA');
  }

  private delay(ms: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }

  async solveReCaptchaV2(websiteUrl: string, websiteKey: string): Promise<string> {
    const taskId = await this.createTask({
      type: 'ReCaptchaV2TaskProxyLess',
      websiteURL: websiteUrl,
      websiteKey,
    });

    const solution = await this.getTaskResult(taskId);
    return solution.gRecaptchaResponse || '';
  }

  async solveReCaptchaV3(
    websiteUrl: string,
    websiteKey: string,
    pageAction: string = 'submit'
  ): Promise<string> {
    const taskId = await this.createTask({
      type: 'ReCaptchaV3TaskProxyLess',
      websiteURL: websiteUrl,
      websiteKey,
      pageAction,
    });

    const solution = await this.getTaskResult(taskId);
    return solution.gRecaptchaResponse || '';
  }

  async solveTurnstile(
    websiteUrl: string,
    websiteKey: string,
    action?: string,
    cdata?: string
  ): Promise<string> {
    const taskData: Record<string, unknown> = {
      type: 'AntiTurnstileTaskProxyLess',
      websiteURL: websiteUrl,
      websiteKey,
    };

    // Añadir metadatos opcionales
    if (action || cdata) {
      taskData.metadata = {};
      if (action) (taskData.metadata as Record<string, string>).action = action;
      if (cdata) (taskData.metadata as Record<string, string>).cdata = cdata;
    }

    const taskId = await this.createTask(taskData);
    const solution = await this.getTaskResult(taskId);
    return solution.token || '';
  }

  async checkBalance(): Promise<number> {
    const response = await this.client.post('/getBalance', {
      clientKey: this.apiKey,
    });

    return response.data.balance || 0;
  }
}

export { CapSolverService, CapSolverConfig, TaskResult };

★ Insight ─────────────────────────────────────
El servicio de CapSolver utiliza un patrón de sondeo (getTaskResult) porque la resolución de CAPTCHA es asincrónica: la API acepta una tarea, la procesa en sus servidores y devuelve un resultado cuando está listo. El retraso de 2 segundos entre sondeos equilibra la respuesta con los límites de tasa de la API.
─────────────────────────────────────────────────


Resolviendo diferentes tipos de CAPTCHA

reCAPTCHA v2 con Maxun

Como Maxun opera a un nivel más alto que la automatización del navegador en bruto, el enfoque de integración se centra en resolver CAPTCHAs antes o durante la ejecución del robot:

typescript Copy
import { Extract } from 'maxun-sdk';
import { CapSolverService } from './capsolver-service';

const CAPSOLVER_API_KEY = process.env.CAPSOLVER_API_KEY!;
const MAXUN_API_KEY = process.env.MAXUN_API_KEY!;
const MAXUN_BASE_URL = process.env.MAXUN_BASE_URL || 'https://app.maxun.dev/api/sdk';

const capSolver = new CapSolverService({ apiKey: CAPSOLVER_API_KEY });
const extractor = new Extract({
  apiKey: MAXUN_API_KEY,
  baseUrl: MAXUN_BASE_URL,
});

async function extractWithRecaptchaV2() {
  const targetUrl = 'https://example.com/protected-page';
  const recaptchaSiteKey = '6LcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABC';

  console.log('Resolviendo reCAPTCHA v2...');

  // Resolver el CAPTCHA primero
  const token = await capSolver.solveReCaptchaV2(targetUrl, recaptchaSiteKey);

  console.log('CAPTCHA resuelto, creando robot de extracción...');

  // Crear un robot usando encadenamiento de métodos
  const robot = await extractor
    .create('Extractor de Productos')
    .navigate(targetUrl)
    .type('#g-recaptcha-response', token)
    .click('button[type="submit"]')
    .wait(2000)
    .captureList({ selector: '.product-item' });

  // Ejecutar el robot
  const result = await robot.run({ timeout: 30000 });

  console.log('Extracción completa:', result.data);
  return result.data;
}

extractWithRecaptchaV2().catch(console.error);

reCAPTCHA v3 con Maxun

typescript Copy
import { Extract } from 'maxun-sdk';
import { CapSolverService } from './capsolver-service';

const CAPSOLVER_API_KEY = process.env.CAPSOLVER_API_KEY!;
const MAXUN_API_KEY = process.env.MAXUN_API_KEY!;
const MAXUN_BASE_URL = process.env.MAXUN_BASE_URL || 'https://app.maxun.dev/api/sdk';

const capSolver = new CapSolverService({ apiKey: CAPSOLVER_API_KEY });
const extractor = new Extract({
  apiKey: MAXUN_API_KEY,
  baseUrl: MAXUN_BASE_URL,
});

async function extractWithRecaptchaV3() {
  const targetUrl = 'https://example.com/v3-protected';
  const recaptchaSiteKey = '6LcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxDEF';

  console.log('Resolviendo reCAPTCHA v3 con alto puntaje...');

  // Resolver con acción de página personalizada
  const token = await capSolver.solveReCaptchaV3(
    targetUrl,
    recaptchaSiteKey,
    'submit'  // pageAction
  );

  console.log('Token obtenido con alto puntaje, creando robot...');

  // Crear robot de extracción usando encadenamiento de métodos
  const robot = await extractor
    .create('Extractor protegido por v3')
    .navigate(targetUrl)
    .type('input[name="g-recaptcha-response"]', token)
    .click('#submit-btn')
    .wait(2000)
    .captureText({ resultData: '.result-data' });

  const result = await robot.run({ timeout: 30000 });

  console.log('Datos extraídos:', result.data);
  return result.data;
}

extractWithRecaptchaV3().catch(console.error);

Cloudflare Turnstile con Maxun

typescript Copy
import { Scrape } from 'maxun-sdk';
import { CapSolverService } from './capsolver-service';

const CAPSOLVER_API_KEY = process.env.CAPSOLVER_API_KEY!;
const MAXUN_API_KEY = process.env.MAXUN_API_KEY!;
const MAXUN_BASE_URL = process.env.MAXUN_BASE_URL || 'https://app.maxun.dev/api/sdk';

const capSolver = new CapSolverService({ apiKey: process.env.CAPSOLVER_API_KEY! });
const scraper = new Scrape({
  apiKey: MAXUN_API_KEY,
  baseUrl: MAXUN_BASE_URL,
});

async function extractWithTurnstile() {
  const targetUrl = 'https://example.com/turnstile-protected';
  const turnstileSiteKey = '0x4xxxxxxxxxxxxxxxxxxxxxxxxxxxxGHI';

  console.log('Resolviendo Cloudflare Turnstile...');

  // Resolver con metadatos opcionales (acción y cdata)
  const token = await capSolver.solveTurnstile(
    targetUrl,
    turnstileSiteKey,
    'login',                              // acción opcional
    '0000-1111-2222-3333-ejemplo-cdata'  // cdata opcional
  );

  console.log('Turnstile resuelto, creando robot de raspado...');

  // Crear robot de raspado - para Turnstile, normalmente necesitamos
  // enviar el token mediante una solicitud POST primero, luego raspar
  const robot = await scraper.create('raspador-de-turnstile', targetUrl, {
    formats: ['markdown', 'html'],
  });

  const result = await robot.run({ timeout: 30000 });

  console.log('Extracción completa');
  console.log('Markdown:', result.data.markdown?.substring(0, 500));
  return result.data;
}

extractWithTurnstile().catch(console.error);

Integración con flujos de trabajo de Maxun

Uso con la clase Extract

La clase Extract se utiliza para extraer datos estructurados de elementos específicos de una página. Soporta extracción impulsada por LLM (usando prompts de lenguaje natural) y extracción no LLM (usando selectores CSS):

typescript Copy
import { Extract } from 'maxun-sdk';
import { CapSolverService } from './capsolver-service';

const capSolver = new CapSolverService({ apiKey: process.env.CAPSOLVER_API_KEY! });
const extractor = new Extract({
  apiKey: process.env.MAXUN_API_KEY!,
  baseUrl: process.env.MAXUN_BASE_URL || 'https://app.maxun.dev/api/sdk',
});

interface ProductData {
  name: string;
  price: string;
  rating: string;
}

async function extractProductsWithCaptcha(): Promise<ProductData[]> {
const targetUrl = 'https://example.com/products';
  const siteKey = '6LcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABC';

  // Resolver CAPTCHA previamente
  const captchaToken = await capSolver.solveReCaptchaV2(targetUrl, siteKey);

  console.log('CAPTCHA resuelto, creando robot de extracción...');

  // Crear robot de extracción usando encadenamiento de métodos
  const robot = await extractor
    .create('Extractor de Productos')
    .navegar(targetUrl)
    .escribir('#g-recaptcha-response', captchaToken)
    .hacerClic('button[type="submit"]')
    .esperar(3000)
    .capturarLista({
      selector: '.product-card',
      paginación: { tipo: 'hacerClicSiguiente', selector: '.next-page' },
      maxItems: 50,
    });

  // Ejecutar la extracción
  const resultado = await robot.ejecutar({ timeout: 60000 });

  return resultado.data.listaDatos as ProductData[];
}

extraerProductosConCaptcha()
  .then((productos) => {
    productos.forEach((producto) => {
      console.log(`${producto.nombre}: ${producto.precio}`);
    });
  })
  .catch(console.error);

★ Insight ─────────────────────────────────────
El método capturarLista en la clase Extract de Maxun detecta automáticamente los campos dentro de los elementos de lista y maneja la paginación. Cuando especifica un tipo de paginación (hacerClicSiguiente, hacerClicCargarMas), el robot continuará extrayendo hasta alcanzar su límite o agotar las páginas.
─────────────────────────────────────────────────

Uso con la clase Scrape

La clase Scrape convierte páginas web en HTML limpio, Markdown listo para LLM o capturas de pantalla:

typescript Copy
import { Scrape } from 'maxun-sdk';
import { CapSolverService } from './capsolver-service';
import axios from 'axios';

const capSolver = new CapSolverService({ apiKey: process.env.CAPSOLVER_API_KEY! });
const scraper = new Scrape({
  apiKey: process.env.MAXUN_API_KEY!,
  baseUrl: process.env.MAXUN_BASE_URL || 'https://app.maxun.dev/api/sdk',
});

interface ResultadoScrape {
  url: string;
  markdown?: string;
  html?: string;
  captchaResuelto: boolean;
}

async function scrapeConManejoCaptcha(): Promise<ResultadoScrape[]> {
  const urls = [
    'https://example.com/pagina1',
    'https://example.com/pagina2',
    'https://example.com/pagina3',
  ];

  const resultados: ResultadoScrape[] = [];

  for (const url of urls) {
    try {
      // Para páginas protegidas por CAPTCHA, resolver primero y establecer sesión
      const siteKey = '6LcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABC'; // Obtener dinámicamente si es necesario

      console.log(`Resolviendo CAPTCHA para ${url}...`);
      const tokenCaptcha = await capSolver.solveReCaptchaV2(url, siteKey);

      // Enviar CAPTCHA para obtener cookies de sesión
      const respuestaVerificar = await axios.post(`${url}/verificar`, {
        'g-recaptcha-response': tokenCaptcha,
      });

      // Crear robot de scrapeo para la página autenticada
      const robot = await scraper.crear(`escrapador-${Date.now()}`, url, {
        formatos: ['markdown', 'html'],
      });

      const resultado = await robot.ejecutar({ timeout: 30000 });

      resultados.push({
        url,
        markdown: resultado.data.markdown,
        html: resultado.data.html,
        captchaResuelto: true,
      });

      // Limpiar - eliminar robot después de usarlo
      await robot.eliminar();

    } catch (error) {
      console.error(`Fallo al escrapear ${url}:`, error);
      resultados.push({ url, captchaResuelto: false });
    }
  }

  return resultados;
}

scrapeConManejoCaptcha().then(console.log).catch(console.error);

Uso con la clase Crawl

La clase Crawl descubre y escrapea múltiples páginas usando sitemaps y seguimiento de enlaces:

typescript Copy
import { Crawl } from 'maxun-sdk';
import { CapSolverService } from './capsolver-service';
import axios from 'axios';

const capSolver = new CapSolverService({ apiKey: process.env.CAPSOLVER_API_KEY! });
const crawler = new Crawl({
  apiKey: process.env.MAXUN_API_KEY!,
  baseUrl: process.env.MAXUN_BASE_URL || 'https://app.maxun.dev/api/sdk',
});

interface ResultadoPagina {
  url: string;
  titulo: string;
  texto: string;
  cantidadPalabras: number;
}

async function crawlearConProtecciónCaptcha(): Promise<ResultadoPagina[]> {
  const urlInicio = 'https://example.com';
  const siteKey = '6LcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABC';

  // Resolver CAPTCHA previamente para el dominio
  console.log('Resolviendo CAPTCHA para acceso al dominio...');
  const tokenCaptcha = await capSolver.solveReCaptchaV2(urlInicio, siteKey);

  // Enviar CAPTCHA para establecer sesión (específico del sitio)
  await axios.post(`${urlInicio}/verificar`, {
    'g-recaptcha-response': tokenCaptcha,
  });

  // Crear robot de crawleo con configuración de alcance de dominio
  const robot = await crawler.crear('crawleo-del-sitio', urlInicio, {
    modo: 'dominio',           // Alcance de crawleo: 'dominio', 'subdominio' o 'ruta'
    límite: 50,                // Máximo de páginas a crawlear
    profundidadMáxima: 3,      // Qué tan profundo seguir enlaces
    usarSitemap: true,         // Analizar sitemap.xml para URLs
    seguirEnlaces: true,       // Extraer y seguir enlaces de página
    incluirRutas: ['/blog/', '/docs/'],  // Patrones regex a incluir
    excluirRutas: ['/admin/', '/login/'], // Patrones regex a excluir
    respetarRobots: true,      // Honrar robots.txt
  });

  // Ejecutar el crawleo
  const resultado = await robot.ejecutar({ timeout: 120000 });

  // Cada página en resultado contiene: metadata, html, texto, cantidadPalabras, enlaces
  return resultado.data.crawlData.map((pagina: any) => ({
    url: pagina.metadata.url,
    titulo: pagina.metadata.titulo,
    texto: pagina.texto,
    cantidadPalabras: pagina.cantidadPalabras,
  }));
}

crawlearConProtecciónCaptcha()
  .then((páginas) => {
    console.log(`Crawleadas ${páginas.length} páginas`);
    páginas.forEach((página) => {
      console.log(`- ${página.titulo}: ${página.url} (${página.cantidadPalabras} palabras)`);
    });
  })
  .catch(console.error);

Patrón de Autenticación Previa

Para sitios que requieren CAPTCHA antes de acceder al contenido, use un flujo de trabajo de autenticación previa:

typescript Copy
import axios from 'axios';
import { Extract } from 'maxun-sdk';
import { CapSolverService } from './capsolver-service';

const capSolver = new CapSolverService({ apiKey: process.env.CAPSOLVER_API_KEY! });
const extractor = new Extract({
  apiKey: process.env.MAXUN_API_KEY!,
  baseUrl: process.env.MAXUN_BASE_URL || 'https://app.maxun.dev/api/sdk',
});

interface CookiesSesión {
  nombre: string;
  valor: string;
  dominio: string;
}

async function autenticarPrevioConCaptcha(
  urlLogin: string,
  siteKey: string
): Promise<CookiesSesión[]> {
  // Paso 1: Resolver el CAPTCHA
  const tokenCaptcha = await capSolver.solveReCaptchaV2(urlLogin, siteKey);

  // Paso 2: Enviar el token del CAPTCHA para obtener cookies de sesión
  const respuesta = await axios.post(
    urlLogin,
    {
      'g-recaptcha-response': tokenCaptcha,
    },
    {
      withCredentials: true,
      maxRedirects: 0,
      validateStatus: (estado) => estado < 400,
    }
  );

  // Paso 3: Extraer cookies de la respuesta
  const cookiesSet = respuesta.headers['set-cookie'] || [];
  const cookies: CookiesSesión[] = cookiesSet.map((cookie: string) => {
    const [nombreValor] = cookie.split(';');
    const [nombre, valor] = nombreValor.split('=');
    return {
      nombre: nombre.trim(),
      valor: valor.trim(),
      dominio: new URL(urlLogin).hostname,
    };
  });

  return cookies;
}

async function extraerConAutenticaciónPrevia() {
  const urlLogin = 'https://example.com/verificar';
  const urlObjetivo = 'https://example.com/datos-privados';
  const siteKey = '6LcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABC';

  // Autenticar previamente para obtener cookies de sesión
  const cookiesSesión = await autenticarPrevioConCaptcha(urlLogin, siteKey);

  console.log('Sesión establecida, creando robot de extracción...');

  // Crear robot de extracción usando encadenamiento de métodos
  // Nota: Usar setCookies() para pasar cookies de sesión autenticadas
  const robot = await extractor
    .crear('Extractor Autenticado')
    .setCookies(cookiesSesión)
    .navegar(urlObjetivo)
    .esperar(2000)
    .capturarTexto({ contenido: '.contenido-privado' });

  // Ejecutar la extracción
  const resultado = await robot.ejecutar({ timeout: 30000 });

  return resultado.data;
}

extraerConAutenticaciónPrevia().then(console.log).catch(console.error);

★ Insight ─────────────────────────────────────
El patrón de autenticación previa separa la resolución de CAPTCHA de la extracción de datos. Esto es especialmente útil para Maxun porque opera a un nivel de abstracción más alto: en lugar de inyectar tokens en el DOM, establece primero una sesión autenticada, luego deja que los robots de Maxun trabajen dentro de esa sesión.
─────────────────────────────────────────────────


Ejecución Paralela de Robots con Manejo de CAPTCHA

Manejar CAPTCHAs en múltiples ejecuciones concurrentes de robots:

typescript Copy
import { Scrape } from 'maxun-sdk';
import { CapSolverService } from './capsolver-service';

const capSolver = new CapSolverService({ apiKey: process.env.CAPSOLVER_API_KEY! });
const scraper = new Scrape({
  apiKey: process.env.MAXUN_API_KEY!,
  baseUrl: process.env.MAXUN_BASE_URL || 'https://app.maxun.dev/api/sdk',
});

interface TareaExtracción {
  url: string;
  siteKey?: string;
}

interface ResultadoExtracción {
  url: string;
  datos: { markdown?: string; html?: string };
  captchaResuelto: boolean;
  duración: number;
}

async function procesarTarea(tarea: TareaExtracción): Promise<ResultadoExtracción> {
  const tiempoInicio = Date.now();

  let captchaResuelto = false;

  // Resolver CAPTCHA si se proporciona siteKey
  if (tarea.siteKey) {
    console.log(`Resolviendo CAPTCHA para ${tarea.url}...`);
    const token = await capSolver.solveReCaptchaV2(tarea.url, tarea.siteKey);
    captchaResuelto = true;
    // El token se enviaría para establecer sesión antes de escrapear
  }

  // Crear y ejecutar un robot de escrapeo
  const robot = await scraper.crear(`escrapador-${Date.now()}`, tarea.url, {
    formatos: ['markdown', 'html'],
  });

  const resultado = await robot.ejecutar({ timeout: 30000 });

  // Limpiar robot después de usarlo
  await robot.eliminar();

  return {
    url: tarea.url,
    datos: {
      markdown: resultado.data.markdown,
      html: resultado.data.html,
    },
    captchaResuelto,
    duración: Date.now() - tiempoInicio,
  };
}

async function ejecutarExtraccionesParalelas(
  tareas: TareaExtracción[],
  concurrencia: number = 5
): Promise<ResultadoExtracción[]> {
  const resultados: ResultadoExtracción[] = [];
  const fragmentos: TareaExtracción[][] = [];

  // Dividir tareas en fragmentos para controlar la concurrencia
  for (let i = 0; i < tareas.length; i += concurrencia) {
    fragmentos.push(tareas.slice(i, i + concurrencia));
  }

  for (const fragmento of fragmentos) {
    const resultadosFragmento = await Promise.all(fragmento.map(procesarTarea));
    resultados.push(...resultadosFragmento);
  }

  return resultados;
}

// Ejemplo de uso
const tareas: TareaExtracción[] = [
  { url: 'https://sitio1.com/datos', siteKey: '6Lc...' },
  { url: 'https://sitio2.com/datos' },
  { url: 'https://sitio3.com/datos', siteKey: '6Lc...' },
  // ... más tareas
];

ejecutarExtraccionesParalelas(tareas, 5)
  .then((resultados) => {
    const resueltos = resultados.filter((r) => r.captchaResuelto).length;
    console.log(`Completadas ${resultados.length} extracciones, resueltos ${resueltos} CAPTCHAs`);

    resultados.forEach((r) => {
      console.log(`${r.url}: ${r.duración}ms`);
    });
  })
  .catch(console.error);

Buenas Prácticas

1. Manejo de errores con reintentos

typescript Copy
async function resolverConReintento<T>(
  funciónSolver: () => Promise<T>,
  maxReintentos: number = 3
): Promise<T> {
  let últimoError: Error | undefined;

  for (let intento = 0; intento < maxReintentos; intento++) {
    try {
      return await funciónSolver();
    } catch (error) {
      últimoError = error as Error;
      console.warn(`Intento ${intento + 1} fallido: ${últimoError.message}`);

      // Retraso exponencial
      await new Promise((resolver) =>
        setTimeout(resolver, Math.pow(2, intento) * 1000)
      );
    }
  }

  throw new Error(`Se excedió el número máximo de reintentos: ${últimoError?.message}`);
}

// Uso
const token = await resolverConReintento(() =>
  capSolver.solveReCaptchaV2(url, siteKey)
);

2. Gestión de balance

typescript Copy
async function asegurarBalanceSuficiente(minBalance: number = 1.0): Promise<void> {
  const balance = await capSolver.verificarBalance();

  if (balance < minBalance) {
    throw new Error(
      `Balance insuficiente de CapSolver: $${balance.toFixed(2)}. Por favor recargue.`
    );
  }

  console.log(`Balance de CapSolver: $${balance.toFixed(2)}`);
}

// Verificar balance antes de iniciar tareas de extracción
await asegurarBalanceSuficiente(5.0);

3. Almacenamiento en caché de tokens

typescript Copy
interface TokenCaché {
  token: string;
  timestamp: number;
}

class AlmacenamientoCaché {
  private cache = new Map<string, TokenCaché>();
  private ttlMs: number;

  constructor(ttlSegundos: number = 90) {
    this.ttlMs = ttlSegundos * 1000;
  }

  private obtenerClave(dominio: string, siteKey: string): string {
    return `${dominio}:${siteKey}`;
  }

  obtener(dominio: string, siteKey: string): string | null {
    const clave = this.obtenerClave(dominio, siteKey);
    const caché = this.cache.get(clave);

    if (!caché) return null;

    if (Date.now() - caché.timestamp > this.ttlMs) {
      this.cache.delete(clave);
      return null;
    }

    return caché.token;
  }

  establecer(dominio: string, siteKey: string, token: string): void {
    const clave = this.obtenerClave(dominio, siteKey);
    this.cache.set(clave, { token, timestamp: Date.now() });
  }
}

const almacenamientoCaché = new AlmacenamientoCaché(90);

async function obtenerTokenCaché(
  url: string,
  siteKey: string
): Promise<string> {
  const dominio = new URL(url).hostname;

  const caché = almacenamientoCaché.obtener(dominio, siteKey);
  if (caché) {
    console.log('Usando token en caché');
    return caché;
  }

  const token = await capSolver.solveReCaptchaV2(url, siteKey);
  almacenamientoCaché.establecer(dominio, siteKey, token);

  return token;
}

Opciones de Configuración

Maxun admite configuración a través de variables de entorno y opciones del SDK:

Configuración Descripción Predeterminado
MAXUN_API_KEY Su clave de API de Maxun -
MAXUN_BASE_URL URL base de la API de Maxun http://localhost:8080/api/sdk (autohospedado) o https://app.maxun.dev/api/sdk (nube)
CAPSOLVER_API_KEY Su clave de API de CapSolver -
CAPSOLVER_TIMEOUT Tiempo de espera en ms 30000
CAPSOLVER_MAX_ATTEMPTS Máximo de intentos de polling 60

Configuración del SDK

typescript Copy
import { Extract, Scrape, Crawl, Search } from 'maxun-sdk';
import { CapSolverService } from './capsolver-service';

// Para Maxun Cloud (app.maxun.dev)
const MAXUN_BASE_URL = 'https://app.maxun.dev/api/sdk';

// Para Maxun autohospedado (predeterminado)
// const MAXUN_BASE_URL = 'http://localhost:8080/api/sdk';

// Configurar módulos del SDK de Maxun
const extractor = new Extract({
  apiKey: process.env.MAXUN_API_KEY!,
  baseUrl: MAXUN_BASE_URL,
});
const scraper = new Scrape({
  apiKey: process.env.MAXUN_API_KEY!,
  baseUrl: MAXUN_BASE_URL,
});
const crawler = new Crawl({
  apiKey: process.env.MAXUN_API_KEY!,
  baseUrl: MAXUN_BASE_URL,
});
const searcher = new Search({
  apiKey: process.env.MAXUN_API_KEY!,
  baseUrl: MAXUN_BASE_URL,
});

// Configurar CapSolver
const capSolver = new CapSolverService({

Aviso de Cumplimiento: La información proporcionada en este blog es solo para fines informativos. CapSolver se compromete a cumplir con todas las leyes y regulaciones aplicables. El uso de la red de CapSolver para actividades ilegales, fraudulentas o abusivas está estrictamente prohibido y será investigado. Nuestras soluciones para la resolución de captcha mejoran la experiencia del usuario mientras garantizan un 100% de cumplimiento al ayudar a resolver las dificultades de captcha durante el rastreo de datos públicos. Fomentamos el uso responsable de nuestros servicios. Para obtener más información, visite nuestros Términos de Servicio y Política de Privacidad.

Máse

Maxun con integración de CapSolver
Cómo resolver Captcha en Maxun con integración de CapSolver

Una guía práctica para integrar CapSolver con Maxun para el scraping de web en el mundo real. Aprende cómo manejar reCAPTCHA, Cloudflare Turnstile y sitios protegidos por CAPTCHA utilizando flujos de trabajo de pre-autenticación y robot.

web scraping
Logo of CapSolver

Adélia Cruz

21-Jan-2026

Browser4 con integración de CapSolver
Cómo resolver Captcha en Browser4 con integración de CapSolver

Automatización de Browser4 con alta capacidad de procesamiento combinada con CapSolver para el manejo de desafíos CAPTCHA en la extracción de datos web a gran escala.

web scraping
Logo of CapSolver

Aloísio Vítor

21-Jan-2026

¿Qué es un bot de scraping y cómo construir uno
¿Qué es un bot de scraping y cómo construir uno

Aprende qué es un bot de raspado y cómo construir uno para la extracción automatizada de datos. Descubre las mejores herramientas, técnicas de navegación segura y prácticas éticas de raspado.

web scraping
Logo of CapSolver

Emma Foster

15-Jan-2026

Scrapy vs. Selenium
Scrapy vs. Selenium: ¿Cuál es el mejor para tu proyecto de raspado web?

Descubre las fortalezas y diferencias entre Scrapy y Selenium para el web scraping. Aprende qué herramienta se adapta mejor a tu proyecto y cómo manejar desafíos como los CAPTCHAs.

web scraping
Logo of CapSolver

Ethan Collins

14-Jan-2026

Cómo usar Selenium Driverless para el raspado web eficiente
Cómo usar Selenium Driverless para un scraping web eficiente

Aprenda a usar Selenium Driverless para un raspado web eficiente. Esta guía proporciona instrucciones paso a paso para configurar su entorno, escribir su primer script de Selenium Driverless y manejar contenido dinámico. Optimice sus tareas de raspado web evitando las complejidades de la gestión tradicional de WebDriver, haciendo que su proceso de extracción de datos sea más sencillo, rápido y portátil.

web scraping
Logo of CapSolver

Rajinder Singh

14-Jan-2026

Solucionar errores 403 Prohibidos al crawlear sitios web con Python
Resolver errores 403 Prohibido al rastrear sitios web con Python

Aprende cómo superar errores 403 Prohibido al crawlear sitios web con Python. Este guía cubre la rotación de IP, el spoofing de user-agent, la limitación de solicitudes, el manejo de autenticación y el uso de navegadores headless para evadir restricciones de acceso y continuar con el scraping de web con éxito.

web scraping
Logo of CapSolver

Lucas Mitchell

13-Jan-2026