حل CAPTCHA النصوص السيريلية يعتمد على شيء واحد: محرك تعرّف ضوئي (OCR) يفهم الأبجدية السيريلية ويعيد رموز Unicode الصحيحة بدلاً من نظيرها اللاتيني المتشابه. في CaptchaAI تحصل على ذلك بضبط المعامل language=2 عند إرسال الصورة، فيميّز المحرك بين الحرف السيريلي В والحرف اللاتيني B ويرجع نقطة الترميز المطابقة للنموذج. هذا الفرق الدقيق هو ما يقرر قبول التحقق أو رفضه على المواقع الروسية والأوكرانية والبلغارية والصربية.
الأحرف السيريلية مقابل اللاتينية: لماذا يفشل OCR القياسي
المشكلة ليست في وضوح الصورة، بل في الترميز. كثير من الأحرف السيريلية يشبه اللاتينية شكلاً لكنه يحمل نقطة ترميز Unicode مختلفة تماماً. محرك OCR مضبوط على اللاتينية فقط سيقرأ الحرف بصرياً ويعيد النظير اللاتيني الخاطئ، فيرفض النموذج نصاً يبدو صحيحاً للعين.
| الشكل الظاهر | اللاتيني | السيريلي | ملاحظة Unicode |
|---|---|---|---|
| A | A (U+0041) | А (U+0410) | نقطتا ترميز مختلفتان |
| B | B (U+0042) | В (U+0412) | السيريلي اسمه "Ve" |
| C | C (U+0043) | С (U+0421) | السيريلي اسمه "Es" |
| E | E (U+0045) | Е (U+0415) | ترميز مختلف |
| H | H (U+0048) | Н (U+041D) | السيريلي اسمه "En" |
| O | O (U+004F) | О (U+041E) | نقطتا ترميز مختلفتان |
| P | P (U+0050) | Р (U+0420) | السيريلي اسمه "Er" |
القاعدة العملية: إرسال نقطة الترميز الخاطئة يجعل التحقق يرفض نصاً مطابقاً في المظهر. لذلك لا يكفي "قراءة" الحرف؛ يجب إعادته بترميزه السيريلي الأصلي.
حل CAPTCHA النصوص السيريلية في Python
يتبع التدفق أربع خطوات: حمّل الصورة وحوّلها إلى Base64، أرسلها مع language=2، استطلع النتيجة دورياً حتى تجهز، ثم تحقّق من أن النص المُعاد سيريلي فعلاً قبل تمريره إلى النموذج. الدالة verify_cyrillic تفحص نطاق Unicode من U+0400 إلى U+04FF لتأكيد ذلك.
import requests
import base64
import time
API_KEY = "YOUR_API_KEY"
SUBMIT_URL = "https://ocr.captchaai.com/in.php"
RESULT_URL = "https://ocr.captchaai.com/res.php"
def solve_cyrillic_captcha(image_path: str) -> str:
"""Solve a Cyrillic text image CAPTCHA."""
with open(image_path, "rb") as f:
image_b64 = base64.b64encode(f.read()).decode()
resp = requests.post(SUBMIT_URL, data={
"key": API_KEY,
"method": "base64",
"body": image_b64,
"language": 2, # Non-Latin character support
"json": 1,
}, timeout=30).json()
if resp.get("status") != 1:
raise RuntimeError(f"Submit: {resp.get('request')}")
task_id = resp["request"]
for _ in range(24):
time.sleep(5)
poll = requests.get(RESULT_URL, params={
"key": API_KEY, "action": "get", "id": task_id, "json": 1,
}, timeout=15).json()
if poll.get("request") == "CAPCHA_NOT_READY":
continue
if poll.get("status") == 1:
return poll["request"]
raise RuntimeError(f"Solve: {poll.get('request')}")
raise RuntimeError("Timeout")
def solve_cyrillic_from_session(session: requests.Session,
captcha_url: str) -> str:
"""Solve a Cyrillic CAPTCHA within a session context."""
resp = session.get(captcha_url, timeout=15)
image_b64 = base64.b64encode(resp.content).decode()
submit = requests.post(SUBMIT_URL, data={
"key": API_KEY,
"method": "base64",
"body": image_b64,
"language": 2,
"json": 1,
}, timeout=30).json()
if submit.get("status") != 1:
raise RuntimeError(f"Submit: {submit.get('request')}")
task_id = submit["request"]
for _ in range(24):
time.sleep(5)
poll = requests.get(RESULT_URL, params={
"key": API_KEY, "action": "get", "id": task_id, "json": 1,
}, timeout=15).json()
if poll.get("request") == "CAPCHA_NOT_READY":
continue
if poll.get("status") == 1:
return poll["request"]
raise RuntimeError(f"Solve: {poll.get('request')}")
raise RuntimeError("Timeout")
def verify_cyrillic(text: str) -> bool:
"""Verify that solved text contains Cyrillic characters."""
return any('\u0400' <= ch <= '\u04FF' for ch in text)
# --- Russian website form flow ---
def solve_russian_form(form_url: str, captcha_url: str,
form_data: dict) -> requests.Response:
"""Complete a Russian website form with CAPTCHA."""
session = requests.Session()
session.headers.update({
"Accept-Language": "ru-RU,ru;q=0.9",
})
# Establish session
session.get(form_url, timeout=15)
# Solve CAPTCHA
captcha_text = solve_cyrillic_from_session(session, captcha_url)
print(f"Cyrillic CAPTCHA: {captcha_text}")
if verify_cyrillic(captcha_text):
print("Confirmed: contains Cyrillic characters")
form_data["captcha"] = captcha_text
return session.post(form_url, data=form_data, timeout=30)
# --- Usage ---
text = solve_cyrillic_captcha("russian_captcha.png")
print(f"Solved: {text}")
print(f"Is Cyrillic: {verify_cyrillic(text)}")
print(f"Unicode codepoints: {[hex(ord(c)) for c in text]}")
ملاحظة مهمة على الجلسة: عند العمل داخل نموذج حقيقي، اربط حل الكابتشا بالجلسة نفسها التي فتحت بها الصفحة (الدالة solve_russian_form). الترويسة Accept-Language: ru-RU تجعل الطلب يبدو متسقاً مع مصدر المحتوى، وتمرير الرمز داخل الجلسة ذاتها يمنع قراءة عرض قديم للنموذج.
حل CAPTCHA السيريلية في JavaScript
نفس المنطق ينطبق في بيئة Node.js: أرسل الصورة كـ Base64 مع language: "2"، ثم استطلع res.php حتى تصل الاستجابة. الدالة showCodepoints مفيدة للتصحيح لأنها تطبع نقطة الترميز لكل حرف، فتتأكد بصرياً أن ما عاد هو С السيريلي (U+0421) وليس C اللاتيني (U+0043).
const API_KEY = "YOUR_API_KEY";
const SUBMIT_URL = "https://ocr.captchaai.com/in.php";
const RESULT_URL = "https://ocr.captchaai.com/res.php";
const fs = require("fs");
async function solveCyrillicCaptcha(imagePath) {
const imageB64 = fs.readFileSync(imagePath, "base64");
const body = new URLSearchParams({
key: API_KEY,
method: "base64",
body: imageB64,
language: "2",
json: "1",
});
const resp = await (await fetch(SUBMIT_URL, { method: "POST", body })).json();
if (resp.status !== 1) throw new Error(`Submit: ${resp.request}`);
const taskId = resp.request;
for (let i = 0; i < 24; i++) {
await new Promise((r) => setTimeout(r, 5000));
const url = `${RESULT_URL}?key=${API_KEY}&action=get&id=${taskId}&json=1`;
const poll = await (await fetch(url)).json();
if (poll.request === "CAPCHA_NOT_READY") continue;
if (poll.status === 1) return poll.request;
throw new Error(`Solve: ${poll.request}`);
}
throw new Error("Timeout");
}
function isCyrillic(text) {
return /[\u0400-\u04FF]/.test(text);
}
function showCodepoints(text) {
return [...text].map((ch) => `${ch}=U+${ch.codePointAt(0).toString(16).padStart(4, "0")}`);
}
// Usage
const text = await solveCyrillicCaptcha("russian_captcha.png");
console.log(`Solved: ${text}`);
console.log(`Is Cyrillic: ${isCyrillic(text)}`);
console.log(`Codepoints: ${showCodepoints(text).join(", ")}`);
أنماط كابتشا السيريلية الشائعة
لا تأتي هذه الاختبارات بصيغة واحدة. معرفة النمط الذي تواجهه يساعدك على ضبط التوقعات وقراءة النتيجة قبل إرسالها للنموذج:
| النمط | الوصف | مثال |
|---|---|---|
| كلمة سيريلية خالصة | كلمة روسية عشوائية | ШКАФ، ПИРОГ |
| مزيج لاتيني + سيريلي | النصان معاً في صورة واحدة | ABСDе (A، B، D لاتينية؛ С، е سيريلية) |
| أرقام مكتوبة بالحروف | عدد بالكلمات | ПЯТЬ (خمسة)، ТРИ (ثلاثة) |
| مسألة حسابية بالروسية | العملية مكتوبة كلمات | два плюс три = ؟ |
| نص سيريلي مشوّه | حروف روسية ملتوية | تحدي OCR قياسي مع السيريلية |
متى تحتاج هذا فعلياً: سيناريو واقعي
تخيّل فريق بيانات في القاهرة أو دبي يبني أداة مراقبة أسعار على متاجر ومنصات روسية وأوكرانية ضمن نطاق عمل مصرّح به أو مملوك للعميل. تعمل الأداة بسلاسة حتى تصطدم بنموذج تسجيل أو بحث محمي بكابتشا نصية سيريلية. الفريق يقرأ الحرف О بصرياً ويرسله كـ O لاتيني، فيرفضه الخادم مراراً دون رسالة خطأ واضحة.
الحل ليس تخمين الأحرف يدوياً، بل تمرير الصورة إلى محرك OCR في CaptchaAI مع language=2 وإعادة النص بترميزه السيريلي الصحيح داخل الجلسة نفسها. بهذا يتحول مسار هش ومتقطع إلى خطوة قابلة للتكرار ضمن سير عمل مراقب وواضح، مع إبقاء الحل مقصوراً على البيئات المملوكة أو المصرّح بها.
استكشاف الأخطاء وإصلاحها
| المشكلة | السبب | الإجراء |
|---|---|---|
| النموذج يرفض نصاً يبدو صحيحاً | تطابق شكلي بين حرف لاتيني وسيريلي | افحص نقاط الترميز — А (U+0410) ليست A (U+0041) |
| الأحرف تظهر مشوّهة عند العرض | ترميز غير صحيح | استخدم UTF-8 في كل مكان واضبط response.encoding = 'utf-8' |
| نص مختلط النصين يعود جزئياً خاطئاً | خلط المحرك بين اللاتيني والسيريلي | مع language=2 يميّز CaptchaAI بينهما بدقة |
| أحرف أوكرانية مفقودة | ґ، є، і، ї غير مُتعرَّف عليها | هذه مدعومة عند ضبط language=2 |
| حساسية حالة الأحرف | الحرف الكبير والصغير مختلفان | أرسل النص كما أعاده CaptchaAI بالضبط |
أسئلة شائعة
ما الفرق بين ضبط language=2 وترك الإعداد الافتراضي؟
الإعداد الافتراضي يوجّه المحرك نحو الأحرف اللاتينية، فيعيد النظير اللاتيني لأي حرف سيريلي متشابه. ضبط language=2 يفعّل نماذج تدعم الأحرف غير اللاتينية، فتعود نقاط الترميز السيريلية الصحيحة ضمن النطاق U+0400 إلى U+04FF. هذا الفارق هو ما يمنع رفض النموذج.
هل يتعرّف الحل على الأحرف الأوكرانية الخاصة؟
نعم. الأوكرانية تستخدم أحرفاً غير موجودة في الروسية مثل ґ (U+0491) وє (U+0454) وі (U+0456) وї (U+0457)، وهي مدعومة عند ضبط language=2. يتعامل المحرك مع نطاقات السيريلية المختلفة بما فيها الروسية والأوكرانية والبلغارية والصربية.
كم تكلّف عملية الحل وكيف تُحتسب؟
يعتمد CaptchaAI على تسعير قائم على الـ Threads وليس على كل عملية حل، مع حلول غير محدودة لكل Thread خلال الشهر. تبدأ الخطط من BASIC بسعر $15 شهرياً و5 threads متزامنة، فيتحدد ما يهم عملياً بعدد الطلبات المتوازية لا بعدد الكابتشا. راجع صفحة الأسعار الرسمية للتفاصيل المحدّثة.
هل يحل CaptchaAI reCAPTCHA على المواقع الروسية أيضاً؟
نعم، يدعم CaptchaAI حل reCAPTCHA v2 وv3 وCloudflare Turnstile إلى جانب كابتشا الصور، لكن هذا التدفق المبني على language=2 مخصص لكابتشا النص السيريلي المصوّر. اختبار reCAPTCHA يستخدم طريقة إرسال مختلفة عبر الـ API.
الخطوات التالية
- البدء السريع مع CaptchaAI: حلّ أول كابتشا في 5 دقائق
- حلّ reCAPTCHA v2 عبر الـ API خطوة بخطوة
- التعامل مع Cloudflare Turnstile عبر واجهة API
- حلّ GeeTest v3 باستخدام الـ API