CAPSOLVER
Blog
Cómo resolver un Captcha en Browser4 con la integración de CapSolver

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

Logo of CapSolver

Aloísio Vítor

Image Processing Expert

21-Jan-2026

Para la automatización web, Browser4 (de PulsarRPA ) ha surgido como un motor de navegador rápido y seguro para coroutines, diseñado para la extracción de datos impulsada por IA. Con capacidades que permiten 100k-200k visitas a páginas complejas por máquina por día, Browser4 está construido para escalar a gran escala. Sin embargo, al extraer datos de sitios web protegidos, los desafíos CAPTCHA se convierten en una barrera significativa.

CapSolver proporciona un complemento perfecto a las capacidades de automatización de Browser4, permitiendo a sus agentes navegar a través de páginas protegidas por CAPTCHA de manera fluida. Esta integración combina la automatización de navegadores de alta capacidad de Browser4 con soluciones líderes en la industria para resolver CAPTCHA.


¿Qué es Browser4?

Browser4 es un framework de automatización de navegadores de alto rendimiento construido en Kotlin. Está diseñado para aplicaciones de IA que requieren capacidades de agentes autónomos, alta capacidad de procesamiento y extracción de datos híbrida que combina LLM, algoritmos de aprendizaje automático y enfoques basados en selectores.

Características clave de Browser4

  • Alta capacidad de procesamiento: 100k-200k visitas a páginas complejas por máquina por día
  • Seguro para coroutines: Construido con coroutines de Kotlin para un procesamiento paralelo eficiente
  • Agentes impulsados por IA: Agentes de navegador autónomos capaces de razonar y ejecutar tareas de múltiples pasos
  • Extracción híbrida: Combina la inteligencia de LLM, algoritmos de aprendizaje automático y selectores CSS/XPath
  • Consultas X-SQL: Sintaxis de SQL extendida para la extracción de datos complejos
  • Funciones anti-bot: Rotación de perfiles, soporte de proxies y programación resiliente

Métodos de API principales

Método Descripción
session.open(url) Carga una página y devuelve un PageSnapshot
session.parse(page) Convierte el snapshot en un documento en memoria
driver.selectFirstTextOrNull(selector) Recupera texto del DOM en tiempo real
driver.evaluate(script) Ejecuta JavaScript en el navegador
session.extract(document, fieldMap) Mapea selectores CSS a campos estructurados

¿Qué es CapSolver?

CapSolver es un servicio líder de resolución de CAPTCHA que proporciona soluciones impulsadas por IA para evitar diversos desafíos de CAPTCHA. Con soporte para múltiples tipos de CAPTCHA y tiempos de respuesta rápidos, CapSolver se integra de manera fluida en flujos de trabajo automatizados.

Tipos de CAPTCHA compatibles


¿Por qué integrar CapSolver con Browser4?

Al construir automatización de Browser4 que interactúa con sitios web protegidos, ya sea para extracción de datos, monitoreo de precios o investigación de mercados, los desafíos CAPTCHA se convierten en una barrera significativa. Estas son las razones por las que la integración es importante:

  1. Extracción de alta capacidad sin interrupciones: Mantener 100k+ visitas diarias a páginas sin bloqueos de CAPTCHA
  2. Operaciones escalables: Manejar CAPTCHAS en ejecuciones paralelas de coroutines
  3. Flujo de trabajo sin interrupciones: Resolver CAPTCHAS como parte de su pipeline de extracción
  4. Costo efectivo: Pagar solo por CAPTCHAS resueltas con éxito
  5. Altas tasas de éxito: Precisión líder en la industria para todos los tipos de CAPTCHA compatibles

Instalación

Requisitos previos

Añadir dependencias

Maven (pom.xml):

xml Copy
<dependencies>
    <!-- Browser4/PulsarRPA -->
    <dependency>
        <groupId>ai.platon.pulsar</groupId>
        <artifactId>pulsar-boot</artifactId>
        <version>2.2.0</version>
    </dependency>

    <!-- Cliente HTTP para CapSolver -->
    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
        <version>4.12.0</version>
    </dependency>

    <!-- Análisis JSON -->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.10.1</version>
    </dependency>

    <!-- Coroutines de Kotlin -->
    <dependency>
        <groupId>org.jetbrains.kotlinx</groupId>
        <artifactId>kotlinx-coroutines-core</artifactId>
        <version>1.8.0</version>
    </dependency>
</dependencies>

Gradle (build.gradle.kts):

kotlin Copy
dependencies {
    implementation("ai.platon.pulsar:pulsar-boot:2.2.0")
    implementation("com.squareup.okhttp3:okhttp:4.12.0")
    implementation("com.google.code.gson:gson:2.10.1")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
}

Configuración del entorno

Cree un archivo application.properties:

properties Copy
# Configuración de CapSolver
CAPSOLVER_API_KEY=your_capsolver_api_key

# Configuración de LLM (opcional, para extracción de IA)
OPENROUTER_API_KEY=your_openrouter_api_key

# Configuración de proxy (opcional)
PROXY_ROTATION_URL=your_proxy_url

Creando un servicio de CapSolver para Browser4

Aquí hay un servicio de Kotlin reutilizable que integra CapSolver con Browser4:

Servicio básico de CapSolver

kotlin Copy
import com.google.gson.Gson
import com.google.gson.JsonObject
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import kotlinx.coroutines.delay
import java.util.concurrent.TimeUnit

data class TaskResult(
    val gRecaptchaResponse: String? = null,
    val token: String? = null,
    val cookies: List<Map<String, String>>? = null,
    val userAgent: String? = null
)

class CapSolverService(private val apiKey: String) {
    private val client = OkHttpClient.Builder()
        .connectTimeout(30, TimeUnit.SECONDS)
        .readTimeout(30, TimeUnit.SECONDS)
        .build()

    private val gson = Gson()
    private val baseUrl = "https://api.capsolver.com"
    private val jsonMediaType = "application/json".toMediaType()

    private suspend fun createTask(taskData: Map<String, Any>): String {
        val payload = mapOf(
            "clientKey" to apiKey,
            "task" to taskData
        )

        val request = Request.Builder()
            .url("$baseUrl/createTask")
            .post(gson.toJson(payload).toRequestBody(jsonMediaType))
            .build()

        val response = client.newCall(request).execute()
        val result = gson.fromJson(response.body?.string(), JsonObject::class.java)

        if (result.get("errorId").asInt != 0) {
            throw Exception("Error de CapSolver: ${result.get("errorDescription").asString}")
        }

        return result.get("taskId").asString
    }

    private suspend fun getTaskResult(taskId: String, maxAttempts: Int = 60): TaskResult {
        val payload = mapOf(
            "clientKey" to apiKey,
            "taskId" to taskId
        )

        repeat(maxAttempts) {
            delay(2000)

            val request = Request.Builder()
                .url("$baseUrl/getTaskResult")
                .post(gson.toJson(payload).toRequestBody(jsonMediaType))
                .build()

            val response = client.newCall(request).execute()
            val result = gson.fromJson(response.body?.string(), JsonObject::class.java)

            when (result.get("status")?.asString) {
                "ready" -> {
                    val solution = result.getAsJsonObject("solution")
                    return TaskResult(
                        gRecaptchaResponse = solution.get("gRecaptchaResponse")?.asString,
                        token = solution.get("token")?.asString,
                        userAgent = solution.get("userAgent")?.asString
                    )
                }
                "failed" -> throw Exception("Tarea fallida: ${result.get("errorDescription")?.asString}")
            }
        }

        throw Exception("Tiempo agotado esperando la solución de CAPTCHA")
    }

    suspend fun solveReCaptchaV2(websiteUrl: String, websiteKey: String): String {
        val taskId = createTask(mapOf(
            "type" to "ReCaptchaV2TaskProxyLess",
            "websiteURL" to websiteUrl,
            "websiteKey" to websiteKey
        ))

        val result = getTaskResult(taskId)
        return result.gRecaptchaResponse ?: throw Exception("No se encontró gRecaptchaResponse en la solución")
    }

    suspend fun solveReCaptchaV3(
        websiteUrl: String,
        websiteKey: String,
        pageAction: String = "submit"
    ): String {
        val taskId = createTask(mapOf(
            "type" to "ReCaptchaV3TaskProxyLess",
            "websiteURL" to websiteUrl,
            "websiteKey" to websiteKey,
            "pageAction" to pageAction
        ))

        val result = getTaskResult(taskId)
        return result.gRecaptchaResponse ?: throw Exception("No se encontró gRecaptchaResponse en la solución")
    }

    suspend fun solveTurnstile(
        websiteUrl: String,
        websiteKey: String,
        action: String? = null,
        cdata: String? = null
    ): String {
        val taskData = mutableMapOf(
            "type" to "AntiTurnstileTaskProxyLess",
            "websiteURL" to websiteUrl,
            "websiteKey" to websiteKey
        )

        // Añadir metadatos opcionales
        if (action != null || cdata != null) {
            val metadata = mutableMapOf<String, String>()
            action?.let { metadata["action"] = it }
            cdata?.let { metadata["cdata"] = it }
            taskData["metadata"] = metadata
        }

        val taskId = createTask(taskData)
        val result = getTaskResult(taskId)
        return result.token ?: throw Exception("No se encontró token en la solución")
    }

    suspend fun checkBalance(): Double {
        val payload = mapOf("clientKey" to apiKey)

        val request = Request.Builder()
            .url("$baseUrl/getBalance")
            .post(gson.toJson(payload).toRequestBody(jsonMediaType))
            .build()

        val response = client.newCall(request).execute()
        val result = gson.fromJson(response.body?.string(), JsonObject::class.java)

        return result.get("balance")?.asDouble ?: 0.0
    }
}

Resolviendo diferentes tipos de CAPTCHA

reCAPTCHA v2 con Browser4

kotlin Copy
import ai.platon.pulsar.context.PulsarContexts
import ai.platon.pulsar.skeleton.session.PulsarSession
import kotlinx.coroutines.runBlocking

class ReCaptchaV2Extractor(
    private val capSolver: CapSolverService
) {
    suspend fun extractWithCaptcha(targetUrl: String, siteKey: String): Map<String, Any?> {
        println("Resolviendo reCAPTCHA v2...")

        // Resolver la CAPTCHA primero
        val token = capSolver.solveReCaptchaV2(targetUrl, siteKey)
        println("CAPTCHA resuelta, longitud del token: ${token.length}")

        // Crear sesión y abrir la página
        val session = PulsarContexts.createSession()
        val page = session.open(targetUrl)
        val driver = session.getOrCreateBoundDriver()

        // Inyectar el token en el campo de texto oculto usando la propiedad value (seguro)
        driver?.evaluate("""
            (function() {
                var el = document.querySelector('#g-recaptcha-response');
                if (el) el.value = arguments[0];
            })('$token');
        """)

        // Enviar el formulario
        driver?.evaluate("document.querySelector('form').submit();")

        // Esperar la navegación
        Thread.sleep(3000)

        // Extraer datos de la página resultante
        val document = session.parse(page)

        mapOf(
            "título" to document.selectFirstTextOrNull("h1"),
            "contenido" to document.selectFirstTextOrNull(".contenido"),
            "éxito" to (document.body().text().contains("éxito", ignoreCase = true))
        )
    }
}

fun main() = runBlocking {
    val apiKey = System.getenv("CAPSOLVER_API_KEY") ?: "su_api_key"
    val capSolver = CapSolverService(apiKey)

    val extractor = ReCaptchaV2Extractor(capSolver)

    val result = extractor.extractWithCaptcha(
        targetUrl = "https://ejemplo.com/página-protegida",
        siteKey = "6LcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABC"
    )

    println("Resultado de la extracción: $result")
}

reCAPTCHA v3 con Browser4

kotlin Copy
class ReCaptchaV3Extractor(
    private val capSolver: CapSolverService
) {
    suspend fun extractWithCaptchaV3(
        targetUrl: String,
        siteKey: String,
        action: String = "submit"
    ): Map<String, Any?> {
        println("Resolviendo reCAPTCHA v3 con acción: $action")

        // Resolver reCAPTCHA v3 con acción de página personalizada
        val token = capSolver.solveReCaptchaV3(
            websiteUrl = targetUrl,
            websiteKey = siteKey,
            pageAction = action
        )

        println("Token obtenido con éxito")

        // Crear sesión y abrir la página
        val session = PulsarContexts.createSession()
        val page = session.open(targetUrl)
        val driver = session.getOrCreateBoundDriver()

        // Inyectar token en campo oculto (usando asignación segura de valor)
        driver?.evaluate("""
            (function(tokenValue) {
                var input = document.querySelector('input[name="g-recaptcha-response"]');
                if (input) {
                    input.value = tokenValue;
                } else {
                    var hidden = document.createElement('input');
                    hidden.type = 'hidden';
                    hidden.name = 'g-recaptcha-response';
                    hidden.value = tokenValue;
                    var form = document.querySelector('form');
                    if (form) form.appendChild(hidden);
                }
            })('$token');
        """)

        // Hacer clic en el botón de envío
        driver?.evaluate("document.querySelector('#submit-btn').click();")

        Thread.sleep(3000)

        val document = session.parse(page)

        mapOf(
            "resultado" to document.selectFirstTextOrNull(".datos-resultados"),
            "estado" to "éxito"
        )
    }
}

Cloudflare Turnstile con Browser4

kotlin Copy
class TurnstileExtractor(
    private val capSolver: CapSolverService
) {
    suspend fun extractWithTurnstile(targetUrl: String, siteKey: String): Map<String, Any?> {
        println("Resolviendo Cloudflare Turnstile...")

        // Resolver con metadatos opcionales (acción y cdata)

val token = capSolver.solveTurnstile(
targetUrl,
siteKey,
action = "login", // opcional
cdata = "0000-1111-2222-3333-ejemplo" // opcional
)
println("¡Turnstile resuelto!")

Copy
    val session = PulsarContexts.createSession()
    val page = session.open(targetUrl)
    val driver = session.getOrCreateBoundDriver()

    // Inyectar token de Turnstile (usando asignación segura de valores)
    driver?.evaluate("""
        (function(tokenValue) {
            var input = document.querySelector('input[name="cf-turnstile-response"]');
            if (input) input.value = tokenValue;
        })('$token');
    """)

    // Enviar formulario
    driver?.evaluate("document.querySelector('form').submit();")

    Thread.sleep(3000)

    val document = session.parse(page)

    mapOf(
        "title" to document.selectFirstTextOrNull("title"),
        "content" to document.selectFirstTextOrNull("body")?.take(500)
    )
}

}

Copy
---

## Integración con Browser4 X-SQL

Browser4's X-SQL proporciona capacidades poderosas de extracción. Así es como se combina con la resolución de CAPTCHA:

```kotlin
class XSqlCaptchaExtractor(
    private val capSolver: CapSolverService
) {
    suspend fun extractProductsWithCaptcha(
        targetUrl: String,
        siteKey: String
    ): List<Map<String, Any?>> {
        // Resolver CAPTCHA previamente
        val token = capSolver.solveReCaptchaV2(targetUrl, siteKey)

        // Crear sesión y establecer sesión autenticada
        val session = PulsarContexts.createSession()
        val page = session.open(targetUrl)
        val driver = session.getOrCreateBoundDriver()

        driver?.evaluate("""
            (function(tokenValue) {
                var el = document.querySelector('#g-recaptcha-response');
                if (el) el.value = tokenValue;
                document.querySelector('form').submit();
            })('$token');
        """)

        Thread.sleep(3000)

        // Ahora analizar la página y extraer datos de productos
        val document = session.parse(page)

        // Extraer datos de productos usando métodos integrados de sesión
        val products = mutableListOf<Map<String, Any?>>()
        val productElements = document.select(".product-item")

        for ((index, element) in productElements.withIndex()) {
            if (index >= 50) break // LÍMITE 50
            products.add(mapOf(
                "name" to element.selectFirstTextOrNull(".product-name"),
                "price" to element.selectFirstTextOrNull(".price")?.let {
                    """(\d+\.?\d*)""".toRegex().find(it)?.groupValues?.get(1)?.toDoubleOrNull() ?: 0.0
                },
                "rating" to element.selectFirstTextOrNull(".rating")
            ))
        }

        return products.map { row ->
            mapOf(
                "name" to row["name"],
                "price" to row["price"],
                "rating" to row["rating"],
                "image_url" to row["image_url"]
            )
        }
    }
}

Patrón de Autenticación Previa

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

kotlin Copy
import okhttp3.Cookie
import okhttp3.CookieJar
import okhttp3.HttpUrl

class PreAuthenticator(
    private val capSolver: CapSolverService
) {
    data class AuthSession(
        val cookies: Map<String, String>,
        val userAgent: String?
    )

    suspend fun authenticateWithCaptcha(
        loginUrl: String,
        siteKey: String
    ): AuthSession {
        // Resolver CAPTCHA
        val captchaToken = capSolver.solveReCaptchaV2(loginUrl, siteKey)

        // Enviar CAPTCHA para obtener cookies de sesión
        val client = OkHttpClient.Builder()
            .cookieJar(object : CookieJar {
                private val cookies = mutableListOf<Cookie>()

                override fun saveFromResponse(url: HttpUrl, cookieList: List<Cookie>) {
                    cookies.addAll(cookieList)
                }

                override fun loadForRequest(url: HttpUrl): List<Cookie> = cookies
            })
            .build()

        val formBody = okhttp3.FormBody.Builder()
            .add("g-recaptcha-response", captchaToken)
            .build()

        val request = Request.Builder()
            .url(loginUrl)
            .post(formBody)
            .build()

        val response = client.newCall(request).execute()

        // Extraer cookies de la respuesta
        val responseCookies = response.headers("Set-Cookie")
            .associate { cookie ->
                val parts = cookie.split(";")[0].split("=", limit = 2)
                parts[0] to (parts.getOrNull(1) ?: "")
            }

        return AuthSession(
            cookies = responseCookies,
            userAgent = response.request.header("User-Agent")
        )
    }
}

class AuthenticatedExtractor(
    private val preAuth: PreAuthenticator,
    private val capSolver: CapSolverService
) {
    suspend fun extractWithAuth(
        loginUrl: String,
        targetUrl: String,
        siteKey: String
    ): Map<String, Any?> {
        // Autenticar previamente
        val authSession = preAuth.authenticateWithCaptcha(loginUrl, siteKey)
        println("Sesión establecida con ${authSession.cookies.size} cookies")

        // Crear sesión de Browser4
        val session = PulsarContexts.createSession()

        // Configurar sesión con cookies
        val cookieScript = authSession.cookies.entries.joinToString(";") { (k, v) ->
            "$k=$v"
        }

        val page = session.open(targetUrl)
        val driver = session.getOrCreateBoundDriver()

        // Establecer cookies
        driver?.evaluate("document.cookie = '$cookieScript';")

        // Recargar con sesión autenticada
        driver?.evaluate("location.reload();")
        Thread.sleep(2000)

        // Extraer datos
        val document = session.parse(page)

        return mapOf(
            "authenticated" to true,
            "content" to document.selectFirstTextOrNull(".protected-content"),
            "userData" to document.selectFirstTextOrNull(".user-profile")
        )
    }
}

Servicio de OpenRouter para Extracción con IA

Las capacidades de inteligencia artificial de Browser4 se pueden mejorar con OpenRouter, una puerta de enlace unificada para acceder a diversos modelos de lenguaje. Esto permite una extracción inteligente de contenido que se adapta a diferentes estructuras de página.

Servicio de OpenRouter

kotlin Copy
import com.google.gson.Gson
import com.google.gson.JsonObject
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import java.util.concurrent.TimeUnit

data class ChatMessage(val role: String, val content: String)
data class ChatCompletion(val content: String, val model: String, val usage: TokenUsage)
data class TokenUsage(val promptTokens: Int, val completionTokens: Int, val totalTokens: Int)

class OpenRouterService(private val apiKey: String) {
    private val client = OkHttpClient.Builder()
        .connectTimeout(60, TimeUnit.SECONDS)
        .readTimeout(60, TimeUnit.SECONDS)
        .build()

    private val gson = Gson()
    private val baseUrl = "https://openrouter.ai/api/v1"
    private val jsonMediaType = "application/json".toMediaType()

    fun chat(
        messages: List<ChatMessage>,
        model: String = "openai/gpt-4o-mini"
    ): ChatCompletion {
        val payload = mapOf(
            "model" to model,
            "messages" to messages.map { mapOf("role" to it.role, "content" to it.content) }
        )

        val request = Request.Builder()
            .url("$baseUrl/chat/completions")
            .header("Authorization", "Bearer $apiKey")
            .post(gson.toJson(payload).toRequestBody(jsonMediaType))
            .build()

        val response = client.newCall(request).execute()
        val result = gson.fromJson(response.body?.string(), JsonObject::class.java)

        val choice = result.getAsJsonArray("choices")?.get(0)?.asJsonObject
        val content = choice?.getAsJsonObject("message")?.get("content")?.asString ?: ""

        val usage = result.getAsJsonObject("usage")

        return ChatCompletion(
            content = content,
            model = result.get("model")?.asString ?: model,
            usage = TokenUsage(
                promptTokens = usage?.get("prompt_tokens")?.asInt ?: 0,
                completionTokens = usage?.get("completion_tokens")?.asInt ?: 0,
                totalTokens = usage?.get("total_tokens")?.asInt ?: 0
            )
        )
    }

    fun extractStructuredData(html: String, schema: String): String {
        val prompt = """
            Extraiga los siguientes datos del contenido HTML.
            Devuelva SOLO JSON válido que coincida con este esquema: $schema

            HTML:
            ${html.take(4000)}
        """.trimIndent()

        return chat(listOf(ChatMessage("user", prompt))).content
    }

    fun listModels(): List<String> {
        val request = Request.Builder()
            .url("$baseUrl/models")
            .header("Authorization", "Bearer $apiKey")
            .build()

        val response = client.newCall(request).execute()
        val result = gson.fromJson(response.body?.string(), JsonObject::class.java)

        return result.getAsJsonArray("data")?.mapNotNull {
            it.asJsonObject.get("id")?.asString
        } ?: emptyList()
    }
}

Extracción con IA y Resolución de CAPTCHA

Combine la resolución de CAPTCHA con la extracción inteligente de datos:

kotlin Copy
class SmartExtractor(
    private val capSolver: CapSolverService,
    private val openRouter: OpenRouterService
) {
    suspend fun extractWithAI(
        targetUrl: String,
        siteKey: String?,
        extractionPrompt: String
    ): Map<String, Any?> {
        // Paso 1: Resolver CAPTCHA si es necesario
        val captchaToken = siteKey?.let {
            println("Resolviendo CAPTCHA...")
            capSolver.solveReCaptchaV2(targetUrl, it)
        }

        // Paso 2: Crear sesión y abrir página
        val session = PulsarContexts.createSession()
        val page = session.open(targetUrl)
        val driver = session.getOrCreateBoundDriver()

        captchaToken?.let { token ->
            driver?.evaluate("""
                (function(tokenValue) {
                    var el = document.querySelector('#g-recaptcha-response');
                    if (el) el.value = tokenValue;
                    var form = document.querySelector('form');
                    if (form) form.submit();
                })('$token');
            """)
            Thread.sleep(3000)
        }

        // Paso 3: Extraer contenido de la página
        val document = session.parse(page)
        val pageContent = document.body().text().take(8000)

        // Paso 4: Usar IA para extraer datos estructurados
        val llmResponse = openRouter.chat(listOf(
            ChatMessage("system", "Eres un asistente de extracción de datos. Extraiga datos estructurados de páginas web."),
            ChatMessage("user", """
                $extractionPrompt

                Contenido de la página:
                $pageContent
            """.trimIndent())
        ))

        println("LLM usó ${llmResponse.usage.totalTokens} tokens")

        return mapOf(
            "url" to targetUrl,
            "captchaSolved" to (captchaToken != null),
            "extractedData" to llmResponse.content,
            "tokensUsed" to llmResponse.usage.totalTokens
        )
    }
}

// Uso
fun main() = runBlocking {
    val capSolver = CapSolverService(System.getenv("CAPSOLVER_API_KEY")!!)
    val openRouter = OpenRouterService(System.getenv("OPENROUTER_API_KEY")!!)

    val extractor = SmartExtractor(capSolver, openRouter)

    val result = extractor.extractWithAI(
        targetUrl = "https://example.com/products",
        siteKey = "6LcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABC",
        extractionPrompt = """
            Extraiga todos los productos con:
            - nombre
            - precio (como número)
            - disponibilidad (en_stock/fuera_de_stock)
            - calificación (1-5)
            Devuélvalos como matriz JSON.
        """.trimIndent()
    )

    println("Resultado de extracción: ${result["extractedData"]}")
}

Generación de Selectores Adaptativos

Utilice LLM para generar selectores CSS para estructuras de página desconocidas:

kotlin Copy
class AdaptiveExtractor(
    private val capSolver: CapSolverService,
    private val openRouter: OpenRouterService
) {
    suspend fun extractWithAdaptiveSelectors(
        targetUrl: String,
        siteKey: String?,
        dataFields: List<String>
    ): Map<String, Any?> {
        // Resolver CAPTCHA primero
        val token = siteKey?.let { capSolver.solveReCaptchaV2(targetUrl, it) }

        val session = PulsarContexts.createSession()
        val page = session.open(targetUrl)
        val driver = session.getOrCreateBoundDriver()

        token?.let { t ->
            driver?.evaluate("""
                (function(tokenValue) {
                    var el = document.querySelector('#g-recaptcha-response');
                    if (el) el.value = tokenValue;
                })('$t');
            """)
        }

        // Obtener estructura HTML de la página
        val htmlSample = driver?.evaluate("document.body.innerHTML")?.toString()?.take(5000) ?: ""

        // Pedir al LLM que genere selectores
        val selectorPrompt = """
            Analice este HTML y proporcione selectores CSS para estos campos: ${dataFields.joinToString(", ")}

            Muestra de HTML:
            $htmlSample

            Devuelva JSON como: {"fieldName": "css-selector", ...}
        """.trimIndent()

        val selectorsJson = openRouter.chat(listOf(ChatMessage("user", selectorPrompt))).content
        val selectors = Gson().fromJson(selectorsJson, Map::class.java) as Map<String, String>

        // Extraer usando selectores generados
        val document = session.parse(page)
        val extractedData = selectors.mapValues { (_, selector) ->
            document.selectFirstTextOrNull(selector)
        }

        return mapOf(
            "url" to targetUrl,
            "selectors" to selectors,
            "data" to extractedData
        )
    }
}

Extracción Paralela con Corrutinas

El diseño seguro para corrutinas de Browser4 permite un manejo eficiente de CAPTCHA en paralelo:

kotlin Copy
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel

data class ExtractionJob(
    val url: String,
    val siteKey: String?
)

data class ExtractionResult(
    val url: String,
    val data: Map<String, Any?>?,
    val captchaSolved: Boolean,
    val error: String?,
    val duration: Long
)

class ParallelExtractor(
    private val capSolver: CapSolverService,
    private val concurrency: Int = 5
) {
    suspend fun extractAll(jobs: List<ExtractionJob>): List<ExtractionResult> = coroutineScope {
        val channel = Channel<ExtractionJob>(Channel.UNLIMITED)
val results = mutableListOf<ExtractionResult>()

        // Enviar todos los trabajos al canal
        jobs.forEach { channel.send(it) }
        channel.close()

        // Procesar con concurrencia limitada
        val workers = (1..concurrency).map { workerId ->
            async {
                val workerResults = mutableListOf<ExtractionResult>()
                // Cada trabajador crea su propia sesión para seguridad de subprocesos
                val workerSession = PulsarContexts.createSession()

                for (job in channel) {
                    val startTime = System.currentTimeMillis()
                    var captchaSolved = false

                    try {
                        // Resolver CAPTCHA si se proporciona la clave del sitio
                        val token = job.siteKey?.let {
                            captchaSolved = true
                            capSolver.solveReCaptchaV2(job.url, it)
                        }

                        // Extraer datos
                        val page = workerSession.open(job.url)

                        token?.let { t ->
                            val driver = workerSession.getOrCreateBoundDriver()
                            driver?.evaluate("""
                                (function(tokenValue) {
                                    var el = document.querySelector('#g-recaptcha-response');
                                    if (el) el.value = tokenValue;
                                })('$t');
                            """)
                        }

                        val document = workerSession.parse(page)

                        workerResults.add(ExtractionResult(
                            url = job.url,
                            data = mapOf(
                                "title" to document.selectFirstTextOrNull("title"),
                                "h1" to document.selectFirstTextOrNull("h1")
                            ),
                            captchaSolved = captchaSolved,
                            error = null,
                            duration = System.currentTimeMillis() - startTime
                        ))
                    } catch (e: Exception) {
                        workerResults.add(ExtractionResult(
                            url = job.url,
                            data = null,
                            captchaSolved = captchaSolved,
                            error = e.message,
                            duration = System.currentTimeMillis() - startTime
                        ))
                    }
                }

                workerResults
            }
        }

        workers.awaitAll().flatten()
    }
}

// Uso
fun main() = runBlocking {
    val capSolver = CapSolverService(System.getenv("CAPSOLVER_API_KEY")!!)

    val extractor = ParallelExtractor(capSolver, concurrency = 5)

    val jobs = listOf(
        ExtractionJob("https://site1.com/data", "6Lc..."),
        ExtractionJob("https://site2.com/data", null),
        ExtractionJob("https://site3.com/data", "6Lc..."),
    )

    val results = extractor.extractAll(jobs)

    val solved = results.count { it.captchaSolved }
    println("Completado ${results.size} extracciones, resueltos $solved CAPTCHAs")

    results.forEach { r ->
        println("${r.url}: ${r.duration}ms - ${r.error ?: "éxito"}")
    }
}

Buenas prácticas

1. Manejo de errores con reintentos

kotlin Copy
suspend fun <T> withRetry(
    maxRetries: Int = 3,
    initialDelay: Long = 1000,
    block: suspend () -> T
): T {
    var lastException: Exception? = null

    repeat(maxRetries) { attempt ->
        try {
            return block()
        } catch (e: Exception) {
            lastException = e
            println("Intento ${attempt + 1} fallido: ${e.message}")
            delay(initialDelay * (attempt + 1))
        }
    }

    throw lastException ?: Exception("Se excedió el número máximo de reintentos")
}

// Uso
val token = withRetry(maxRetries = 3) {
    capSolver.solveReCaptchaV2(url, siteKey)
}

2. Gestión de balance

kotlin Copy
suspend fun ensureSufficientBalance(
    capSolver: CapSolverService,
    minBalance: Double = 1.0
) {
    val balance = capSolver.checkBalance()

    if (balance < minBalance) {
        throw Exception("Saldo insuficiente de CapSolver: $${"%.2f".format(balance)}. Por favor recargue.")
    }

    println("Saldo de CapSolver: $${"%.2f".format(balance)}")
}

3. Almacenamiento en caché de tokens

kotlin Copy
class TokenCache(private val ttlMs: Long = 90_000) {
    private data class CachedToken(val token: String, val timestamp: Long)

    private val cache = mutableMapOf<String, CachedToken>()

    private fun getKey(domain: String, siteKey: String) = "$domain:$siteKey"

    fun get(domain: String, siteKey: String): String? {
        val key = getKey(domain, siteKey)
        val cached = cache[key] ?: return null

        if (System.currentTimeMillis() - cached.timestamp > ttlMs) {
            cache.remove(key)
            return null
        }

        return cached.token
    }

    fun set(domain: String, siteKey: String, token: String) {
        val key = getKey(domain, siteKey)
        cache[key] = CachedToken(token, System.currentTimeMillis())
    }
}

// Uso con caché
class CachedCapSolver(
    private val capSolver: CapSolverService,
    private val cache: TokenCache = TokenCache()
) {
    suspend fun solveReCaptchaV2Cached(websiteUrl: String, websiteKey: String): String {
        val domain = java.net.URL(websiteUrl).host

        cache.get(domain, websiteKey)?.let {
            println("Usar token en caché")
            return it
        }

        val token = capSolver.solveReCaptchaV2(websiteUrl, websiteKey)
        cache.set(domain, websiteKey, token)

        return token
    }
}

Opciones de configuración

Configuración Descripción Predeterminado
CAPSOLVER_API_KEY Su clave de API de CapSolver -
OPENROUTER_API_KEY Clave de API de OpenRouter para funciones de LLM -
PROXY_ROTATION_URL URL del servicio de rotación de proxies -
Browser4 usa application.properties para configuración adicional

Conclusión

La integración de CapSolver con Browser4 crea una combinación poderosa para la extracción de datos web a gran escala. La arquitectura segura para corutinas y las capacidades de rendimiento extremo de Browser4, combinadas con la resolución confiable de CAPTCHAs de CapSolver, permiten la extracción a escala.

Patrones clave de integración:

  1. Inyección de token directa: Inyectar tokens resueltos mediante la evaluación de JavaScript
  2. Autenticación previa: Resolver CAPTCHAs para establecer sesiones antes de la extracción
  3. Procesamiento paralelo: Aprovechar corutinas para manejar CAPTCHAs concurrentes
  4. Integración con X-SQL: Combinar resolución de CAPTCHAs con el lenguaje de consulta potente de Browser4

Ya sea que esté construyendo sistemas de monitoreo de precios, flujos de investigación de mercado o plataformas de agregación de datos, la combinación Browser4 + CapSolver proporciona la confiabilidad y escalabilidad necesarias para entornos de producción.


¿Listo para comenzar? Regístrese en CapSolver y use el código de bonificación BROWSER4 para obtener un 6% adicional en su primer recargo!


Preguntas frecuentes

¿Qué es Browser4?

Browser4 es un marco de automatización de navegadores de alto rendimiento y seguro para corutinas de PulsarRPA. Está escrito en Kotlin y está diseñado para la extracción de datos impulsada por IA, admitiendo 100.000 a 200.000 visitas a páginas complejas por máquina por día.

¿Cómo se integra CapSolver con Browser4?

CapSolver se integra con Browser4 a través de una clase de servicio que resuelve CAPTCHAs mediante la API de CapSolver. Los tokens resueltos luego se inyectan en las páginas usando las capacidades de evaluación de JavaScript de Browser4 (driver.evaluate()).

¿Qué tipos de CAPTCHAs puede resolver CapSolver?

CapSolver admite reCAPTCHA v2, reCAPTCHA v3, Cloudflare Turnstile, Cloudflare Challenge (5 segundos), AWS WAF, GeeTest v3/v4 y muchos más.

¿Cuánto cuesta CapSolver?

CapSolver ofrece precios competitivos según el tipo y volumen de CAPTCHAs resueltos. Visite capsolver.com para conocer los precios actuales. Use el código BROWSER4 para obtener un 6% de bonificación.

¿Qué lenguaje de programación utiliza Browser4?

Browser4 está construido en Kotlin y funciona en la JVM (Java 17+). También se puede utilizar desde aplicaciones Java.

¿Puede Browser4 manejar la resolución de CAPTCHAs en paralelo?

Sí. El diseño seguro para corutinas de Browser4 permite un procesamiento eficiente en paralelo. Combinado con la API de CapSolver, puede resolver múltiples CAPTCHAs concurrentemente en diferentes trabajos de extracción.

¿Cómo encuentro la clave del CAPTCHA?

La clave del sitio normalmente se encuentra en la fuente HTML de la página:

  • reCAPTCHA: atributo data-sitekey en el elemento .g-recaptcha
  • Turnstile: atributo data-sitekey en el elemento .cf-turnstile
  • O revise las solicitudes de red para encontrar la clave en llamadas a la API

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