验证码写着“ШКАФ”,你一字不差照抄,系统却报“验证码错误”——问题不在看错字母,而在西里尔字母里有一批和拉丁字母“长得一样、编码不同”的字符,如西里尔 А(U+0410)与拉丁 A(U+0041)。俄语、乌克兰语、保加利亚语、塞尔维亚语网站验证码常踩这个坑。
CaptchaAI 图片 OCR 求解器的 language=2 参数专门识别非拉丁字符集,返回正确的西里尔 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" |
代码实战:Python 与 Node.js 双语言示例
Python:识别西里尔文图片验证码
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]}")
language=2 切到西里尔感知模型;verify_cyrillic() 校验结果是否落在 U+0400–U+04FF。
Node.js:处理俄语网站的验证码
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(", ")}`);
逻辑同 Python:带上 language: "2",每 5 秒轮询一次,用正则确认结果。
常见西里尔验证码文本类型
| 类型 | 说明 | 示例 |
|---|---|---|
| 纯西里尔文字 | 随机的俄语单词 | ШКАФ(衣柜)、ПИРОГ(馅饼) |
| 拉丁 + 西里尔混排 | 一张图里同时出现两种字符集 | ABСDе(A、B、D 是拉丁字母;С、е 是西里尔字母) |
| 用文字拼出的西里尔数字 | 数字以单词形式出现 | ПЯТЬ(五)、ТРИ(三) |
| 俄语文字算式 | 算术题写成文字 | два плюс три = ?(“二加三等于几”) |
| 扭曲变形的西里尔字母 | 文字被拉伸、倾斜、加噪点 | 西里尔字母版本的标准 OCR 干扰项 |
跨境选品团队怎么踩坑:Ozon、Wildberries 场景
做 Ozon、Wildberries 跨境选品或比价的团队几乎每天都会碰到上表这几种类型:
- 商品详情页登录墙:纯西里尔单词验证码
- 比价工具批量请求:中西混排验证码更常见
带上 language=2 后,这两类基本不受影响。
常见报错与处理方式
| 问题 | 原因 | 处理方式 |
|---|---|---|
| 文字看着没错,表单仍报错 | 拉丁/西里尔同形字冲突 | 核对代码点:西里尔 А (U+0410) ≠ 拉丁 A (U+0041) |
| 终端打印出来是乱码 | 编码设置错误 | 全程 UTF-8;设置 response.encoding = 'utf-8' |
| 混排验证码只对一部分 | OCR 把两种字符集搞混 | 提交时带上 language=2 |
| 乌克兰语字母识别不出来 | ґ、є、і、ї 未被识别 | language=2 同样支持这几个字符 |
| 大小写提交后依然报错 | 验证码区分大小写 | 严格按返回的大小写提交 |
常见问题
CaptchaAI 支持哪些西里尔语言的验证码?
不止俄语,language=2 也覆盖乌克兰语、保加利亚语、塞尔维亚语,含 ґ、є、і、ї。
明明照着输入了,表单为什么还是报错?
大概率是同形字:重打的字母外观一样,代码点不同。直接提交返回的 token,别手动重打。
language=2 必须传吗?
必须,否则字符大概率被当成拉丁字母误判。
识别成功了,打印出来却是乱码?
本地编码问题,检查输出环境是否统一 UTF-8,Python 里设置 response.encoding = 'utf-8' 即可。
西里尔和拉丁字母混排会影响识别准确率吗?
不会,CaptchaAI 会为每个字符返回正确代码点,用 verify_cyrillic() 校验即可。
立即开始处理西里尔验证码
西里尔验证码靠人工核对代码点很难保证一致,用 language=2 对接更稳定——获取 CaptchaAI API 密钥即可开始测试。
相关指南:
- 中文网站验证码识别方案
- 多语言图片验证码字符集详解
- 验证码本地化与语言参数设置