Tutorials

CAPTCHA Çözüm Denetim Günlükleri: Uyumluluk İçin İstekleri İzleme

Bir denetim günlüğü, "bu CAPTCHA çözümünü kim tetikledi, hangi site içindi, ne kadar sürdü ve kaça mal oldu?" sorularına dakikalar içinde yanıt vermenizi sağlar. Günde binlerce CAPTCHA çözen bir ekipte bu kayıt olmadan ne fatura mutabakatı yapabilir, ne bir hatayı geriye doğru izleyebilir, ne de bir denetçiye çözüm hacminizi kanıtlayabilirsiniz. Bu rehber, CaptchaAI çözümleriniz için yapılandırılmış bir denetim günlüğünü Python ve Node.js ile nasıl kuracağınızı adım adım gösterir.

Denetim günlüğü neden gerekli?

Otomatik CAPTCHA çözümü kritik bir iş akışının parçası olduğunda, her istek üç açıdan hesap verebilir olmalıdır: operasyonel, mali ve yasal.

  • Operasyonel: Bir çözüm oranı düştüğünde ya da gecikme arttığında, denetim günlüğü hangi site, hangi CAPTCHA türü ve hangi işin sorumlu olduğunu gösterir. Kör tahminle değil, veriyle sorun giderirsiniz.
  • Mali: Kayıt altındaki istek sayısı ve tahmini maliyet, CaptchaAI kullanım panelindeki gerçek tüketimle karşılaştırılabilir. Beklenmedik bir sıçramayı ilk fark eden siz olursunuz.
  • Yasal ve uyumluluk: Türkiye'de faaliyet gösteren ekipler için KVKK (Kişisel Verilerin Korunması Kanunu), SOC 2 ve GDPR gibi çerçeveler, otomatik veri toplama süreçlerinde izlenebilirlik bekler. İstanbul merkezli bir e-ticaret ekibinin ödeme adımındaki reCAPTCHA'yı QA ortamında doğrularken tuttuğu denetim izi, tam da bu izlenebilirliği kanıtlar.

Kritik ilke: Denetim günlüğü yalnızca meta veriyi kaydeder, kişisel veriyi değil. Hedef siteden gelen kişisel bilgiler KVKK kapsamındadır ve bu kaydın içinde asla yer almamalıdır.

Hangi alanları günlüğe kaydetmelisiniz?

Her CAPTCHA çözümü en az şu alanları içeren yapılandırılmış bir kayıt üretmelidir:

Alan Amaç Örnek
timestamp İsteğin ne zaman yapıldığı 2026-04-04T14:30:00Z
request_id Bu çözüme özel benzersiz kimlik uuid4()
captcha_type Kullanılan CAPTCHA yöntemi userrecaptcha
target_site Çözülen sayfanın URL'si https://staging.example.com/qa-login
task_id CaptchaAI görev kimliği 73829451
status Sonuç solved, failed, timeout
solve_time_ms Gönderimden sonuca kadar geçen süre 18432
error_code Başarısızlıkta hata kodu ERROR_CAPTCHA_UNSOLVABLE
initiator Çözümü tetikleyen iş ya da kullanıcı scraper-job-42
cost Tahmini maliyet 0.003

Şunları asla kaydetmeyin: API anahtarları, CAPTCHA token'ları (zaten geçicidirler) ve hedef sitelerden alınan kişisel veriler. Bunlar günlüğe hiçbir denetim değeri katmaz, buna karşılık ciddi bir güvenlik ve KVKK riski taşır.

Python ile denetim günlüğü uygulaması

Aşağıdaki uygulama, çözüm akışını sarmalayarak her aşamada (gönderim, sorgulama, sonuç, hata) tek satırlık bir JSONL kaydı üretir. Denetim günlükleyicisi bilinçli olarak uygulama loglarından ayrı tutulur ve dosya boyutu RotatingFileHandler ile sınırlanır.

# audit_solver.py
import os
import uuid
import time
import json
import logging
from datetime import datetime, timezone
import requests

API_KEY = os.environ.get("CAPTCHAAI_KEY", "YOUR_API_KEY")

# Configure audit logger — separate from application logs
audit_logger = logging.getLogger("captcha_audit")
audit_logger.setLevel(logging.INFO)

# File handler with rotation
from logging.handlers import RotatingFileHandler
handler = RotatingFileHandler(
    "captcha_audit.jsonl",
    maxBytes=50_000_000,  # 50 MB per file
    backupCount=10,
)
handler.setFormatter(logging.Formatter("%(message)s"))
audit_logger.addHandler(handler)

def log_audit(record):
    """Write a structured audit record."""
    audit_logger.info(json.dumps(record, default=str))

def solve_with_audit(sitekey, pageurl, captcha_type="userrecaptcha",
                      initiator="unknown"):
    """Solve a CAPTCHA with full audit logging."""
    request_id = str(uuid.uuid4())
    start = time.time()

    audit_record = {
        "request_id": request_id,
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "captcha_type": captcha_type,
        "target_site": pageurl,
        "initiator": initiator,
        "status": "submitted",
    }

    session = requests.Session()

    try:
        # Submit
        resp = session.get("https://ocr.captchaai.com/in.php", params={
            "key": API_KEY,
            "method": captcha_type,
            "googlekey": sitekey,
            "pageurl": pageurl,
            "json": "1",
        })
        result = resp.json()

        if result.get("status") != 1:
            audit_record.update({
                "status": "submit_failed",
                "error_code": result.get("request"),
                "solve_time_ms": int((time.time() - start) * 1000),
            })
            log_audit(audit_record)
            return None

        task_id = result["request"]
        audit_record["task_id"] = task_id

        # Poll
        time.sleep(15)
        for _ in range(25):
            poll = session.get("https://ocr.captchaai.com/res.php", params={
                "key": API_KEY, "action": "get",
                "id": task_id, "json": "1",
            })
            poll_result = poll.json()

            if poll_result.get("status") == 1:
                solve_time = int((time.time() - start) * 1000)
                audit_record.update({
                    "status": "solved",
                    "solve_time_ms": solve_time,
                    "cost_estimate": 0.003,  # Adjust per your rate
                })
                log_audit(audit_record)
                return poll_result["request"]

            if poll_result.get("request") != "CAPCHA_NOT_READY":
                audit_record.update({
                    "status": "failed",
                    "error_code": poll_result.get("request"),
                    "solve_time_ms": int((time.time() - start) * 1000),
                })
                log_audit(audit_record)
                return None

            time.sleep(5)

        audit_record.update({
            "status": "timeout",
            "solve_time_ms": int((time.time() - start) * 1000),
        })
        log_audit(audit_record)
        return None

    except Exception as e:
        audit_record.update({
            "status": "error",
            "error_code": str(e)[:200],
            "solve_time_ms": int((time.time() - start) * 1000),
        })
        log_audit(audit_record)
        raise

# Usage
token = solve_with_audit(
    sitekey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
    pageurl="https://www.google.com/recaptcha/api2/demo",
    initiator="price-scraper-v2",
)

Denetim günlüğü çıktısı (JSONL biçimi)

Her satır bağımsız bir JSON nesnesidir; bu, günlüğü hem jq gibi araçlarla hem de bir log toplayıcıyla işlemeyi kolaylaştırır.

{"request_id":"a1b2c3d4-...","timestamp":"2026-04-04T14:30:00+00:00","captcha_type":"userrecaptcha","target_site":"https://www.google.com/recaptcha/api2/demo","initiator":"price-scraper-v2","status":"solved","task_id":"73829451","solve_time_ms":18432,"cost_estimate":0.003}

Node.js ile aynı akış

Node.js tarafında bağımlılıkları hafif tutmak için kayıtları doğrudan bir JSONL dosyasına ekleyebilirsiniz. Mantık Python sürümüyle aynıdır: gönder, 15 saniye bekle, sonucu sorgula ve her durumu kaydet.

// audit_solver.js
const fs = require('fs');
const { v4: uuidv4 } = require('uuid');
const axios = require('axios');

const API_KEY = process.env.CAPTCHAAI_KEY || 'YOUR_API_KEY';
const AUDIT_FILE = 'captcha_audit.jsonl';

function logAudit(record) {
  fs.appendFileSync(AUDIT_FILE, JSON.stringify(record) + '\n');
}

async function solveWithAudit(sitekey, pageurl, initiator = 'unknown') {
  const requestId = uuidv4();
  const start = Date.now();
  const record = {
    request_id: requestId,
    timestamp: new Date().toISOString(),
    captcha_type: 'userrecaptcha',
    target_site: pageurl,
    initiator,
    status: 'submitted',
  };

  try {
    const submit = await axios.get('https://ocr.captchaai.com/in.php', {
      params: {
        key: API_KEY, method: 'userrecaptcha',
        googlekey: sitekey, pageurl, json: '1',
      },
    });

    if (submit.data.status !== 1) {
      record.status = 'submit_failed';
      record.error_code = submit.data.request;
      record.solve_time_ms = Date.now() - start;
      logAudit(record);
      return null;
    }

    record.task_id = submit.data.request;
    await new Promise(r => setTimeout(r, 15000));

    for (let i = 0; i < 25; i++) {
      const poll = await axios.get('https://ocr.captchaai.com/res.php', {
        params: { key: API_KEY, action: 'get', id: submit.data.request, json: '1' },
      });

      if (poll.data.status === 1) {
        record.status = 'solved';
        record.solve_time_ms = Date.now() - start;
        record.cost_estimate = 0.003;
        logAudit(record);
        return poll.data.request;
      }
      if (poll.data.request !== 'CAPCHA_NOT_READY') {
        record.status = 'failed';
        record.error_code = poll.data.request;
        record.solve_time_ms = Date.now() - start;
        logAudit(record);
        return null;
      }
      await new Promise(r => setTimeout(r, 5000));
    }

    record.status = 'timeout';
    record.solve_time_ms = Date.now() - start;
    logAudit(record);
    return null;
  } catch (e) {
    record.status = 'error';
    record.error_code = e.message.slice(0, 200);
    record.solve_time_ms = Date.now() - start;
    logAudit(record);
    throw e;
  }
}

Denetim günlüklerini sorgulama ve raporlama

Kayıtları toplamak yalnızca ilk adımdır; asıl değer, onları özet metriklere dönüştürdüğünüzde ortaya çıkar. Aşağıdaki fonksiyon belirli bir güne ait toplam istek sayısını, durum dağılımını, tahmini maliyeti ve medyan çözüm süresini üretir.

Günlük özet raporu

import json
from collections import Counter
from datetime import date

def daily_summary(log_file, target_date=None):
    """Generate a daily summary from audit logs."""
    target = target_date or date.today().isoformat()
    statuses = Counter()
    total_cost = 0
    solve_times = []

    with open(log_file) as f:
        for line in f:
            record = json.loads(line)
            if record["timestamp"].startswith(target):
                statuses[record["status"]] += 1
                total_cost += record.get("cost_estimate", 0)
                if record.get("solve_time_ms"):
                    solve_times.append(record["solve_time_ms"])

    print(f"Date: {target}")
    print(f"Total requests: {sum(statuses.values())}")
    print(f"Statuses: {dict(statuses)}")
    print(f"Estimated cost: ${total_cost:.2f}")
    if solve_times:
        print(f"Median solve time: {sorted(solve_times)[len(solve_times)//2]}ms")

daily_summary("captcha_audit.jsonl")

Bu çıktıyı her sabah çalıştıran bir cron işi, ekibinize çözüm oranındaki ani düşüşleri ve maliyet sapmalarını erkenden haber verir.

Saklama süresi ve depolama stratejisi

Denetim günlükleri hızla büyür, bu yüzden hacme göre bir depolama planı seçin. Aşağıdaki büyüklükler kaba tahminlerdir ve kayıt yapınıza göre değişir:

Hacim Günlük log boyutu Aylık depolama Öneri
100 çözüm/gün ~30 KB ~1 MB Yerel dosya
1.000 çözüm/gün ~300 KB ~10 MB Yerel dosya + döndürme
10.000 çözüm/gün ~3 MB ~100 MB Bir log toplayıcıya gönderin
100.000 çözüm/gün ~30 MB ~1 GB Merkezi loglama (ELK, Datadog)

Düşük hacimde yerel bir dosya ve logrotate yeterlidir; on binlerce çözümü aşan operasyonlarda kayıtları merkezi bir sisteme akıtmak hem sorgulamayı hem de saklama politikalarını yönetmeyi kolaylaştırır.

Sık karşılaşılan sorunlar ve çözümleri

Sorun Sebep Çözüm
Günlük dosyası aşırı büyüyor Döndürme yapılandırılmamış RotatingFileHandler ya da logrotate kullanın
Kayıtlar eksik kalıyor Loglamadan önce oluşan istisna Kaydı finally bloğunda yazın
Yüksek hacimde yavaş yazma Senkron dosya G/Ç Eşzamansız yazma veya arabelleğe alma kullanın
Tutarsız zaman damgaları Sistem saati sapması NTP kullanın; her zaman UTC'ye yazın

Denetlenebilir bir kayıt için kısa kontrol listesi

Üretime almadan önce günlükleyicinizi şu maddelere karşı gözden geçirin:

  • Kayıt her durumu yazıyor mu — solved, failed, timeout ve error? Yalnızca başarılı çözümleri kaydetmek, denetimin en kritik kısmını gizler.
  • Yazma işlemi bir finally bloğunda mı? İstisna, kaydı atlatmamalı.
  • API anahtarları, token'lar ve kişisel veri kayıt dışında mı tutuluyor?
  • Zaman damgaları UTC ve tek biçim mi? Sapmayı önlemek için NTP kullanın.
  • Döndürme (RotatingFileHandler ya da logrotate) ve saklama süresi tanımlı mı?

Sık sorulan sorular

Denetim günlükleri KVKK kapsamında kişisel veri sayılır mı?

Doğru kurulduğunda hayır. Kayıt yalnızca request_id, task_id, zaman damgası ve maliyet gibi meta veriyi tutar. Hedef sayfadan gelen kullanıcı bilgilerini veya CAPTCHA token'ını saklamadığınız sürece günlük, kişisel veri barındırmaz ve KVKK yükümlülüğünüzü basitleştirir.

Denetim günlüğü verilerini faturamla nasıl karşılaştırırım?

daily_summary çıktısındaki toplam istek sayısını ve tahmini maliyeti, CaptchaAI kullanım paneliyle yan yana koyun. İki değer arasındaki fark, kayıtsız kalan istekleri veya yanlış işaretlenmiş bir işi işaret eder.

Yüksek hacimde günlük yazmak botu yavaşlatır mı?

Senkron dosya yazımı, saniyede yüzlerce çözümde darboğaz olabilir. Bu noktaya geldiğinizde arabelleğe alma ya da eşzamansız yazma kullanın; kayıtları bir kuyruğa alıp toplu halde bir toplayıcıya göndermek de çözüm süresini etkilemeden ölçeklenmenizi sağlar.

Hangi CAPTCHA türleri için denetim günlüğü tutabilirim?

captcha_type alanı yönteme göre değişir, ancak yaklaşım her tür için aynıdır. CaptchaAI'nin desteklediği reCAPTCHA v2/v3, Cloudflare Turnstile, GeeTest v3, BLS ve görüntü/grid CAPTCHA'larının hepsini aynı kayıt şemasıyla izleyebilirsiniz.

Ne kadar süre saklamalıyım?

Operasyonel günlükler için 90 gün yaygın bir standarttır. Uyumluluk odaklı bir kayıt tutuyorsanız, tabi olduğunuz çerçevenin (KVKK, SOC 2, GDPR, HIPAA) saklama gereksinimini esas alın.

İlgili makaleler

Sonraki adımlar

Her CAPTCHA çözümünü hesap verebilir hale getirin — CaptchaAI API anahtarınızı alın ve ilk denetim kaydınızı bugün üretin.

İlgili kılavuzlar:

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