Cómo resolver Captcha en Browser4 con integración de 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
- reCAPTCHA v2 (Imagen e Invisible)
- reCAPTCHA v3
- Cloudflare Turnstile
- Cloudflare Challenge (5s)
- AWS WAF
- Y muchos más...
¿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:
- Extracción de alta capacidad sin interrupciones: Mantener 100k+ visitas diarias a páginas sin bloqueos de CAPTCHA
- Operaciones escalables: Manejar CAPTCHAS en ejecuciones paralelas de coroutines
- Flujo de trabajo sin interrupciones: Resolver CAPTCHAS como parte de su pipeline de extracción
- Costo efectivo: Pagar solo por CAPTCHAS resueltas con éxito
- Altas tasas de éxito: Precisión líder en la industria para todos los tipos de CAPTCHA compatibles
Instalación
Requisitos previos
- Java 17 o superior
- Maven 3.6+ o Gradle
- Una clave de API de CapSolver

Añadir dependencias
Maven (pom.xml):
xml
<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
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
# 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
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
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
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
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!")
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)
)
}
}
---
## 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
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
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
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
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
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
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
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
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:
- Inyección de token directa: Inyectar tokens resueltos mediante la evaluación de JavaScript
- Autenticación previa: Resolver CAPTCHAs para establecer sesiones antes de la extracción
- Procesamiento paralelo: Aprovechar corutinas para manejar CAPTCHAs concurrentes
- 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-sitekeyen el elemento.g-recaptcha - Turnstile: atributo
data-sitekeyen 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

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.

Adélia Cruz
21-Jan-2026

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.

Aloísio Vítor
21-Jan-2026

¿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.

Emma Foster
15-Jan-2026

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.

Ethan Collins
14-Jan-2026

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.

Rajinder Singh
14-Jan-2026

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.

Lucas Mitchell
13-Jan-2026


