CAPSOLVER
ブログ
ブラウザ4でキャプチャを解く方法とキャップソルバー統合

Captchaをブラウザ4で解く方法とCapSolverの統合

Logo of CapSolver

Sora Fujimoto

AI Solutions Architect

21-Jan-2026

Webオートメーションにおいて、Browser4(PulsarRPAから)は、AI駆動型データ抽出に最適な、高速でコルーチンセーフなブラウザエンジンとして登場しました。1台のマシンで1日あたり10万〜20万件の複雑なページアクセスをサポートするこのブラウザエンジンは、大規模な処理に最適です。しかし、保護されたウェブサイトからデータを抽出する際、CAPTCHAチャレンジが大きな障壁となります。

CapSolverは、Browser4の自動化機能に理想的な補完を提供し、エージェントがCAPTCHA保護されたページをスムーズにナビゲートできるようにします。この統合により、Browser4の高スループットブラウザ自動化と業界をリードするCAPTCHA解決が組み合わされます。


Browser4とは何ですか?

Browser4は、Kotlinで構築された高性能でコルーチンセーフなブラウザ自動化フレームワークです。AIアプリケーションに自律的なエージェント機能、極限のスループット、LLM、機械学習、セレクタベースのアプローチを組み合わせたハイブリッドデータ抽出を必要とするものに設計されています。

Browser4の主な特徴

  • 極限のスループット: 1台のマシンで1日あたり10万〜20万件の複雑なページアクセス
  • コルーチンセーフ: 効率的な並列処理のためのKotlinコルーチンで構築
  • AI駆動エージェント: 複数ステップのタスクを実行できる自律的なブラウザエージェント
  • ハイブリッド抽出: LLMの知能、機械学習アルゴリズム、CSS/XPathセレクタを組み合わせた抽出
  • X-SQLクエリ: 複雑なデータ抽出用の拡張SQL構文
  • アンチボット機能: プロフィールローテーション、プロキシサポート、耐障害性のあるスケジューリング

コアAPIメソッド

メソッド 説明
session.open(url) ページを読み込み、PageSnapshotを返します
session.parse(page) スナップショットをメモリ内ドキュメントに変換します
driver.selectFirstTextOrNull(selector) ライブDOMからテキストを取得します
driver.evaluate(script) ブラウザでJavaScriptを実行します
session.extract(document, fieldMap) CSSセレクタを構造化されたフィールドにマップします

CapSolverとは何ですか?

CapSolverは、さまざまなCAPTCHAチャレンジを回避するAI駆動型ソリューションを提供するリーディングなCAPTCHA解決サービスです。複数のCAPTCHAタイプをサポートし、高速な応答時間を実現し、自動化ワークフローにシームレスに統合できます。

サポートされているCAPTCHAタイプ


Browser4とCapSolverを統合する理由

保護されたウェブサイトとやり取りするBrowser4の自動化を構築する際、データ抽出、価格モニタリング、市場調査など、さまざまな用途においてCAPTCHAチャレンジが大きな障害となります。統合の重要性について以下に説明します。

  1. 中断のない高スループット抽出: CAPTCHAブロックなしで1日あたり10万件以上のページアクセスを維持
  2. スケーラブルな運用: コルーチンの並列実行でCAPTCHAを処理
  3. シームレスなワークフロー: 抽出パイプラインの一環としてCAPTCHAを解決
  4. コスト効率: 成功したCAPTCHAのみに支払い
  5. 高い成功確率: サポートされているすべてのCAPTCHAタイプで業界をリードする精度

インストール

前提条件

依存関係の追加

Maven (pom.xml):

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

    <!-- CapSolver用HTTPクライアント -->
    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
        <version>4.12.0</version>
    </dependency>

    <!-- JSONパーサー -->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.10.1</version>
    </dependency>

    <!-- 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")
}

環境設定

application.propertiesファイルを作成します:

properties Copy
# CapSolver設定
CAPSOLVER_API_KEY=your_capsolver_api_key

# LLM設定 (オプション、AI抽出用)
OPENROUTER_API_KEY=your_openrouter_api_key

# プロキシ設定 (オプション)
PROXY_ROTATION_URL=your_proxy_url

Browser4用のCapSolverサービスの作成

Browser4に統合された再利用可能なKotlinサービスを以下に示します。

基本的な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("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("タスク失敗: ${result.get("errorDescription")?.asString}")
            }
        }

        throw Exception("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("解決にgRecaptchaResponseがありません")
    }

    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("解決にgRecaptchaResponseがありません")
    }

    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
        )

        // オプションのメタデータを追加
        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("解決にトークンがありません")
    }

    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
    }
}

異なるCAPTCHAタイプの解決

Browser4でのreCAPTCHA v2

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("reCAPTCHA v2を解決中...")

        // まずCAPTCHAを解決します
        val token = capSolver.solveReCaptchaV2(targetUrl, siteKey)
        println("CAPTCHAが解決されました、トークンの長さ: ${token.length}")

        // セッションを作成し、ページを開きます
        val session = PulsarContexts.createSession()
        val page = session.open(targetUrl)
        val driver = session.getOrCreateBoundDriver()

        // 安全な値の割り当てを使用して、トークンを隠しテキストエリアに注入します
        driver?.evaluate("""
            (function() {
                var el = document.querySelector('#g-recaptcha-response');
                if (el) el.value = arguments[0];
            })('$token');
        """)

        // フォームを送信します
        driver?.evaluate("document.querySelector('form').submit();")

        // ナビゲーションを待機します
        Thread.sleep(3000)

        // 結果ページからデータを抽出します
        val document = session.parse(page)

        mapOf(
            "title" to document.selectFirstTextOrNull("h1"),
            "content" to document.selectFirstTextOrNull(".content"),
            "success" to (document.body().text().contains("success", ignoreCase = true))
        )
    }
}

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

    val extractor = ReCaptchaV2Extractor(capSolver)

    val result = extractor.extractWithCaptcha(
        targetUrl = "https://example.com/protected-page",
        siteKey = "6LcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxABC"
    )

    println("抽出結果: $result")
}

Browser4でのreCAPTCHA v3

kotlin Copy
class ReCaptchaV3Extractor(
    private val capSolver: CapSolverService
) {
    suspend fun extractWithCaptchaV3(
        targetUrl: String,
        siteKey: String,
        action: String = "submit"
    ): Map<String, Any?> {
        println("アクション: $actionでreCAPTCHA v3を解決中")

        // カスタムページアクションでreCAPTCHA v3を解決します
        val token = capSolver.solveReCaptchaV3(
            websiteUrl = targetUrl,
            websiteKey = siteKey,
            pageAction = action
        )

        println("トークンを取得しました")

        // セッションを作成し、ページを開きます
        val session = PulsarContexts.createSession()
        val page = session.open(targetUrl)
        val driver = session.getOrCreateBoundDriver()

        // 安全な値の割り当てを使用してトークンを隠し入力に注入します
        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');
        """)

        // 送信ボタンをクリックします
        driver?.evaluate("document.querySelector('#submit-btn').click();")

        Thread.sleep(3000)

        val document = session.parse(page)

        mapOf(
            "result" to document.selectFirstTextOrNull(".result-data"),
            "status" to "success"
        )
    }
}

Browser4でのCloudflare Turnstile

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

        // オプションのメタデータ(actionとcdata)で解決します
        val taskId = createTask(mapOf(
            "type" to "AntiTurnstileTaskProxyLess",
            "websiteURL" to targetUrl,
            "websiteKey" to siteKey
        ))

        val result = getTaskResult(taskId)
        return TaskResult(
            token = result.token ?: throw Exception("解決にトークンがありません")
        )
    }
}

val token = capSolver.solveTurnstile(
targetUrl,
siteKey,
action = "login", // オプション
cdata = "0000-1111-2222-3333-example" // オプション
)
println("Turnstileが解決されました!")

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

    // Turnstileトークンを注入(安全な値の割り当てを使用)
    driver?.evaluate("""
        (function(tokenValue) {
            var input = document.querySelector('input[name="cf-turnstile-response"]');
            if (input) input.value = tokenValue;
        })('$token');
    """)

    // 送信
    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
---

## Browser4 X-SQLとの統合

Browser4のX-SQLは強力な抽出機能を提供します。CAPTCHA解決と組み合わせる方法を以下に示します。

```kotlin
class XSqlCaptchaExtractor(
    private val capSolver: CapSolverService
) {
    suspend fun extractProductsWithCaptcha(
        targetUrl: String,
        siteKey: String
    ): List<Map<String, Any?>> {
        // CAPTCHAを事前に解決
        val token = capSolver.solveReCaptchaV2(targetUrl, siteKey)

        // セッションを作成し、認証済みセッションを確立
        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)

        // 今やページを解析し、製品データを抽出
        val document = session.parse(page)

        // 内蔵セッションメソッドを使用して製品データを抽出
        val products = mutableListOf<Map<String, Any?>>()
        val productElements = document.select(".product-item")

        for ((index, element) in productElements.withIndex()) {
            if (index >= 50) break // LIMIT 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"]
            )
        }
    }
}

事前認証パターン

コンテンツにアクセスする前にCAPTCHAを必要とするサイトでは、事前認証ワークフローを使用してください。

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 {
        // CAPTCHAを解決
        val captchaToken = capSolver.solveReCaptchaV2(loginUrl, siteKey)

        // CAPTCHAを送信してセッションクッキーを取得
        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()

        // レスポンスからクッキーを抽出
        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?> {
        // 事前認証
        val authSession = preAuth.authenticateWithCaptcha(loginUrl, siteKey)
        println("セッションが確立されました。${authSession.cookies.size}個のクッキーがあります")

        // Browser4セッションを作成
        val session = PulsarContexts.createSession()

        // セッションにクッキーを設定
        val cookieScript = authSession.cookies.entries.joinToString(";") { (k, v) ->
            "$k=$v"
        }

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

        // クッキーを設定
        driver?.evaluate("document.cookie = '$cookieScript';")

        // 認証済みセッションで再読み込み
        driver?.evaluate("location.reload();")
        Thread.sleep(2000)

        // データを抽出
        val document = session.parse(page)

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

LLM対応抽出のOpenRouter統合

Browser4のAI機能は、OpenRouterを介して強化され、さまざまなLLMモデルにアクセスするための統一APIゲートウェイとして機能します。これにより、ページ構造に応じて適応する知的コンテンツ抽出が可能になります。

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 = """
            次のHTMLコンテンツからデータを抽出してください。
            以下のスキーマに一致する有効なJSONを返してください: $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()
    }
}

CAPTCHA解決とLLMによる抽出の統合

CAPTCHA解決と知的コンテンツ抽出を組み合わせます。

kotlin Copy
class SmartExtractor(
    private val capSolver: CapSolverService,
    private val openRouter: OpenRouterService
) {
    suspend fun extractWithAI(
        targetUrl: String,
        siteKey: String?,
        extractionPrompt: String
    ): Map<String, Any?> {
        // ステップ1: 必要に応じてCAPTCHAを解決
        val captchaToken = siteKey?.let {
            println("CAPTCHAを解決中...")
            capSolver.solveReCaptchaV2(targetUrl, it)
        }

        // ステップ2: セッションを作成し、ページを開く
        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)
        }

        // ステップ3: ページコンテンツを抽出
        val document = session.parse(page)
        val pageContent = document.body().text().take(8000)

        // ステップ4: LLMを使用して構造化データを抽出
        val llmResponse = openRouter.chat(listOf(
            ChatMessage("system", "あなたはデータ抽出アシスタントです。ウェブページから構造化データを抽出してください。"),
            ChatMessage("user", """
                $extractionPrompt

                ページコンテンツ:
                $pageContent
            """.trimIndent())
        ))

        println("LLMが${llmResponse.usage.totalTokens}トークンを使用しました")

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

// 使用例
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 = """
            次の製品をすべて抽出してください:
            - 名前
            - 価格(数値として)
            - 在庫状況(在庫あり/在庫切れ)
            - 評価(1-5)
            JSON配列として返してください。
        """.trimIndent()
    )

    println("抽出結果: ${result["extractedData"]}")
}

逆引きセレクタ生成

LLMを使用して、未知のページ構造用のCSSセレクタを生成します。

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?> {
        // まずCAPTCHAを解決
        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');
            """)
        }

        // ページHTML構造を取得
        val htmlSample = driver?.evaluate("document.body.innerHTML")?.toString()?.take(5000) ?: ""

        // LLMにセレクタを生成させる
        val selectorPrompt = """
            次のHTMLを分析し、これらのフィールドに該当するCSSセレクタを提供してください: ${dataFields.joinToString(", ")}

            HTMLサンプル:
            $htmlSample

            以下のJSON形式で返してください: {"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>

        // 生成されたセレクタを使用して抽出
        val document = session.parse(page)
        val extractedData = selectors.mapValues { (_, selector) ->
            document.selectFirstTextOrNull(selector)
        }

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

コルーチンによる並列抽出

Browser4のコルーチン対応設計により、効率的な並列CAPTCHA処理が可能です。

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>()

        // すべてのジョブをチャネルに送信
        jobs.forEach { channel.send(it) }
        channel.close()

        // 並列処理で処理
        val workers = (1..concurrency).map { workerId ->
            async {
                val workerResults = mutableListOf<ExtractionResult>()
                // 各ワーカーはスレッドセーフのため独自のセッションを作成
                val workerSession = PulsarContexts.createSession()

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

                    try {
                        // サイトキーが提供されている場合CAPTCHAを解決
                        val token = job.siteKey?.let {
                            captchaSolved = true
                            capSolver.solveReCaptchaV2(job.url, it)
                        }

                        // データを抽出
                        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()
    }
}

// 使用例
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("完了した${results.size}件の抽出、${solved}件のCAPTCHAを解決")

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

最適な実践方法

1. エラー処理とリトライ

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("試行${attempt + 1}が失敗: ${e.message}")
            delay(initialDelay * (attempt + 1))
        }
    }

    throw lastException ?: Exception("最大リトライ回数に達しました")
}

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

2. バランス管理

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

    if (balance < minBalance) {
        throw Exception("CapSolverの残高が不足しています: $${"%.2f".format(balance)}. 再充電してください。")
    }

    println("CapSolverの残高: $${"%.2f".format(balance)}")
}

3. トークンキャッシュ

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())
    }
}

// キャッシュを使用する例
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("キャッシュされたトークンを使用")
            return it
        }

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

        return token
    }
}

設定オプション

設定 説明 デフォルト
CAPSOLVER_API_KEY CapSolverのAPIキー -
OPENROUTER_API_KEY LLM機能用のOpenRouter APIキー -
PROXY_ROTATION_URL プロキシローテーションサービスのURL -
Browser4は追加の設定にapplication.propertiesを使用します

結論

CapSolverをBrowser4に統合することで、高スループットのウェブデータ抽出が可能になります。Browser4のコルーチンセーフなアーキテクチャと極限のパフォーマンス能力、CapSolverの信頼性のあるCAPTCHA解決機能を組み合わせることで、スケーラブルな抽出が実現できます。

キーパターン:

  1. トークンの直接挿入: JavaScript評価を通じて解決されたトークンを挿入
  2. 事前認証: 抽出前にセッションを確立するためにCAPTCHAを解決
  3. 並列処理: コルーチンを使って同時CAPTCHA処理を活用
  4. X-SQL統合: CAPTCHA解決をBrowser4の強力なクエリ言語と組み合わせ

価格モニタリングシステム、市場研究パイプライン、データ集約プラットフォームの構築にかかわらず、Browser4 + CapSolverの組み合わせは、本番環境で必要な信頼性とスケーラビリティを提供します。


開始する準備はできましたか? CapSolverに登録 そして、初回チャージで6%のボーナスを獲得するためのボーナスコード BROWSER4 を使ってください!


質問と回答

Browser4とは何ですか?

Browser4は、PulsarRPAから提供される高パフォーマンスでコルーチンセーフなブラウザ自動化フレームワークです。Kotlinで構築され、AI駆動のデータ抽出をサポートし、1台のマシンで1日あたり10万〜20万件の複雑なページアクセスを処理できます。

CapSolverはBrowser4とどのように統合されますか?

CapSolverは、CapSolver APIを介してCAPTCHAを解決するサービスクラスを通じてBrowser4に統合されます。解決されたトークンは、Browser4のJavaScript評価機能(driver.evaluate())を使用してページに挿入されます。

CapSolverが解決できるCAPTCHAの種類は何ですか?

CapSolverはreCAPTCHA v2、reCAPTCHA v3、Cloudflare Turnstile、Cloudflare Challenge(5秒)、AWS WAF、GeeTest v3/v4など、多くの種類のCAPTCHAをサポートしています。

CapSolverの料金はどのくらいですか?

CapSolverは、解決するCAPTCHAの種類とボリュームに基づいて競争力のある料金を提供しています。現在の料金についてはcapsolver.comを参照してください。ボーナスコード BROWSER4 を使用すると6%のボーナスが得られます。

Browser4はどのプログラミング言語で構築されていますか?

Browser4はKotlinで構築され、JVM(Java 17以上)で動作します。Javaアプリケーションからも使用できます。

Browser4は並列CAPTCHA解決を処理できますか?

はい!Browser4のコルーチンセーフな設計により、効率的な並列処理が可能です。CapSolverのAPIと組み合わせることで、異なる抽出ジョブで複数のCAPTCHAを同時に解決できます。

CAPTCHAのサイトキーを見つける方法は?

サイトキーは通常、ページのHTMLソースにあります:

  • reCAPTCHA: .g-recaptcha要素のdata-sitekey属性
  • Turnstile: .cf-turnstile要素のdata-sitekey属性
  • または、API呼び出しでキーパラメータを確認

コンプライアンス免責事項: このブログで提供される情報は、情報提供のみを目的としています。CapSolverは、すべての適用される法律および規制の遵守に努めています。CapSolverネットワークの不法、詐欺、または悪用の目的での使用は厳格に禁止され、調査されます。私たちのキャプチャ解決ソリューションは、公共データのクローリング中にキャプチャの問題を解決する際に100%のコンプライアンスを確保しながら、ユーザーエクスペリエンスを向上させます。私たちは、サービスの責任ある使用を奨励します。詳細については、サービス利用規約およびプライバシーポリシーをご覧ください。

もっと見る

Maxun と CapSolver のインテグレーション
MaxunでCapSolver統合を使用してCaptchaを解決する方法

CapSolverとMaxunを統合して実際のウェブスクレイピングを行うための実用的なガイド。reCAPTCHA、Cloudflare Turnstile、およびCAPTCHAで保護されたサイトを扱う方法を、事前認証とロボットワークフローを使用して学びましょう。

web scraping
Logo of CapSolver

Sora Fujimoto

21-Jan-2026

Browser4 と CapSolver の統合
Captchaをブラウザ4で解く方法とCapSolverの統合

高スループットブラウザ4の自動化と、大規模なウェブデータ抽出におけるCAPTCHAチャレンジを処理するためのCapSolverの組み合わせ。

web scraping
Logo of CapSolver

Sora Fujimoto

21-Jan-2026

スクレイピングボットとは そしてどのように構築するか
スクレイピングボットとは何か、そしてどのように構築するか

スクレイピングボットとは何かを学び、自動データ抽出用にそれを構築する方法を学びましょう。最高のツール、セキュリティナビゲーションのテクニック、および倫理的なスクレイピングの実践を探ってみましょう。

web scraping
Logo of CapSolver

Emma Foster

16-Jan-2026

スクラピ vs. セレニウム
Scrapy と Selenium: あなたのウェブスクレイピングプロジェクトにどちらが最適か

ScrapyとSeleniumのウェブスクリーピングにおける強みと違いを探る。どのツールがあなたのプロジェクトに最も適しているか、そしてCAPTCHAなどの課題をどう対処するかを学ぶ。

web scraping
Logo of CapSolver

Nikolai Smirnov

14-Jan-2026

Selenium Driverlessの使い方: 効率的なウェブスクリーピング
Selenium ドライバーレスの使い方: 効率的なウェブスクレイピング

効率的なウェブスクレイピングにSelenium Driverlessを使用する方法を学びましょう。このガイドでは、環境を設定する、最初のSelenium Driverlessスクリプトを書く、動的コンテンツを扱うためのステップバイステップの手順を提供しています。従来のWebDriver管理の複雑さを避けて、ウェブスクレイピングのタスクを簡素化し、データ抽出プロセスをより簡単で、速く、そしてよりポータブルにすることができます。

web scraping
Logo of CapSolver

Nikolai Smirnov

14-Jan-2026

Pythonでウェブサイトをクローリングする際の403 Forbiddenエラーの解決方法
Pythonでウェブサイトをクロールする際の403エラーの解決方法

Pythonでウェブサイトをクロールする際の403 Forbiddenエラーを乗り越える方法を学びましょう。このガイドでは、IPローテーション、User-Agentのスプーフィング、リクエストのスローティング、認証処理、アクセス制限を回避し、ウェブスキャッピングを成功裏に継続するためのヘッドレスブラウザの使用についてカバーしています。

web scraping
Logo of CapSolver

Sora Fujimoto

13-Jan-2026