API Tutorials

PowerShell + CaptchaAI: Windows Otomasyonu CAPTCHA Çözme

Windows otomasyon script'leriniz bir CAPTCHA duvarına takıldığında, çözüm tek bir yerleşik cmdlet kadar yakınınızda: Invoke-RestMethod. CaptchaAI'nin HTTP API'sini PowerShell'e bağlamak için harici modül kurmanıza ya da ekstra bağımlılık eklemenize gerek yok — API anahtarınızı alıp bir POST isteği göndermeniz yeterli.

Bu rehber; reCAPTCHA v2/v3'ü, Cloudflare Turnstile'ı ve resim (OCR) CAPTCHA'larını PowerShell 5.1 ve 7+ üzerinde, doğrudan iş akışınıza kopyalayabileceğiniz üretime hazır fonksiyonlarla nasıl çözeceğinizi adım adım gösterir. Sistem yöneticileri, QA mühendisleri ve DevOps ekipleri için yazıldı.


PowerShell CAPTCHA otomasyonu için neden mantıklı

Ayrı bir çalışma zamanı ya da paket yöneticisi kurmadan, sunucuda zaten hazır olan bir araçla REST çağrısı yapabilmeniz en büyük avantaj. Öne çıkan noktalar:

  • Windows'ta yerleşik — kurulum gerektirmez (PowerShell 5.1+)
  • Invoke-RestMethod — otomatik JSON ayrıştırmayla yerel REST API desteği
  • Görev Zamanlayıcı — CAPTCHA'ya bağımlı script'leri sistem üzerinde zamanlayın
  • Boru hattı dostu — çözümü aşağı yönlü otomasyonla zincirleyin
  • Platformlar arası — PowerShell 7+ Linux ve macOS'ta da çalışır

Başlamadan önce gerekenler

Kuruluma başlamadan önce elinizde şunların olması yeterli:

  • PowerShell 5.1 (Windows) veya PowerShell 7+ (platformlar arası)
  • CaptchaAI API anahtarı (buradan edinin)
  • Ek modül gerekmez

Temel çözücü fonksiyonları

Tüm çözücüler iki yapı taşına dayanır: görevi gönderen ve sonucu sorgulayan iki fonksiyon. Önce bunları tanımlayın; her CAPTCHA türü için sonra ince birer sarmalayıcı eklersiniz.

Görevi gönderme

Görevi in.php uç noktasına POST eder ve takip için bir task ID döndürür.

function Submit-CaptchaTask {
    param(
        [Parameter(Mandatory)]
        [string]$ApiKey,

        [Parameter(Mandatory)]
        [hashtable]$TaskParams
    )

    $body = @{
        key  = $ApiKey
        json = 1
    } + $TaskParams

    $response = Invoke-RestMethod -Uri "https://ocr.captchaai.com/in.php" `
        -Method Post `
        -Body $body `
        -ContentType "application/x-www-form-urlencoded"

    if ($response.status -ne 1) {
        throw "Submit failed: $($response.request)"
    }

    return $response.request
}

Sonucu sorgulama

Task ID'yi res.php üzerinde token hazır olana kadar belirli aralıklarla sorgular.

function Get-CaptchaResult {
    param(
        [Parameter(Mandatory)]
        [string]$ApiKey,

        [Parameter(Mandatory)]
        [string]$TaskId,

        [int]$MaxWaitSeconds = 300,
        [int]$PollIntervalSeconds = 5
    )

    $deadline = (Get-Date).AddSeconds($MaxWaitSeconds)

    while ((Get-Date) -lt $deadline) {
        Start-Sleep -Seconds $PollIntervalSeconds

        $response = Invoke-RestMethod -Uri "https://ocr.captchaai.com/res.php" `
            -Method Get `
            -Body @{
                key    = $ApiKey
                action = "get"
                id     = $TaskId
                json   = 1
            }

        if ($response.request -eq "CAPCHA_NOT_READY") {
            Write-Verbose "Waiting for solution..."
            continue
        }

        if ($response.status -ne 1) {
            throw "Solve failed: $($response.request)"
        }

        return $response.request
    }

    throw "Timeout: CAPTCHA not solved within $MaxWaitSeconds seconds"
}

Aşağıdaki tüm çözücüler bu ikiliyi çağırır; tür başına yalnızca gönderim parametreleri değişir. Bu yüzden bir kez doğru kurduğunuzda geri kalan her şey ince bir sarmalayıcıdan ibarettir.


reCAPTCHA v2 çözme

reCAPTCHA v2 en sık karşılaşacağınız türdür; çözücü setine buradan başlamak mantıklı.

function Solve-RecaptchaV2 {
    param(
        [Parameter(Mandatory)]
        [string]$ApiKey,

        [Parameter(Mandatory)]
        [string]$SiteUrl,

        [Parameter(Mandatory)]
        [string]$SiteKey
    )

    Write-Host "Submitting reCAPTCHA v2 task..."
    $taskId = Submit-CaptchaTask -ApiKey $ApiKey -TaskParams @{
        method    = "userrecaptcha"
        googlekey = $SiteKey
        pageurl   = $SiteUrl
    }
    Write-Host "Task ID: $taskId"

    Write-Host "Polling for solution..."
    $token = Get-CaptchaResult -ApiKey $ApiKey -TaskId $taskId
    Write-Host "Solved! Token: $($token.Substring(0, [Math]::Min(50, $token.Length)))..."

    return $token
}

# Usage
$apiKey = "YOUR_API_KEY"
$token = Solve-RecaptchaV2 `
    -ApiKey $apiKey `
    -SiteUrl "https://staging.example.com/qa-login" `
    -SiteKey "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-"

googlekey sayfanın reCAPTCHA sitekey'idir; pageurl ise doğrulamanın göründüğü tam adrestir. Dönen token'ı hedef formun g-recaptcha-response alanına yerleştirerek isteği tamamlarsınız.


Cloudflare Turnstile çözme

Turnstile neredeyse aynı akışı kullanır; tek fark method değerinin turnstile olması ve sitekey'in key alanında gönderilmesidir.

function Solve-Turnstile {
    param(
        [Parameter(Mandatory)]
        [string]$ApiKey,

        [Parameter(Mandatory)]
        [string]$SiteUrl,

        [Parameter(Mandatory)]
        [string]$SiteKey
    )

    $taskId = Submit-CaptchaTask -ApiKey $ApiKey -TaskParams @{
        method  = "turnstile"
        key     = $SiteKey
        pageurl = $SiteUrl
    }

    return Get-CaptchaResult -ApiKey $ApiKey -TaskId $taskId
}

# Usage
$token = Solve-Turnstile `
    -ApiKey "YOUR_API_KEY" `
    -SiteUrl "https://example.com/form" `
    -SiteKey "0x4AAAAAAAB5..."

Dönen değeri formun cf-turnstile-response alanına yerleştirin — reCAPTCHA'nın g-recaptcha-response alanından farklı olduğuna dikkat edin.


reCAPTCHA v3 çözme

reCAPTCHA v3 görünmezdir ve arka planda bir güven skoru üretir. Gönderim aynı fonksiyonla yapılır; yalnızca sürüm ve eylem adı eklenir.

function Solve-RecaptchaV3 {
    param(
        [Parameter(Mandatory)]
        [string]$ApiKey,

        [Parameter(Mandatory)]
        [string]$SiteUrl,

        [Parameter(Mandatory)]
        [string]$SiteKey,

        [string]$Action = "verify",
    )

    $taskId = Submit-CaptchaTask -ApiKey $ApiKey -TaskParams @{
        method    = "userrecaptcha"
        googlekey = $SiteKey
        pageurl   = $SiteUrl
        version   = "v3"
        action    = $Action
    }

    return Get-CaptchaResult -ApiKey $ApiKey -TaskId $taskId
}

reCAPTCHA v3 aynı gönderim fonksiyonunu kullanır; tek farkı version = "v3" ve sayfada tanımlı action adını eklemenizdir.


Resim (OCR) CAPTCHA çözme

Resim tabanlı CAPTCHA'larda token değil, doğrudan okunmuş metin alırsınız. Görüntüyü base64'e çevirip body alanında gönderin.

function Solve-ImageCaptcha {
    param(
        [Parameter(Mandatory)]
        [string]$ApiKey,

        [Parameter(Mandatory)]
        [string]$ImagePath
    )

    if (-not (Test-Path $ImagePath)) {
        throw "Image file not found: $ImagePath"
    }

    $imageBytes = [System.IO.File]::ReadAllBytes($ImagePath)
    $base64 = [Convert]::ToBase64String($imageBytes)

    $taskId = Submit-CaptchaTask -ApiKey $ApiKey -TaskParams @{
        method = "base64"
        body   = $base64
    }

    return Get-CaptchaResult -ApiKey $ApiKey -TaskId $taskId
}

# Usage
$text = Solve-ImageCaptcha -ApiKey "YOUR_API_KEY" -ImagePath "C:\captcha.png"
Write-Host "CAPTCHA text: $text"

URL'deki resimden çözme

Görüntü uzak bir adresteyse önce indirip byte dizisini base64'e çevirin.

function Solve-ImageCaptchaFromUrl {
    param(
        [Parameter(Mandatory)]
        [string]$ApiKey,

        [Parameter(Mandatory)]
        [string]$ImageUrl
    )

    $imageBytes = (Invoke-WebRequest -Uri $ImageUrl).Content
    $base64 = [Convert]::ToBase64String($imageBytes)

    $taskId = Submit-CaptchaTask -ApiKey $ApiKey -TaskParams @{
        method = "base64"
        body   = $base64
    }

    return Get-CaptchaResult -ApiKey $ApiKey -TaskId $taskId
}

Kaynak ne olursa olsun akış aynıdır: kaynak yerel bir dosya (Solve-ImageCaptcha) ya da bir URL (Solve-ImageCaptchaFromUrl) olabilir.


Hazır çözücü modülü

CaptchaAI.psm1 olarak kaydedin:

class CaptchaAISolver {
    [string]$ApiKey
    [string]$BaseUrl = "https://ocr.captchaai.com"
    [int]$PollInterval = 5
    [int]$MaxWait = 300

    CaptchaAISolver([string]$apiKey) {
        $this.ApiKey = $apiKey
    }

    [string] SolveRecaptchaV2([string]$siteUrl, [string]$siteKey) {
        return $this.Solve(@{
            method    = "userrecaptcha"
            googlekey = $siteKey
            pageurl   = $siteUrl
        })
    }

    [string] SolveTurnstile([string]$siteUrl, [string]$siteKey) {
        return $this.Solve(@{
            method  = "turnstile"
            key     = $siteKey
            pageurl = $siteUrl
        })
    }

    [string] SolveImage([string]$imagePath) {
        $bytes = [System.IO.File]::ReadAllBytes($imagePath)
        $base64 = [Convert]::ToBase64String($bytes)
        return $this.Solve(@{
            method = "base64"
            body   = $base64
        })
    }

    [double] GetBalance() {
        $response = Invoke-RestMethod -Uri "$($this.BaseUrl)/res.php" `
            -Body @{ key = $this.ApiKey; action = "getbalance"; json = 1 }
        return [double]$response.request
    }

    hidden [string] Solve([hashtable]$params) {
        $taskId = $this.Submit($params)
        return $this.Poll($taskId)
    }

    hidden [string] Submit([hashtable]$params) {
        $body = @{ key = $this.ApiKey; json = 1 } + $params
        $response = Invoke-RestMethod -Uri "$($this.BaseUrl)/in.php" `
            -Method Post -Body $body
        if ($response.status -ne 1) { throw "Submit: $($response.request)" }
        return $response.request
    }

    hidden [string] Poll([string]$taskId) {
        $deadline = (Get-Date).AddSeconds($this.MaxWait)
        while ((Get-Date) -lt $deadline) {
            Start-Sleep -Seconds $this.PollInterval
            $response = Invoke-RestMethod -Uri "$($this.BaseUrl)/res.php" `
                -Body @{ key = $this.ApiKey; action = "get"; id = $taskId; json = 1 }
            if ($response.request -eq "CAPCHA_NOT_READY") { continue }
            if ($response.status -ne 1) { throw "Solve: $($response.request)" }
            return $response.request
        }
        throw "Timeout"
    }
}

# Export
Export-ModuleMember

Ayrı fonksiyonlar yerine tekrar kullanılabilir tek bir sınıf isterseniz CaptchaAISolver tüm mantığı toplar: gönderim, sorgulama ve bakiye kontrolü aynı nesnede durur.

Modülü kullanma

using module ile içe aktarın, bir kez örnekleyin ve tüm çağrıları aynı nesne üzerinden yapın:

using module .\CaptchaAI.psm1

$solver = [CaptchaAISolver]::new("YOUR_API_KEY")

# Check balance
$balance = $solver.GetBalance()
Write-Host "Balance: `$$balance"

# Solve reCAPTCHA v2
$token = $solver.SolveRecaptchaV2("https://staging.example.com/qa-login", "SITEKEY")
Write-Host "Token: $($token.Substring(0, 50))..."

Sınıf tabanlı yaklaşım, büyük script'lerde API anahtarını ve yapılandırmayı tek yerde tutarak parametre tekrarını azaltır.


Token'ı forma gönderme

Token'ı almak işin yarısı; asıl doğrulama, onu hedef formla birlikte göndermenizle tamamlanır.

function Submit-FormWithToken {
    param(
        [string]$Url,
        [string]$Token,
        [hashtable]$FormData
    )

    $body = $FormData + @{
        "g-recaptcha-response" = $Token
    }

    $response = Invoke-WebRequest -Uri $Url `
        -Method Post `
        -Body $body `
        -ContentType "application/x-www-form-urlencoded"

    return $response
}

# Usage
$token = Solve-RecaptchaV2 -ApiKey "YOUR_API_KEY" `
    -SiteUrl "https://staging.example.com/qa-login" `
    -SiteKey "SITEKEY"

$result = Submit-FormWithToken `
    -Url "https://staging.example.com/qa-login" `
    -Token $token `
    -FormData @{
        username = "user@example.com"
        password = "password"
    }

Write-Host "Response: $($result.StatusCode)"

Token'ı hedef formun beklediği alanla göndermeniz gerekir: reCAPTCHA için g-recaptcha-response, Turnstile için cf-turnstile-response. Kısa ömürlü olduğundan geciktirmeden gönderin.


Job'larla paralel çözüm

Görevleri tek tek beklemek yerine Start-Job ile eşzamanlı çalıştırıp toplam süreyi belirgin biçimde kısaltabilirsiniz.

$apiKey = "YOUR_API_KEY"

$tasks = @(
    @{ Url = "https://site-a.com"; Key = "SITEKEY_A" },
    @{ Url = "https://site-b.com"; Key = "SITEKEY_B" },
    @{ Url = "https://site-c.com"; Key = "SITEKEY_C" }
)

$jobs = $tasks | ForEach-Object {
    $task = $_
    Start-Job -ScriptBlock {
        param($ApiKey, $Url, $SiteKey)

        $taskId = (Invoke-RestMethod -Uri "https://ocr.captchaai.com/in.php" -Method Post -Body @{
            key = $ApiKey; json = 1; method = "userrecaptcha"
            googlekey = $SiteKey; pageurl = $Url
        }).request

        $deadline = (Get-Date).AddSeconds(300)
        while ((Get-Date) -lt $deadline) {
            Start-Sleep -Seconds 5
            $result = Invoke-RestMethod -Uri "https://ocr.captchaai.com/res.php" -Body @{
                key = $ApiKey; action = "get"; id = $taskId; json = 1
            }
            if ($result.request -ne "CAPCHA_NOT_READY" -and $result.status -eq 1) {
                return @{ Url = $Url; Token = $result.request }
            }
        }
        return @{ Url = $Url; Error = "Timeout" }
    } -ArgumentList $apiKey, $task.Url, $task.Key
}

# Wait and collect results
$results = $jobs | Wait-Job | Receive-Job
$results | ForEach-Object {
    if ($_.Token) {
        Write-Host "$($_.Url): $($_.Token.Substring(0, 50))..."
    } else {
        Write-Host "$($_.Url): $($_.Error)" -ForegroundColor Red
    }
}
$jobs | Remove-Job

Sonuçları toplamadan önce tüm işleri Wait-Job ile bekleyin. Paralel çözümde iki noktaya dikkat edin:

  • Eşzamanlı çözüm kapasiteniz planınızdaki thread sayısına bağlıdır.
  • Her Start-Job ayrı bir PowerShell süreci başlatır; çok sayıda iş için bellek kullanımını izleyin.

Yeniden deneme ve hata yönetimi

Geçici ağ ya da kapasite hataları için gönderime yeniden deneme mantığı ekleyin.

function Solve-WithRetry {
    param(
        [Parameter(Mandatory)]
        [string]$ApiKey,

        [Parameter(Mandatory)]
        [hashtable]$TaskParams,

        [int]$MaxRetries = 3
    )

    $retryableErrors = @(
        "ERROR_NO_SLOT_AVAILABLE",
        "ERROR_CAPTCHA_UNSOLVABLE"
    )

    for ($attempt = 0; $attempt -le $MaxRetries; $attempt++) {
        if ($attempt -gt 0) {
            $delay = [Math]::Pow(2, $attempt) + (Get-Random -Maximum 3)
            Write-Host "Retry $attempt/$MaxRetries after $($delay)s..."
            Start-Sleep -Seconds $delay
        }

        try {
            $taskId = Submit-CaptchaTask -ApiKey $ApiKey -TaskParams $TaskParams
            $result = Get-CaptchaResult -ApiKey $ApiKey -TaskId $taskId
            return $result
        }
        catch {
            $errorMsg = $_.Exception.Message
            $isRetryable = $retryableErrors | Where-Object { $errorMsg -like "*$_*" }

            if (-not $isRetryable -or $attempt -eq $MaxRetries) {
                throw
            }
            Write-Warning "Retryable error: $errorMsg"
        }
    }
}

Üretimde her istek ilk denemede başarılı olmaz. ERROR_NO_SLOT_AVAILABLE veya ERROR_CAPTCHA_UNSOLVABLE gibi geçici hatalar için üstel geri çekilme (exponential backoff) ile yeniden deneyin; kalıcı hataları ise doğrudan yukarı fırlatın.


Görev Zamanlayıcı ile otomasyon

CAPTCHA'ya bağımlı bir script'i elle çalıştırmak yerine Windows Görev Zamanlayıcı ile planlayın.

# Create a scheduled task that runs CAPTCHA automation daily
$action = New-ScheduledTaskAction `
    -Execute "powershell.exe" `
    -Argument "-ExecutionPolicy QA tanılama -File C:\Scripts\captcha-automation.ps1"

$trigger = New-ScheduledTaskTrigger -Daily -At "08:00"

Register-ScheduledTask `
    -TaskName "CaptchaAutomation" `
    -Action $action `
    -Trigger $trigger `
    -Description "Run daily CAPTCHA automation with CaptchaAI"

Türkiye'deki QA ekipleri için yaygın bir senaryo: bir Windows sunucusunda her gece çalışan checkout akışı smoke testleri. Görev Zamanlayıcı ile bu script'i sabah 08:00'e kurar, gece boyunca CAPTCHA'lı formları otomatik doğrularsınız. Maliyet tarafında CaptchaAI thread bazlı ve USD üzerinden faturalanır — örneğin BASIC ($15/ay, 5 thread) planı, TL kurundaki dalgalanmalardan bağımsız, öngörülebilir aylık bir gider anlamına gelir. Her plan thread başına sınırsız çözüm içerir.

Zamanlanmış görevi kurarken şu iki noktaya dikkat edin:

  • -At değerini sunucunuzun saat dilimine (Europe/Istanbul) göre ayarlayın.
  • Görevi çalıştıran hesabın script klasörüne ve ağa erişim yetkisi olduğundan emin olun.

Sorun giderme

En sık karşılaşılan hataların çoğu yanlış anahtar, boş bakiye ya da eski TLS ayarından kaynaklanır:

Hata Sebep Düzeltme
ERROR_WRONG_USER_KEY Geçersiz API anahtarı Anahtarı kontrol panelinde doğrulayın
ERROR_ZERO_BALANCE Bakiye yok Hesabınıza bakiye yükleyin
Invoke-RestMethod: SSL/TLS TLS sürüm uyuşmazlığı [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12'yi ekleyin
The response content cannot be parsed JSON dışı yanıt Invoke-WebRequest kullanıp yanıtı elle ayrıştırın
Execution policy hatası Script engellendi Set-ExecutionPolicy -Scope CurrentUser RemoteSigned'ı çalıştırın
Cannot convert to double Bakiye ayrıştırma hatası [double]::Parse($response.request)'yi kullanın

Bir hatayı çözemezseniz önce anahtarınızı ve bakiyenizi panelden doğrulayın; sorunların büyük kısmı bu iki kontrolle kapanır.


Sık sorulan sorular

Aşağıda PowerShell entegrasyonuyla ilgili en sık gelen sorular yer alıyor:

PowerShell 7 ile Linux ve macOS'ta da kullanabilir miyim?

Evet. Invoke-RestMethod ve Invoke-WebRequest çapraz platformdur; buradaki fonksiyonlar Windows'ta PowerShell 5.1 ile, Linux ve macOS'ta ise PowerShell 7+ ile aynı şekilde çalışır. Kod tarafında değişiklik gerekmez.

CaptchaAI hangi CAPTCHA türlerini çözer?

reCAPTCHA v2/v3, Cloudflare Turnstile ve Challenge, GeeTest v3, resim/OCR, grid ve BLS türlerini çözer. hCaptcha ve FunCaptcha (Arkose Labs) desteklenmez; GeeTest v4 çok yakında gelecek. CaptchaFox, Friendly Captcha ve Lemin ise beta aşamasındadır.

API anahtarımı script içinde nasıl güvende tutarım?

Anahtarı koda gömmeyin. Yerelde ortam değişkeni ($env:CAPTCHAAI_KEY) olarak, CI/CD ortamında ise gizli değişken (secret variable) olarak saklayın. Azure DevOps, GitHub Actions ve Jenkins bu yöntemi doğrudan destekler.

Fiyatlandırma thread'e göre mi hesaplanıyor?

Evet. CaptchaAI çözüm başına değil, eşzamanlı thread başına faturalandırılır ve her plan thread başına sınırsız çözüm içerir. BASIC ($15/ay, 5 thread) ile başlar; paralel iş yükünüz arttıkça daha yüksek thread'li planlara geçebilirsiniz.

Bir çözümün zaman aşımına uğraması ne kadar sürer?

Varsayılan olarak Get-CaptchaResult en fazla 300 saniye bekler ve her 5 saniyede bir sorgular. MaxWaitSeconds ve PollIntervalSeconds parametreleriyle bu değerleri iş yükünüze göre ayarlayabilirsiniz.


İlgili rehberler


CAPTCHA'ları Windows komut satırından otomatikleştirin — API anahtarınızı alın ve script yazmaya başlayın.

Bu makale için yorumlar devre dışı bırakılmıştır.