API Tutorials

Bash Komut Dosyası + cURL + CaptchaAI: Kabuk CAPTCHA Otomasyonu

Gecenin bir yarısı çalışan bir cron işi bir CAPTCHA'ya takıldığında ve konteynerde ne Python ne Node.js kuruluysa, geriye tek bir sağlam araç kalır: shell. İyi haber, CaptchaAI'nin HTTP API'sinin tam da bunun için biçilmiş kaftan olması — bir curl isteğiyle görevi gönderir, ikincisiyle sonucu sorgularsınız. Ek çalışma zamanı, SDK ya da paket kurulumu yok.

Bu rehber, saf Bash ve cURL ile reCAPTCHA v2/v3, Cloudflare Turnstile ve görüntü (image/OCR) CAPTCHA'larını nasıl çözeceğinizi gösterir. Sonunda, kopyalayıp doğrudan cron işlerinize, CI/CD ardışık düzenlerinize veya izleme betiklerinize yerleştirebileceğiniz fonksiyonlarınız olur.


Neden Bash + cURL ile CAPTCHA çözme?

  • Sıfır bağımlılık — Bash ve cURL her Linux/macOS sisteminde hazır gelir
  • Hafif — çalışma zamanı, paket yöneticisi veya kurulum adımı yok
  • Cron dostu — CAPTCHA'ya bağlı görevleri standart cron ile zamanlayın
  • CI/CD uyumlu — Docker, GitHub Actions, Jenkins ve GitLab CI'da sorunsuz çalışır
  • Boru hattına uygunjq, grep ve awk ile çözüm adımlarını zincirleyin

Başlamadan önce: gereksinimler

  • Bash 4.0+
  • cURL (Linux/macOS'ta varsayılan olarak kuruludur)
  • JSON ayrıştırma için jq: apt install jq ya da brew install jq
  • Bir CaptchaAI API anahtarı (buradan alın)

Çekirdek fonksiyonlar: gönder ve sorgula

CaptchaAI'nin akışı iki adımdan oluşur: görevi in.php uç noktasına gönderir, ardından sonucu res.php üzerinden periyodik olarak sorgularsınız. Diğer tüm çözücüler bu iki fonksiyonun üzerine kurulur.

Görevi gönderme

#!/bin/bash

CAPTCHAAI_URL="https://ocr.captchaai.com"

submit_task() {
    local api_key="$1"
    shift
    local params=("$@")

    local response
    response=$(curl -s -X POST "${CAPTCHAAI_URL}/in.php" \
        -d "key=${api_key}" \
        -d "json=1" \
        "${params[@]}")

    local status
    status=$(echo "$response" | jq -r '.status')
    local request
    request=$(echo "$response" | jq -r '.request')

    if [ "$status" != "1" ]; then
        echo "ERROR: Submit failed: $request" >&2
        return 1
    fi

    echo "$request"
}

submit_task, API anahtarını ve türe özgü parametreleri in.php uç noktasına iletir, json=1 ile yanıtı JSON olarak ister ve jq ile status alanını doğrular.

Başarılıysa görev kimliğini (request) döndürür; bu kimliği bir sonraki adımda sorgulama için kullanırsınız. Hata durumunda mesajı stderr'e yazıp 1 ile döner, böylece çağıran betik akışını durdurabilir.

Sonucu sorgulama

poll_result() {
    local api_key="$1"
    local task_id="$2"
    local max_wait="${3:-300}"
    local interval="${4:-5}"

    local elapsed=0

    while [ "$elapsed" -lt "$max_wait" ]; do
        sleep "$interval"
        elapsed=$((elapsed + interval))

        local response
        response=$(curl -s "${CAPTCHAAI_URL}/res.php?key=${api_key}&action=get&id=${task_id}&json=1")

        local status
        status=$(echo "$response" | jq -r '.status')
        local request
        request=$(echo "$response" | jq -r '.request')

        if [ "$request" = "CAPCHA_NOT_READY" ]; then
            echo "Waiting... (${elapsed}s/${max_wait}s)" >&2
            continue
        fi

        if [ "$status" != "1" ]; then
            echo "ERROR: Solve failed: $request" >&2
            return 1
        fi

        echo "$request"
        return 0
    done

    echo "ERROR: Timeout after ${max_wait}s" >&2
    return 1
}

poll_result, çözüm hazır olana kadar res.php uç noktasını interval saniyede bir sorgular. Yanıt CAPCHA_NOT_READY ise beklemeye devam eder; token geldiğinde onu döndürür.

max_wait (varsayılan 300 saniye) ve interval değerlerini iş yükünüze göre ayarlayın — yoğun türlerde daha uzun bir zaman aşımı, hızlı türlerde daha kısa bir sorgulama aralığı mantıklıdır.


reCAPTCHA v2 çözme

En sık karşılaşılan tür. submit_task fonksiyonuna method=userrecaptcha, sayfanın googlekey değeri (sitekey) ve pageurl bilgisini verirsiniz; gerisini poll_result halleder ve g-recaptcha-response token'ını döndürür.

solve_recaptcha_v2() {
    local api_key="$1"
    local site_url="$2"
    local sitekey="$3"

    echo "Submitting reCAPTCHA v2..." >&2
    local task_id
    task_id=$(submit_task "$api_key" \
        -d "method=userrecaptcha" \
        -d "googlekey=${sitekey}" \
        -d "pageurl=${site_url}")

    if [ $? -ne 0 ]; then return 1; fi
    echo "Task ID: $task_id" >&2

    echo "Polling for solution..." >&2
    local token
    token=$(poll_result "$api_key" "$task_id")

    if [ $? -ne 0 ]; then return 1; fi
    echo "$token"
}

# Usage
API_KEY="YOUR_API_KEY"
TOKEN=$(solve_recaptcha_v2 "$API_KEY" \
    "https://staging.example.com/qa-login" \
    "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-")

echo "Token: ${TOKEN:0:50}..."

Not: googlekey değeri sayfanın kaynak kodunda data-sitekey özniteliğinde bulunur; yanlış sitekey en sık görülen çözüm hatasıdır.


Cloudflare Turnstile çözme

Turnstile akışı reCAPTCHA'ya çok benzer; tek fark method=turnstile değeridir ve sitekey bu türde key parametresiyle iletilir. Dönen cf-turnstile-response token'ını sayfanın gizli form alanına yerleştirirsiniz.

İpucu: Turnstile için sorgulama aralığını 3 saniyeye düşürmek, çözüm süresini gereksiz beklemeden kısaltabilir.

solve_turnstile() {
    local api_key="$1"
    local site_url="$2"
    local sitekey="$3"

    local task_id
    task_id=$(submit_task "$api_key" \
        -d "method=turnstile" \
        -d "key=${sitekey}" \
        -d "pageurl=${site_url}")

    if [ $? -ne 0 ]; then return 1; fi

    poll_result "$api_key" "$task_id"
}

# Usage
TOKEN=$(solve_turnstile "$API_KEY" \
    "https://example.com/form" \
    "0x4AAAAAAAB5...")

reCAPTCHA v3 çözme

reCAPTCHA v3 görünmez çalışır ve kutucuk yerine bir puan üretir. v2'den farkı, isteğe version=v3 ve sayfanın beklediği action değerini eklemenizdir; action adı hedef sayfayla eşleşmezse token düşük puan alır.

Not: reCAPTCHA v3 kutucuk göstermediği için, action değerini hedef sayfanın kaynak kodundan doğrulamak en güvenilir yoldur.

solve_recaptcha_v3() {
    local api_key="$1"
    local site_url="$2"
    local sitekey="$3"
    local action="${4:-verify}"

    local task_id
    task_id=$(submit_task "$api_key" \
        -d "method=userrecaptcha" \
        -d "googlekey=${sitekey}" \
        -d "pageurl=${site_url}" \
        -d "version=v3" \
        -d "action=${action}" \

    if [ $? -ne 0 ]; then return 1; fi

    poll_result "$api_key" "$task_id"
}

Görüntü CAPTCHA'larını çözme

Klasik metin tabanlı (image/OCR) CAPTCHA'lar için görüntüyü base64'e kodlar ve method=base64 ile gönderirsiniz. Aşağıdaki fonksiyon dosya yolunu doğrular ve çözülen metni döndürür.

İkinci fonksiyon ise görüntüyü bir URL'den indirir, geçici dosyaya yazar ve işi bitince temizler — cron betiklerinde disk birikmesini önler.

İpucu: Yüksek çözünürlüklü görüntülerde base64 çıktısı büyür; gerekirse göndermeden önce görüntüyü küçültmek istek boyutunu azaltır.

solve_image_captcha() {
    local api_key="$1"
    local image_path="$2"

    if [ ! -f "$image_path" ]; then
        echo "ERROR: File not found: $image_path" >&2
        return 1
    fi

    local base64_data
    base64_data=$(base64 -w 0 "$image_path" 2>/dev/null || base64 "$image_path")

    local task_id
    task_id=$(submit_task "$api_key" \
        -d "method=base64" \
        --data-urlencode "body=${base64_data}")

    if [ $? -ne 0 ]; then return 1; fi

    poll_result "$api_key" "$task_id"
}

# From URL
solve_image_from_url() {
    local api_key="$1"
    local image_url="$2"
    local tmp_file
    tmp_file=$(mktemp /tmp/captcha_XXXXXX.png)

    curl -s -o "$tmp_file" "$image_url"
    local result
    result=$(solve_image_captcha "$api_key" "$tmp_file")
    rm -f "$tmp_file"

    echo "$result"
}

# Usage
TEXT=$(solve_image_captcha "$API_KEY" "captcha.png")
echo "CAPTCHA text: $TEXT"

Tam çözücü kütüphanesi (captchaai.sh)

Aşağıdaki kütüphaneyi captchaai.sh olarak kaydedin; diğer betiklerinizden source ./captchaai.sh ile çağırın. Bakiye kontrolü, gönderme, sorgulama ve tüm çözücüler tek dosyada toplanır:

#!/bin/bash
# CaptchaAI Solver Library
# Source this file: source ./captchaai.sh

CAPTCHAAI_URL="https://ocr.captchaai.com"
CAPTCHAAI_POLL_INTERVAL=5
CAPTCHAAI_MAX_WAIT=300

captchaai_submit() {
    local api_key="$1"; shift
    local response
    response=$(curl -s -X POST "${CAPTCHAAI_URL}/in.php" \
        -d "key=${api_key}" -d "json=1" "$@")
    local status=$(echo "$response" | jq -r '.status')
    local request=$(echo "$response" | jq -r '.request')
    [ "$status" = "1" ] && echo "$request" || { echo "Submit: $request" >&2; return 1; }
}

captchaai_poll() {
    local api_key="$1" task_id="$2" elapsed=0
    while [ "$elapsed" -lt "$CAPTCHAAI_MAX_WAIT" ]; do
        sleep "$CAPTCHAAI_POLL_INTERVAL"
        elapsed=$((elapsed + CAPTCHAAI_POLL_INTERVAL))
        local resp=$(curl -s "${CAPTCHAAI_URL}/res.php?key=${api_key}&action=get&id=${task_id}&json=1")
        local req=$(echo "$resp" | jq -r '.request')
        local st=$(echo "$resp" | jq -r '.status')
        [ "$req" = "CAPCHA_NOT_READY" ] && continue
        [ "$st" = "1" ] && { echo "$req"; return 0; }
        echo "Solve: $req" >&2; return 1
    done
    echo "Timeout" >&2; return 1
}

captchaai_balance() {
    local api_key="$1"
    curl -s "${CAPTCHAAI_URL}/res.php?key=${api_key}&action=getbalance&json=1" | jq -r '.request'
}

captchaai_recaptcha_v2() {
    local key="$1" url="$2" sk="$3"
    local tid=$(captchaai_submit "$key" -d "method=userrecaptcha" -d "googlekey=$sk" -d "pageurl=$url") || return 1
    captchaai_poll "$key" "$tid"
}

captchaai_turnstile() {
    local key="$1" url="$2" sk="$3"
    local tid=$(captchaai_submit "$key" -d "method=turnstile" -d "sitekey=$sk" -d "pageurl=$url") || return 1
    captchaai_poll "$key" "$tid"
}

captchaai_image() {
    local key="$1" path="$2"
    local b64=$(base64 -w 0 "$path" 2>/dev/null || base64 "$path")
    local tid=$(captchaai_submit "$key" -d "method=base64" --data-urlencode "body=$b64") || return 1
    captchaai_poll "$key" "$tid"
}

Kütüphaneyi kullanma

#!/bin/bash
source ./captchaai.sh

API_KEY="YOUR_API_KEY"

# Check balance
echo "Balance: $(captchaai_balance "$API_KEY")"

# Solve reCAPTCHA v2
TOKEN=$(captchaai_recaptcha_v2 "$API_KEY" \
    "https://staging.example.com/qa-login" \
    "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-")

echo "Token: ${TOKEN:0:50}..."

İpucu: Kütüphaneyi /usr/local/lib/captchaai.sh altına koyup betiklerinizde tek satırla source ederek tüm otomasyonlarınızda yeniden kullanabilirsiniz.


Çözülen token'ı forma gönderme

Token'ı almak yalnızca ilk yarısıdır; onu hedef formun beklediği alan adıyla göndermeniz gerekir. reCAPTCHA için bu alan g-recaptcha-response, Turnstile için cf-turnstile-response'tur. Aşağıdaki yardımcı, token'ı ilgili alanla birlikte POST eder ve ek form alanlarını (kullanıcı adı, parola gibi) esnek biçimde eklemenize izin verir.

submit_form_with_token() {
    local url="$1"
    local token="$2"
    shift 2

    curl -s -X POST "$url" \
        -d "g-recaptcha-response=${token}" \
        "$@"
}

# Usage: solve then submit
TOKEN=$(captchaai_recaptcha_v2 "$API_KEY" \
    "https://staging.example.com/qa-login" "SITEKEY")

RESPONSE=$(submit_form_with_token "https://staging.example.com/qa-login" \
    "$TOKEN" \
    -d "username=user@example.com" \
    -d "password=password")

echo "Response: $RESPONSE"

Arka plan işleriyle paralel çözüm

Birden çok CAPTCHA'yı sırayla çözmek yavaştır. Bash'in arka plan işleri (&) ile çözümleri eşzamanlı başlatabilir, sonuçları geçici dosyalarda toplayabilirsiniz. Kaç işi paralel başlattığınızı planınızın thread sayısıyla uyumlu tutun — BASIC ($15/ay, 5 thread) planında aynı anda 5 aktif çözüme kadar verimli çalışırsınız.

İpucu: Paralel iş sayısı thread limitinizi aşarsa fazladan istekler kuyruğa girer; eşzamanlılığı plan limitinizle eşleştirin.

#!/bin/bash
source ./captchaai.sh

API_KEY="YOUR_API_KEY"
RESULTS_DIR=$(mktemp -d)

# Define tasks
declare -A TASKS
TASKS["site-a"]="https://site-a.com|SITEKEY_A"
TASKS["site-b"]="https://site-b.com|SITEKEY_B"
TASKS["site-c"]="https://site-c.com|SITEKEY_C"

# Launch parallel solves
pids=()
for name in "${!TASKS[@]}"; do
    IFS='|' read -r url sitekey <<< "${TASKS[$name]}"
    (
        token=$(captchaai_recaptcha_v2 "$API_KEY" "$url" "$sitekey" 2>/dev/null)
        if [ $? -eq 0 ]; then
            echo "$token" > "${RESULTS_DIR}/${name}.token"
        else
            echo "FAILED" > "${RESULTS_DIR}/${name}.token"
        fi
    ) &
    pids+=($!)
done

# Wait for all
for pid in "${pids[@]}"; do
    wait "$pid"
done

# Collect results
echo "=== Results ==="
for name in "${!TASKS[@]}"; do
    token=$(cat "${RESULTS_DIR}/${name}.token")
    if [ "$token" = "FAILED" ]; then
        echo "$name: FAILED"
    else
        echo "$name: ${token:0:50}..."
    fi
done

rm -rf "$RESULTS_DIR"

Üstel geri çekilme (exponential backoff) ile yeniden deneme

Geçici hatalar (ERROR_NO_SLOT_AVAILABLE gibi) genellikle kısa süre sonra kendiliğinden geçer. Bekleme süresini üstel biçimde artıran bir sarmalayıcı yükü dağıtır; aşağıdaki fonksiyon yalnızca yeniden denenebilir hataları tekrar dener.

Not: Kalıcı hataları yeniden denemek bakiyenizi boşa harcar; hata kodunu ayırt etmek maliyet açısından önemlidir.

solve_with_retry() {
    local api_key="$1"
    local solve_cmd="$2"
    shift 2
    local max_retries="${1:-3}"

    local retryable_errors=("ERROR_NO_SLOT_AVAILABLE" "ERROR_CAPTCHA_UNSOLVABLE")
    local attempt=0

    while [ "$attempt" -le "$max_retries" ]; do
        if [ "$attempt" -gt 0 ]; then
            local delay=$((2 ** attempt + RANDOM % 3))
            echo "Retry $attempt/$max_retries after ${delay}s..." >&2
            sleep "$delay"
        fi

        local result
        result=$($solve_cmd "$api_key" "${@:2}")

        if [ $? -eq 0 ]; then
            echo "$result"
            return 0
        fi

        # Check if error is retryable
        local is_retryable=0
        for err in "${retryable_errors[@]}"; do
            if echo "$result" | grep -q "$err"; then
                is_retryable=1
                break
            fi
        done

        if [ "$is_retryable" -eq 0 ]; then
            echo "$result"
            return 1
        fi

        attempt=$((attempt + 1))
    done

    echo "Max retries exceeded" >&2
    return 1
}

Cron ile zamanlama

Betiği zamanlarken saat dilimine dikkat edin. Sunucularınız çoğunlukla UTC ile çalışır; işin İstanbul saatiyle her sabah 08:00'de tetiklenmesini sağlamak için betiğin başına TZ='Europe/Istanbul' ekleyin. Böylece çözüm işleri, veri ihracatı gibi yerel iş akışlarınızla aynı zamanda çalışır.

# Edit crontab: crontab -e
# Run daily at 8 AM
0 8 * * * /path/to/captcha-automation.sh >> /var/log/captcha.log 2>&1

Örnek cron betiği:

#!/bin/bash
source /path/to/captchaai.sh

API_KEY="YOUR_API_KEY"
LOG_FILE="/var/log/captcha-$(date +%Y%m%d).log"

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"; }

# Check balance first
BALANCE=$(captchaai_balance "$API_KEY")
log "Balance: $BALANCE"

if (( $(echo "$BALANCE < 1.0" | bc -l) )); then
    log "WARNING: Low balance!"
    exit 1
fi

# Solve and process
TOKEN=$(captchaai_recaptcha_v2 "$API_KEY" \
    "https://portal.example.com" "SITEKEY")

if [ $? -eq 0 ]; then
    log "Solved successfully"
    # Submit form, download data, etc.
    curl -s "https://portal.example.com/data" \
        -d "g-recaptcha-response=$TOKEN" \
        -o "/data/export-$(date +%Y%m%d).csv"
    log "Data exported"
else
    log "ERROR: Failed to solve CAPTCHA"
    exit 1
fi

Docker entegrasyonu

Yalnızca bash, curl ve jq gerektiğinden, Alpine tabanlı çok küçük bir imaj yeterlidir — CI/CD ardışık düzenlerinde hızlı başlatma ve düşük bellek kullanımı anlamına gelir.

FROM alpine:3.19

RUN apk add --no-cache bash curl jq

COPY captchaai.sh /usr/local/lib/captchaai.sh
COPY automation.sh /app/automation.sh

RUN chmod +x /app/automation.sh

CMD ["/app/automation.sh"]

Üretim ortamında dikkat edilecekler

API anahtarını asla depoya göndermeyin; ortam değişkeni veya ayrı bir env dosyası kullanın.

Cron işlerinde bakiyeyi her koşuda kontrol edip düşük bakiyede uyarı üretmek, gece yarısı sessizce başarısız olan işleri önler.

Kazınan içerik kişisel veri içeriyorsa Türkiye'de KVKK kapsamına girer; CaptchaAI'yi yalnızca yetkiniz olan sayfalar ve QA iş akışları için kullanın.

Sorun giderme

Hata Sebep Düzeltme
ERROR_WRONG_USER_KEY Geçersiz API anahtarı Anahtarı kontrol panelinde doğrulayın
ERROR_ZERO_BALANCE Fon yok Hesap yükleme
curl: (60) SSL certificate CA paketi eksik Test için --cacert /path/to/ca-bundle.crt veya -k'yi ekleyin
jq: command not found jq yüklü değil apt install jq veya brew install jq
base64: invalid option -- 'w' macOS base64 sözdizimi base64 -w 0 file yerine base64 file kullanın
Boş yanıt Ağ sorunu Hata ayıklama için kıvrılmaya -v bayrağını ekleyin

Sık sorulan sorular

Bash ile hangi CAPTCHA türlerini çözebilirim?

reCAPTCHA v2 ve v3, Cloudflare Turnstile ve görüntü (image/OCR) CAPTCHA'larını. Aynı submit_task fonksiyonuyla yalnızca method parametresini değiştirirsiniz. hCaptcha ve FunCaptcha CaptchaAI tarafından desteklenmez; bu türler için bir cURL akışı kurmaya çalışmayın.

res.php neden sürekli CAPCHA_NOT_READY döndürüyor?

Bu bir hata değil — çözüm henüz hazır değil demektir. poll_result fonksiyonu birkaç saniyede bir yeniden sorgular ve sonuç geldiğinde token'ı döndürür. Varsayılan max_wait değeri 300 saniyedir; yavaş türlerde bu süreyi artırabilirsiniz.

API anahtarımı cron betiğinde nasıl güvenli tutarım?

Anahtarı koda gömmeyin. Onu bir ortam değişkeninde tutun (export CAPTCHAAI_KEY="...") ve betikte $CAPTCHAAI_KEY ile çağırın; cron için değişkeni ayrı bir env dosyasına yazıp betiğin başında source edin.

Betik macOS'ta çalışır mı?

Evet. Tek fark base64 aracıdır: macOS BSD sürümünü kullandığı için -w 0 bayrağını desteklemez. Örneklerdeki base64 -w 0 ... || base64 ... kalıbı hem Linux hem de macOS'u kapsar.

Bu akışta CaptchaAI'yi kullanmak ne kadar tutar?

Faturalama çözüm başına değil, eşzamanlı thread başınadır ve her planda thread başına sınırsız çözüm gelir. Planlar BASIC ($15/ay, 5 thread) ile başlar; TL'nin oynaklığına karşı USD fiyatı öngörülebilir kalır.


İlgili rehberler


Terminalden çıkmadan CAPTCHA çözün — API anahtarınızı alın ve akışı Bash ile otomatikleştirin.

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