CAPTCHA'yı çözmek zaman ve para gerektirir. Aynı belirteç geçerlilik süresi içinde yeniden kullanılabiliyorsa önbelleğe alma, gereksiz API çağrılarını ortadan kaldırır. Bu kılavuz, hangi belirteçlerin önbelleğe alınabileceğini, ne kadar süre dayanabileceğini ve önbelleğe almanın güvenli bir şekilde nasıl uygulanacağını kapsar.
CAPTCHA türüne göre belirteç yaşam süreleri
| CAPTCHA türü | Jeton ömrü | Önbelleğe alınabilir mi? | Notlar |
|---|---|---|---|
| reCAPTCHA v2 | ~120 saniye | Sınırlı | Çoğu sitede tek seferlik kullanım |
| reCAPTCHA v3 | ~120 saniye | Sınırlı | Puan isteğe göre değişebilir |
| reCAPTCHA Kurumsal | ~120 saniye | Hayır | Aksiyona özel, tek kullanımlık |
| Cloudflare Turnstile | ~300 saniye | Evet, pencere içinde | Jeton, süresi dolana kadar yeniden kullanılabilir |
| Cloudflare doğrulama akışı | qa_session_cookie ~15–30 dk. |
Evet | Çerez oturum için yeniden kullanılabilir |
| Görüntü OCR'si | N/A (metin sonucu) | Evet | Sonuç hiçbir zaman sona ermez |
| GeeTest v3 | ~60 saniye | Hayır | Mücadeleye özel |
Önemli bilgi: Cloudflare doğrulama akışı (qa_session_cookie) ve Görüntü OCR en önbelleğe alınabilir olanlardır. reCAPTCHA belirteçlerinin kısa pencereleri vardır ve genellikle tek kullanımlıktır.
Önbelleğe alma çalıştığında
Önbelleğe alma şu durumlarda etkilidir:
- Aynı sayfa, birden fazla istek - örneğin, aynı formun birden çok kez gönderilmesi
- Cloudflare
qa_session_cookie– tek bir çözüm tüm oturumun kilidini açar - Toplu OCR – aynı resim tekrar tekrar görünüyor (ör. statik CAPTCHA)
- Ön QA testileme – jetonları ihtiyaç duyulmadan önce çözün
Önbelleğe alma aşağıdaki durumlarda çalışmaz:
- Site her jetonu yalnızca bir kez doğrular
- Belirteç belirli bir eyleme veya oturuma bağlıdır
- Belirtecin süresi zaten doldu
Python – bellek içi önbellek
import time
import hashlib
from typing import Optional
import requests
SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"
class TokenCache:
def __init__(self):
self.cache = {}
def _key(self, method: str, params: dict) -> str:
# Cache key from method + stable params
stable = {k: v for k, v in sorted(params.items())
if k not in ("key", "json")}
raw = f"{method}:{stable}"
return hashlib.sha256(raw.encode()).hexdigest()[:16]
def get(self, method: str, params: dict) -> Optional[str]:
key = self._key(method, params)
entry = self.cache.get(key)
if entry and entry["expires_at"] > time.time():
print(f"Cache HIT: {key}")
return entry["token"]
if entry:
del self.cache[key]
return None
def set(self, method: str, params: dict, token: str, ttl: int):
key = self._key(method, params)
self.cache[key] = {
"token": token,
"expires_at": time.time() + ttl,
}
print(f"Cached: {key} (TTL: {ttl}s)")
def invalidate(self, method: str, params: dict):
key = self._key(method, params)
self.cache.pop(key, None)
def cleanup(self):
now = time.time()
expired = [k for k, v in self.cache.items() if v["expires_at"] <= now]
for k in expired:
del self.cache[k]
# TTL per CAPTCHA type
TTL_MAP = {
"userrecaptcha": 100, # 120s lifetime, 20s safety margin
"turnstile": 240, # 300s lifetime, 60s margin
"cloudflare_challenge": 900,# 15min lifetime, 5min margin
"base64": 86400, # OCR result never expires — cache 24h
}
class CachedSolver:
def __init__(self, api_key: str):
self.api_key = api_key
self.cache = TokenCache()
def solve(self, method: str, params: dict) -> str:
# Check cache first
cached = self.cache.get(method, params)
if cached:
return cached
# Solve via API
token = self._api_solve(method, params)
ttl = TTL_MAP.get(method, 60)
self.cache.set(method, params, token, ttl)
return token
def _api_solve(self, method: str, params: dict) -> str:
data = {
"key": self.api_key,
"method": method,
"json": 1,
**params
}
resp = requests.post(SUBMIT_URL, data=data, timeout=15)
result = resp.json()
if result.get("status") != 1:
raise Exception(result.get("error_text", result.get("request")))
task_id = result["request"]
return self._poll(task_id)
def _poll(self, task_id: str, max_wait: int = 120) -> str:
elapsed = 0
while elapsed < max_wait:
time.sleep(5)
elapsed += 5
resp = requests.get(RESULT_URL, params={
"key": self.api_key,
"action": "get",
"id": task_id,
"json": 1
}, timeout=10)
result = resp.json()
if result.get("status") == 1:
return result["request"]
if result.get("request") == "CAPCHA_NOT_READY":
continue
raise Exception(result.get("error_text", result.get("request")))
raise Exception(f"Timeout: {task_id}")
# Usage
solver = CachedSolver(api_key="YOUR_API_KEY")
# First call — hits API
token1 = solver.solve("turnstile", {
"sitekey": "0x4AAAA-SITEKEY",
"pageurl": "https://example.com"
})
print(f"Token 1: {token1[:40]}...")
# Second call within TTL — cache hit, no API call
token2 = solver.solve("turnstile", {
"sitekey": "0x4AAAA-SITEKEY",
"pageurl": "https://example.com"
})
print(f"Token 2: {token2[:40]}...")
print(f"Same token: {token1 == token2}") # True
Node.js – bellek içi önbellek
const axios = require("axios");
const crypto = require("crypto");
const SUBMIT_URL = "https://ocr.captchaai.com/in.php";
const RESULT_URL = "https://ocr.captchaai.com/res.php";
const TTL_MAP = {
userrecaptcha: 100,
turnstile: 240,
cloudflare_challenge: 900,
base64: 86400,
};
class TokenCache {
constructor() {
this.cache = new Map();
}
_key(method, params) {
const stable = Object.entries(params)
.filter(([k]) => k !== "key" && k !== "json")
.sort(([a], [b]) => a.localeCompare(b))
.map(([k, v]) => `${k}=${v}`)
.join("&");
return crypto.createHash("sha256").update(`${method}:${stable}`).digest("hex").slice(0, 16);
}
get(method, params) {
const key = this._key(method, params);
const entry = this.cache.get(key);
if (entry && entry.expiresAt > Date.now()) {
console.log(`Cache HIT: ${key}`);
return entry.token;
}
if (entry) this.cache.delete(key);
return null;
}
set(method, params, token, ttlMs) {
const key = this._key(method, params);
this.cache.set(key, { token, expiresAt: Date.now() + ttlMs });
console.log(`Cached: ${key} (TTL: ${ttlMs / 1000}s)`);
}
}
class CachedSolver {
constructor(apiKey) {
this.apiKey = apiKey;
this.cache = new TokenCache();
}
async solve(method, params) {
const cached = this.cache.get(method, params);
if (cached) return cached;
const token = await this._apiSolve(method, params);
const ttl = (TTL_MAP[method] || 60) * 1000;
this.cache.set(method, params, token, ttl);
return token;
}
async _apiSolve(method, params) {
const resp = await axios.post(SUBMIT_URL, null, {
params: { key: this.apiKey, method, json: 1, ...params },
timeout: 15000,
});
if (resp.data.status !== 1) {
throw new Error(resp.data.error_text || resp.data.request);
}
return this._poll(resp.data.request);
}
async _poll(taskId, maxWait = 120000) {
let elapsed = 0;
while (elapsed < maxWait) {
await new Promise((r) => setTimeout(r, 5000));
elapsed += 5000;
const resp = await axios.get(RESULT_URL, {
params: { key: this.apiKey, action: "get", id: taskId, json: 1 },
timeout: 10000,
});
if (resp.data.status === 1) return resp.data.request;
if (resp.data.request === "CAPCHA_NOT_READY") continue;
throw new Error(resp.data.error_text || resp.data.request);
}
throw new Error("Timeout");
}
}
// Usage
(async () => {
const solver = new CachedSolver("YOUR_API_KEY");
const token1 = await solver.solve("turnstile", {
sitekey: "0x4AAAA-SITEKEY",
pageurl: "https://example.com",
});
console.log(`Token 1: ${token1.slice(0, 40)}...`);
const token2 = await solver.solve("turnstile", {
sitekey: "0x4AAAA-SITEKEY",
pageurl: "https://example.com",
});
console.log(`Token 2: ${token2.slice(0, 40)}...`);
console.log(`Same token: ${token1 === token2}`);
})();
Dağıtılmış sistemler için Redis önbelleği
Çok çalışanlı kurulumlarda bellek içi önbellek yerine Redis'i kullanın:
import redis
import json
r = redis.Redis(host="localhost", port=6379, db=0)
def cache_token(method, params, token, ttl):
key = f"captcha:{method}:{hash(frozenset(params.items()))}"
r.setex(key, ttl, token)
def get_cached_token(method, params):
key = f"captcha:{method}:{hash(frozenset(params.items()))}"
return r.get(key)
Redis, TTL'nin sona ermesini otomatik olarak yönetir ve birden fazla işlemde çalışır.
Ön QA testileme modeli
Belirteçleri ihtiyaç duyulmadan önce çözün. Hazır jetonlardan oluşan bir arabellek tutun:
from collections import deque
from threading import Thread
token_buffer = deque(maxlen=5)
def pre_solve_worker(solver, method, params):
while True:
if len(token_buffer) < 3:
try:
token = solver._api_solve(method, params)
ttl = TTL_MAP.get(method, 60)
token_buffer.append({
"token": token,
"expires_at": time.time() + ttl
})
except Exception as e:
print(f"Ön QA testi failed: {e}")
time.sleep(2)
# Start ön QA testir in background
thread = Thread(
target=pre_solve_worker,
args=(solver, "turnstile", {"sitekey": "0x4AAAA-KEY", "pageurl": "https://example.com"}),
daemon=True
)
thread.start()
# Consume ön QA testid tokens
def get_presolved():
while token_buffer:
entry = token_buffer.popleft()
if entry["expires_at"] > time.time():
return entry["token"]
return None
Önbellek geçersiz kılma kuralları
| Tetikleyici | Eylem |
|---|---|
| Belirteç hedef site tarafından reddedildi | Geçersiz kıl ve yeniden çöz |
| TTL'nin süresi doldu | Önbellekten otomatik olarak kaldırıldı |
| Proxy değiştirildi | Cloudflare belirteçlerini geçersiz kılma (IP'ye bağlı) |
| Site CAPTCHA yapılandırmasını güncelledi | Bu site için önbelleğe alınmış tüm jetonları temizle |
Sorun giderme
| Sorun | Sebep | Düzeltme |
|---|---|---|
| Önbelleğe alınan jeton reddedildi | Jetonun süresi dolmuş veya tek kullanımlık | Bu tür için TTL'yi azaltın veya önbelleğe almayı devre dışı bırakın |
| Önbellek asla isabet etmez | Paramlar aramalar arasında farklılık gösterir | Karma işleminden önce parametreleri normalleştirin |
| Redis'teki eski belirteçler | TTL çok uzun | Güvenlik marjıyla TTL'yi düşürün |
| Bellek büyümesi | Temizleme yok | cleanup()'yi periyodik olarak arayın veya Redis'i TTL ile kullanın |
SSS
reCAPTCHA v2 belirteçlerini önbelleğe alabilir miyim?
Bazen. Birçok site jetonu yalnızca bir kez kabul eder. Aynı jetonu iki kez göndererek test edin; ikinci gönderim başarılı olursa, önbelleğe alma o site için işe yarar.
Önbelleğe alma ne kadar tasarruf sağlayabilir?
Cloudflare doğrulama akışı için tek bir çözüm, 15-30 dakikalık bir oturumun tamamını kapsayabilir. Bu, aynı alanda yüksek frekanslı kazıma maliyetlerini %90'ın üzerinde azaltabilir.
Ön QA testi buna değer mi?
Evet, boru hattınızda öngörülebilir bir talep varsa. Ön QA testileme, talebin düşmesi durumunda potansiyel token israfı pahasına bekleme süresini ortadan kaldırır.
CaptchaAI ile CAPTCHA maliyetlerini optimize edin
Belirteçleri önbelleğe almaya şuradan başlayın:captchaai.com.