Use Cases

Résoudre les CAPTCHA sur les sites Web chinois avec CaptchaAI

Deux mécanismes couvrent la quasi-totalité des CAPTCHA d'un site chinois : l'OCR d'images de caractères, envoyée avec language=2, et le puzzle coulissant GeeTest v3. Le reste n'est pas de la résolution mais de la plomberie : encodage UTF-8, cookies conservés, adresse IP acceptée par le portail. Voici les deux appels d'API, puis les réglages qui font échouer un formulaire malgré un défi résolu.

Les défis CAPTCHA sur un site chinois

Une même session peut demander une image de caractères à l'inscription, un GeeTest v3 à la connexion, puis un reCAPTCHA v2 côté international.

Type de CAPTCHA Où il apparaît Méthode CaptchaAI
Image de caractères chinois Portails administratifs, bases universitaires Image/OCR (language=chi_sim)
Calcul arithmétique en chinois Formulaires d'inscription Image/OCR
Puzzle coulissant GeeTest v3 Baidu, Bilibili, grandes plateformes GeeTest v3
Clic sur les caractères dans l'ordre Connexion, achats Image/OCR (mode coordonnées)
reCAPTCHA v2 Versions internationales des sites chinois reCAPTCHA v2

Les vérifications maison, comme celle de Tencent, n'ont pas de méthode dédiée : passez par l'OCR si le défi se réduit à du texte. hCaptcha et FunCaptcha ne sont pas pris en charge ; GeeTest v4 est annoncé comme à venir.

Préparer la session avant le premier appel d'API

Ces réglages ne relèvent pas de la résolution, mais ils décident du sort du formulaire.

Point de friction Ce qu'il faut faire
Caractères illisibles Forcer l'UTF-8 sur la requête et la réponse
Cookies de session Récupérer l'image dans la session qui enverra le formulaire
gt et challenge calculés en JavaScript Les extraire du code de la page ou de l'appel d'API
Limitation de débit des CDN chinois Répartir le trafic sur des proxys résidentiels chinois
Image régénérée à chaque chargement Télécharger une fois, résoudre, envoyer

Python : lire une image de caractères chinois

Les images affichent des consignes du type 请输入验证码 (« saisissez le code de vérification ») : language=2 bascule l'OCR sur les jeux de caractères chinois. Le script couvre l'image sur disque, l'image en session et le GeeTest v3.

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_chinese_image_captcha(image_path: str) -> str:
    """Solve a Chinese character 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,          # 2 = Chinese characters supported
        "json": 1,
    }, timeout=30).json()

    if resp.get("status") != 1:
        raise RuntimeError(f"Submit failed: {resp.get('request')}")

    task_id = resp["request"]
    start = time.monotonic()

    while time.monotonic() - start < 120:
        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 failed: {poll.get('request')}")

    raise RuntimeError("Timeout")


def solve_chinese_captcha_from_url(captcha_url: str, cookies: dict = None) -> str:
    """Download and solve a Chinese CAPTCHA from a URL."""
    session = requests.Session()
    if cookies:
        session.cookies.update(cookies)

    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")


# --- GeeTest on Chinese platforms ---

def solve_geetest_chinese(gt: str, challenge: str, pageurl: str) -> dict:
    """Solve GeeTest v3 commonly found on Baidu, Bilibili, etc."""
    resp = requests.post(SUBMIT_URL, data={
        "key": API_KEY,
        "method": "geetest",
        "gt": gt,
        "challenge": challenge,
        "pageurl": pageurl,
        "json": 1,
    }, timeout=30).json()

    if resp.get("status") != 1:
        raise RuntimeError(f"Submit: {resp.get('request')}")

    task_id = resp["request"]
    for _ in range(36):
        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:
            # GeeTest returns challenge, validate, seccode
            return poll["request"]
        raise RuntimeError(f"Solve: {poll.get('request')}")

    raise RuntimeError("Timeout")


# Usage — Chinese government portal
text = solve_chinese_image_captcha("chinese_captcha.png")
print(f"Chinese CAPTCHA text: {text}")

# GeeTest on a Chinese platform
geetest_result = solve_geetest_chinese(
    gt="b46d1900d0a894591f1561f8c35670a7",
    challenge="dynamic_challenge_string",
    pageurl="https://www.example.cn/login",
)

L'intervalle d'interrogation de 5 secondes reste prudent. Le GeeTest renvoie challenge, validate et seccode à recopier dans le formulaire.

JavaScript : le même flux côté Node.js

Sous Node.js, la logique ne change pas. Gardez le même client HTTP, donc les mêmes cookies, pour la page, l'image et l'envoi.

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 solveChineseImageCaptcha(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");
}

async function solveGeeTest(gt, challenge, pageurl) {
  const body = new URLSearchParams({
    key: API_KEY,
    method: "geetest",
    gt,
    challenge,
    pageurl,
    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 < 36; 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");
}

// Usage
const text = await solveChineseImageCaptcha("chinese_captcha.png");
console.log(`Chinese text: ${text}`);

Dépannage

Symptôme Cause probable Correctif
Caractères tronqués dans le résultat Réponse décodée en latin-1 Décoder explicitement en UTF-8
challenge GeeTest refusé Durée de vie très courte L'extraire et l'envoyer aussitôt, jamais depuis un cache
Texte reconnu incohérent Caractères chinois et latins mélangés Envoyer language=2 sur chaque tâche
Formulaire rejeté après résolution Cookies perdus entre deux clients Réutiliser une session unique
Réussite faible sur les portails publics Rendu des caractères dégradé Capturer l'image à sa résolution native

Scénario : surveiller un catalogue fournisseur depuis l'Europe

Une équipe e-commerce lyonnaise relève chaque matin les fiches de ses fournisseurs sur une place de marché chinoise. Le worker tourne sur une instance OVHcloud, et le portail affiche une image de caractères dès quelques dizaines de pages par minute. Deux ajustements suffisent : router la collecte via des proxys résidentiels chinois, et résoudre en parallèle. La facturation CaptchaAI repose sur les threads simultanés : BASIC ($15/mois, 5 threads) absorbe un relevé matinal, ADVANCE ($90/mois, 50 threads) une surveillance continue. Ces pages sont publiques, mais ne conservez pas les données personnelles croisées au passage : la finalité de la collecte relève du RGPD.

FAQ

Pourquoi le texte chinois revient-il en caractères illisibles ?

Parce qu'un maillon de la chaîne a décodé la réponse en latin-1. Fixez l'UTF-8 sur le client HTTP, le stockage et les logs : un résultat correct peut devenir illisible au seul affichage.

CaptchaAI prend-il en charge hCaptcha ou les CAPTCHA propriétaires chinois ?

Non, ni hCaptcha ni FunCaptcha, et les systèmes maison des plateformes chinoises n'ont pas de méthode dédiée. Sont couverts : Image/OCR, grilles d'images, GeeTest v3, reCAPTCHA v2 et v3, Cloudflare Turnstile et Challenge, plus CaptchaFox (bêta), Friendly Captcha (bêta) et Lemin (bêta).

GeeTest v4 fonctionne-t-il sur les plateformes qui ont migré ?

Pas encore : GeeTest v4 est annoncé comme à venir. Le script chargé par la page expose sa version ; sur une v4, method=geetest ne convient pas.

Articles connexes

Prochaines étapes

Avant d'industrialiser, testez sur un seul défi : récupérez votre clé API CaptchaAI et envoyez une image en base64.

Guides associés :

Les commentaires sont désactivés pour cet article.