Liefert ein Bild-CAPTCHA das lateinische B statt des kyrillischen В oder bei chinesischen Schriftzeichen gar kein Ergebnis, liegt das selten am Bild und fast immer an einem einzigen Parameter. language teilt dem Image/OCR-Solver von CaptchaAI mit, welchen Zeichensatz er erwarten soll: 0 für lateinische Schrift, 1 für Kyrillisch, 2 für alle nicht-lateinischen oder gemischten Schriften. Steht der Wert falsch, vergleicht der Solver das Bild mit den falschen Zeichenmodellen – die Antwort sieht plausibel aus, passt aber nicht in das Zielformular.
Diese Referenz ordnet jeden Wert einem Schriftsystem zu, zeigt die Umsetzung in Python und JavaScript und erklärt, woran Sie Homoglyphen-Fehler erkennen. Der Solver deckt über 27.500 Bild-CAPTCHA-Varianten in mehreren Schreibsystemen ab; welches Erkennungsmodell greift, entscheiden Sie mit diesem einen Wert.
Die drei Werte des language-Parameters
| Wert | Zeichensätze | Geeignet für |
|---|---|---|
0 |
Nicht angegeben (Standard) | Rein lateinische CAPTCHAs – Deutsch, Englisch, Französisch, Spanisch |
1 |
Nur Kyrillisch | Russische, ukrainische, bulgarische CAPTCHAs |
2 |
Nicht-lateinische Zeichen | Chinesisch, Japanisch, Koreanisch, Arabisch, Kyrillisch, gemischte Schriften |
Zeichensatz je Bild-CAPTCHA-Inhalt: die Zuordnung
| CAPTCHA-Inhalt | Wert | Begründung |
|---|---|---|
| Lateinische Buchstaben + Ziffern | 0 oder weglassen |
Standardmodell für lateinische Erkennung |
| Russischer Text | 1 oder 2 |
Kyrillisch-spezifisches Modell |
| Chinesische Schriftzeichen | 2 |
CJK-Zeichensatz erforderlich |
| Japanisches Hiragana/Katakana | 2 |
Nicht-lateinische Erkennung erforderlich |
| Arabische Schrift | 2 |
Nicht-lateinische Erkennung erforderlich |
| Koreanisches Hangul | 2 |
Nicht-lateinische Erkennung erforderlich |
| Gemischt Lateinisch + Kyrillisch | 2 |
Verarbeitet mehrere Schriftsysteme |
| Nur Ziffern | 0 oder weglassen |
Ziffern sind über alle Modelle identisch |
Faustregel: Sobald ein Zeichen außerhalb des lateinischen Alphabets auftauchen kann, ist 2 die sichere Wahl. Bei reinen Ziffern-CAPTCHAs lassen Sie den Parameter weg.
Homoglyphen: der häufigste stille Fehler
Kyrillisch und Latein teilen sich ein gutes Dutzend optisch identischer Zeichen: В/B, Е/E, о/o, р/p, с/c. Mit language=0 gibt der Solver für ein russisches CAPTCHA deshalb eine lateinische Zeichenkette zurück, die auf dem Bildschirm völlig korrekt aussieht. Erst das Zielformular lehnt sie ab, und im Log steht ein Ergebnis, das niemand als Fehler erkennt – der Aufruf war ja erfolgreich.
Prüfen Sie bei kyrillischen und gemischten Quellen deshalb nicht die Optik, sondern die Codepoints. hex(ord(zeichen)) zeigt sofort, ob ein В im Bereich 0x0400–0x04FF liegt oder ein lateinisches B aus dem ASCII-Bereich. Als Assertion in der Testsuite fängt dieselbe Prüfung eine falsche Konfiguration ab, bevor sie tausende Datensätze verunreinigt.
Praxisbeispiel: ein Crawler, drei Schriftsysteme
Ein Datenteam in München überwacht öffentliche Ausschreibungs- und Registerportale in drei Märkten: deutschsprachige Portale mit lateinischen Bild-CAPTCHAs, ein russischsprachiges Portal mit kyrillischen Abfragen und ein chinesisches Lieferantenverzeichnis. Alle drei Jobs laufen als Worker auf demselben Hetzner-Server und teilen sich eine einzige Solver-Funktion.
Der Fehler, der solche Setups zuverlässig ausbremst, ist ein global gesetzter Standardwert. Belastbarer ist der Zeichensatz als Eigenschaft der Quelle – ein Feld in derselben YAML-Konfiguration, in der bereits Basis-URL, Proxy und Abfrageintervall stehen:
portal_de→ Parameter weglassen (entspricht0)portal_ru→language: 1portal_cn→language: 2
Beim Deployment über GitLab CI wandert diese Zuordnung mit der Konfiguration statt mit dem Code: Kommt ein viertes Portal hinzu, ändert sich eine Zeile YAML und keine Verzweigung im Solver-Aufruf. Bei unbekanntem Schriftsystem starten Sie mit 2 und schärfen den Wert nach den ersten hundert Lösungen anhand der Codepoints nach.
Python: Zeichensatz pro Aufruf steuern
Das folgende Beispiel übergibt language als Argument und lässt den Parameter bei 0 bewusst weg – so bleibt die Payload für lateinische Aufgaben identisch mit dem Standardfall. Nach dem Absenden an in.php fragt die Schleife den Status über res.php ab, bis ein Ergebnis vorliegt oder das Timeout greift. Die Hilfsfunktion detect_script wertet anschließend aus, welches Schriftsystem tatsächlich zurückkam.
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_image_captcha(image_path: str, language: int = 0) -> str:
"""Solve an image CAPTCHA with the specified language setting.
Args:
image_path: Path to the CAPTCHA image file.
language: 0=Latin, 1=Cyrillic, 2=non-Latin/mixed.
"""
with open(image_path, "rb") as f:
image_b64 = base64.b64encode(f.read()).decode()
params = {
"key": API_KEY,
"method": "base64",
"body": image_b64,
"json": 1,
}
# Only set language if non-default
if language > 0:
params["language"] = language
resp = requests.post(SUBMIT_URL, data=params, 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 detect_script(text: str) -> str:
"""Detect the primary script of solved text."""
for ch in text:
cp = ord(ch)
if 0x0400 <= cp <= 0x04FF:
return "cyrillic"
if 0x4E00 <= cp <= 0x9FFF:
return "cjk"
if 0x3040 <= cp <= 0x30FF:
return "japanese"
if 0xAC00 <= cp <= 0xD7AF:
return "korean"
if 0x0600 <= cp <= 0x06FF:
return "arabic"
if 0x0590 <= cp <= 0x05FF:
return "hebrew"
return "latin"
# --- Usage examples ---
# Latin CAPTCHA (default)
latin_text = solve_image_captcha("english_captcha.png", language=0)
print(f"Latin: {latin_text} (script: {detect_script(latin_text)})")
# Russian CAPTCHA
cyrillic_text = solve_image_captcha("russian_captcha.png", language=1)
print(f"Cyrillic: {cyrillic_text} (script: {detect_script(cyrillic_text)})")
# Chinese CAPTCHA
chinese_text = solve_image_captcha("chinese_captcha.png", language=2)
print(f"CJK: {chinese_text} (script: {detect_script(chinese_text)})")
# Mixed script CAPTCHA
mixed_text = solve_image_captcha("mixed_captcha.png", language=2)
print(f"Mixed: {mixed_text} (script: {detect_script(mixed_text)})")
Im Produktivbetrieb lohnt sich ein Abgleich: Passt das erkannte Schriftsystem nicht zur Quelle, gehört der Fall ins Log – mit Solver-Typ, Lösungszeit und Fehlercode.
JavaScript: Bild-CAPTCHAs mit Sprachhinweis lösen
In Node.js ist der Ablauf derselbe: ein fetch gegen in.php, danach eine Schleife, die res.php abfragt. Zusätzlich bildet eine kleine Map Sprachhinweise wie „russian“, „chinese“ oder „auto“ auf den Parameterwert ab. Das ist praktisch, wenn die Quelle ohnehin ein Sprachkürzel mitliefert und Sie es nicht im Solver-Aufruf verzweigen wollen.
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 solveImageCaptcha(imagePath, language = 0) {
const imageB64 = fs.readFileSync(imagePath, "base64");
const params = {
key: API_KEY,
method: "base64",
body: imageB64,
json: "1",
};
if (language > 0) params.language = String(language);
const body = new URLSearchParams(params);
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 detectScript(text) {
for (const ch of text) {
const cp = ch.codePointAt(0);
if (cp >= 0x0400 && cp <= 0x04ff) return "cyrillic";
if (cp >= 0x4e00 && cp <= 0x9fff) return "cjk";
if (cp >= 0x3040 && cp <= 0x30ff) return "japanese";
if (cp >= 0xac00 && cp <= 0xd7af) return "korean";
if (cp >= 0x0600 && cp <= 0x06ff) return "arabic";
}
return "latin";
}
// Auto-detect-and-solve helper
async function solveWithAutoLanguage(imagePath, hint = "auto") {
const languageMap = {
latin: 0,
cyrillic: 1,
russian: 1,
chinese: 2,
japanese: 2,
korean: 2,
arabic: 2,
auto: 2, // language=2 handles all scripts
};
const language = languageMap[hint] ?? 2;
return solveImageCaptcha(imagePath, language);
}
// Usage
const text = await solveWithAutoLanguage("captcha.png", "auto");
console.log(`Text: ${text}, Script: ${detectScript(text)}`);
Die Umwandlung mit String(language) ist Absicht: Im Code-Review bleibt sichtbar, dass hier ein API-Parameter übergeben wird und kein Schwellenwert.
Typische Fehlkonfigurationen
| Fehler | Wirkung | Korrektur |
|---|---|---|
language=0 bei kyrillischem Text |
Liefert lateinische Lookalikes (B statt В) |
Setzen Sie language=1 oder language=2 |
language=1 bei chinesischen Zeichen |
Der Solver erwartet Kyrillisch und erhält CJK | Setzen Sie language=2 für nicht-lateinische Schriften |
| Parameter bei gemischten Schriften weggelassen | Mehrdeutige Zeichen werden falsch zugeordnet | Bei gemischten Inhalten grundsätzlich language=2 |
| Annahme, der Standard decke alles ab | Das rein lateinische Modell übersieht fremde Zeichen | Zeichensatz für nicht-lateinische Portale explizit setzen |
Fehlerbehebung
| Problem | Ursache | Lösung |
|---|---|---|
| Text sieht richtig aus, wird abgelehnt | Homoglyphen-Verwechslung Latein/Kyrillisch | Codepoints prüfen, language korrigieren |
| Leeres Ergebnis bei CJK-Abfragen | Zeichensatz steht nicht auf 2 |
language=2 für Chinesisch, Japanisch, Koreanisch |
| Ergebnis kommt verstümmelt an | Antwort wird nicht als UTF-8 gelesen | response.encoding = 'utf-8' setzen |
| Erfolgsquote sinkt nach Quellenwechsel | Neues Portal nutzt ein anderes Schriftsystem | Zeichensatz pro Quelle konfigurieren |
Was sich durch den Zeichensatz nicht ändert
Der Parameter wählt ein Erkennungsmodell aus – mehr nicht. An der Abrechnung ändert er nichts: CaptchaAI rechnet pro gleichzeitigem Thread ab, nicht pro Lösung, und jeder Tarif enthält unbegrenzte Lösungen pro Thread. BASIC (15 $/Monat, 5 Threads) verhält sich bei CJK-Aufgaben also genauso wie bei lateinischen; einen Aufschlag nach CAPTCHA-Typ gibt es nicht. Abgerechnet wird in US-Dollar.
Auch die Lösungszeit verschiebt sich nur geringfügig: Zeichensätze mit mehreren tausend Glyphen brauchen tendenziell etwas länger als 26 lateinische Buchstaben, der Unterschied bleibt aber im Bereich von Sekundenbruchteilen.
Häufige Fragen
Woran erkenne ich, welchen Zeichensatz ein Portal verwendet?
Am schnellsten an der Seite selbst: Formularbeschriftungen, lang-Attribut im HTML und die Zeichen bereits gelöster Antworten. Bleibt es unklar, lösen Sie zwanzig Testbilder mit language=2 und werten die Codepoints aus – die Verteilung zeigt das dominierende Schriftsystem.
Kann ich language=2 dauerhaft als Standard setzen?
Funktional spricht nichts dagegen, denn der Wert deckt alle Schriftsysteme ab. Für rein lateinische Portale ist das Standardmodell allerdings enger zugeschnitten, deshalb lohnt sich die Unterscheidung, sobald eine Quelle dauerhaft nur lateinische Zeichen liefert.
Warum kommen Umlaute oder kyrillische Zeichen verstümmelt an?
Das ist ein Kodierungsproblem im Client, nicht im Solver. Die API antwortet UTF-8-kodiert; interpretiert die HTTP-Bibliothek die Antwort als Latin-1, entstehen Mojibake-Zeichen. In Python setzen Sie vor dem Auslesen response.encoding = 'utf-8'.
Beeinflusst der Parameter Threads oder Kontingent?
Nein. Der Zeichensatz steuert ausschließlich die Modellwahl. Ihr Durchsatz ergibt sich aus der Anzahl gleichzeitiger Threads im gebuchten Tarif und der Lösungszeit pro Aufgabe – unabhängig davon, ob Sie lateinische, kyrillische oder CJK-Bilder einreichen.