حل اختبار CAPTCHA يكلف الوقت والمال. إذا كان من الممكن إعادة استخدام الرمز المميز نفسه ضمن نافذة الصلاحية الخاصة به، فإن التخزين المؤقت يلغي استدعاءات واجهة برمجة التطبيقات المتكررة. يغطي هذا الدليل الرموز المميزة القابلة للتخزين المؤقت، ومدة استمرارها، وكيفية تنفيذ التخزين المؤقت بأمان.
عمر الرمز المميز حسب نوع CAPTCHA
| نوع التحقق | عمر الرمز | قابلة للتخزين المؤقت؟ | ملاحظات |
|---|---|---|---|
| reCAPTCHA v2 | ~120 ثانية | محدودة | الاستخدام لمرة واحدة في معظم المواقع |
| reCAPTCHA v3 | ~120 ثانية | محدودة | قد تختلف النتيجة لكل طلب |
| reCAPTCHA المؤسسة | ~120 ثانية | لا | عمل محدد، استخدام واحد |
| Cloudflare Turnstile | ~300 ثانية | نعم، داخل النافذة | رمز قابل لإعادة الاستخدام حتى انتهاء الصلاحية |
| Cloudflare Challenge | cf_clearance ~ 15-30 دقيقة |
نعم | ملف تعريف الارتباط قابل لإعادة الاستخدام للجلسة |
| ** التعرف الضوئي على الحروف للصورة ** | N/A (نتيجة نصية) | نعم | النتيجة لا تنتهي أبدا |
| GeeTest v3 | ~60 ثانية | لا | خاص بالتحدي |
الرؤية الرئيسية: Cloudflare Challenge (cf_clearance) وImage OCR هما الأكثر قابلية للتخزين المؤقت. تحتوي رموز reCAPTCHA المميزة على نوافذ قصيرة وغالبًا ما تكون للاستخدام مرة واحدة.
عندما يعمل التخزين المؤقت
يكون التخزين المؤقت فعالاً عندما:
- نفس الصفحة، طلبات متعددة - على سبيل المثال، إرسال نفس النموذج عدة مرات
- Cloudflare
cf_clearance— حل واحد يفتح الجلسة بأكملها - التعرف الضوئي على الحروف بكميات كبيرة - تظهر الصورة نفسها بشكل متكرر (على سبيل المثال، اختبار CAPTCHA الثابت)
- الحل المسبق - حل الرموز المميزة قبل الحاجة إليها
التخزين المؤقت لا يعمل عندما:
- يقوم الموقع بالتحقق من صحة كل رمز مميز مرة واحدة فقط
- يرتبط الرمز المميز بإجراء أو جلسة محددة
- لقد انتهت صلاحية الرمز المميز بالفعل
بايثون - ذاكرة التخزين المؤقت في الذاكرة
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 - ذاكرة التخزين المؤقت في الذاكرة
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}`);
})();
Redis ذاكرة التخزين المؤقت للأنظمة الموزعة
بالنسبة للإعدادات متعددة العمال، استخدم Redis بدلاً من ذاكرة التخزين المؤقت في الذاكرة:
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 ويعمل عبر عمليات متعددة.
نمط ما قبل الحل
حل الرموز قبل الحاجة إليها. احتفظ بمخزن مؤقت من الرموز الجاهزة:
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"Pre-solve failed: {e}")
time.sleep(2)
# Start pre-solver in background
thread = Thread(
target=pre_solve_worker,
args=(solver, "turnstile", {"sitekey": "0x4AAAA-KEY", "pageurl": "https://example.com"}),
daemon=True
)
thread.start()
# Consume pre-solved tokens
def get_presolved():
while token_buffer:
entry = token_buffer.popleft()
if entry["expires_at"] > time.time():
return entry["token"]
return None
قواعد إبطال ذاكرة التخزين المؤقت
| المحفّز | العمل |
|---|---|
| تم رفض الرمز المميز بواسطة الموقع المستهدف | إبطال وإعادة الحل |
| انتهت صلاحية TTL | تتم إزالتها تلقائيًا من ذاكرة التخزين المؤقت |
| تم تغيير الوكيل | إبطال رموز Cloudflare المميزة (المرتبطة بـ IP) |
| تم تحديث إعدادات CAPTCHA للموقع | مسح جميع الرموز المميزة المخزنة مؤقتًا لهذا الموقع |
استكشاف الأخطاء وإصلاحها
| مشكلة | السبب | إصلاح |
|---|---|---|
| تم رفض الرمز المميز المخبأ | انتهت صلاحية الرمز المميز أو تم استخدامه مرة واحدة | قم بتقليل TTL أو تعطيل التخزين المؤقت لهذا النوع |
| ذاكرة التخزين المؤقت لا تصل أبدا | تختلف المعلمات بين المكالمات | تطبيع المعلمات قبل التجزئة |
| الرموز القديمة في Redis | TTL طويل جدًا | انخفاض TTL مع هامش أمان |
| نمو الذاكرة | لا تنظيف | اتصل بـ cleanup() بشكل دوري أو استخدم Redis مع TTL |
الأسئلة الشائعة
هل يمكنني تخزين الرموز المميزة reCAPTCHA v2؟
في بعض الأحيان. تقبل العديد من المواقع الرمز المميز مرة واحدة فقط. قم بالاختبار عن طريق إرسال نفس الرمز المميز مرتين - إذا نجح الإرسال الثاني، فسيعمل التخزين المؤقت لهذا الموقع.
كم يمكن أن يوفر التخزين المؤقت؟
بالنسبة إلى Cloudflare Challenge، يمكن أن يغطي الحل الواحد جلسة كاملة مدتها 15-30 دقيقة. يمكن أن يؤدي ذلك إلى تقليل التكاليف بنسبة تزيد عن 90% للتجريف عالي التردد على نفس النطاق.
هل الحل المسبق يستحق العناء؟
نعم، إذا كان خط الأنابيب الخاص بك يحتوي على طلب يمكن التنبؤ به. يؤدي الحل المسبق إلى التخلص من وقت الانتظار على حساب هدر الرمز المميز المحتمل في حالة انخفاض الطلب.
قم بتحسين تكاليف اختبار CAPTCHA باستخدام CaptchaAI
ابدأ في تخزين الرموز المميزة فيcaptchaai.com.