Use Cases

CAPTCHA em árabe e RTL: como resolver com a CaptchaAI

Um formulário em árabe não fica mais simples só porque você inverteu a leitura de LTR para RTL. O problema real é outro: as letras árabes mudam de forma conforme a posição na palavra, números e texto latino se misturam ao árabe (bidirecional), e o layout da página é espelhado. Para resolver o CAPTCHA de imagem, a CaptchaAI usa o mesmo endpoint de sempre — a diferença fica em um único parâmetro, language=2.

Isso aparece com frequência em times de QA brasileiros testando portais de visto e comércio exterior com o Golfo e o Norte da África: o formulário roda em staging, os testes partem da região sa-east-1 da AWS, e o texto sai invertido na tela sem tratamento RTL — não porque o solver errou, mas porque o cliente exibiu texto RTL como LTR.

Por que o CAPTCHA em árabe e RTL é mais difícil de resolver

Desafio Detalhe
Conexão de caracteres Uma mesma letra árabe muda de forma conforme a posição na palavra — inicial, medial, final ou isolada
Leitura da direita para a esquerda O texto do CAPTCHA é lido da direita para a esquerda
Direção mista (bidirecional) Números e texto latino aparecem misturados com árabe na mesma string
Marcas diacríticas Pontos e sinais acima ou abaixo dos caracteres — fáceis de perder em imagem de baixa resolução
Layout de página RTL Formulário e posição do CAPTCHA ficam espelhados em relação a uma página LTR

Os dois primeiros pontos quebram um OCR genérico, que trata cada forma de uma letra árabe como um caractere diferente e ignora a leitura direita-para-esquerda. O solver Image/OCR da CaptchaAI já lida com escrita conectada — é isso que language=2 ativa.

Roteiros RTL que a CaptchaAI reconhece

Roteiro Idiomas cobertos Parâmetro na API
Árabe Árabe, urdu, pashto language=2
Farsi/Persa Persa, com caracteres extras language=2
Hebraico Hebraico language=2

O parâmetro é o mesmo nos três casos — a CaptchaAI detecta o conjunto de caracteres estendido automaticamente.

Resolvendo CAPTCHA de imagem em árabe com Python

Envie a imagem em base64 para in.php com language=2, faça o polling em res.php e trate o texto retornado como UTF-8.

O exemplo cobre os dois casos mais comuns nesse fluxo: resolver a partir de um arquivo local e da URL do CAPTCHA dentro de uma sessão autenticada, terminando no envio do formulário.

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_arabic_captcha(image_path: str) -> str:
    """Solve an Arabic script 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_arabic_captcha_from_url(session: requests.Session,
                                   captcha_url: str) -> str:
    """Download and solve an Arabic CAPTCHA from a URL."""
    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")


# --- RTL-aware form submission ---

def submit_form_with_arabic_captcha(
    form_url: str,
    captcha_url: str,
    form_data: dict,
    captcha_field: str = "captcha",
) -> requests.Response:
    """Complete an Arabic website form with CAPTCHA."""
    session = requests.Session()
    session.headers.update({
        "Accept-Language": "ar-SA,ar;q=0.9",
        "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
    })

    # Load the form page to establish session
    session.get(form_url, timeout=15)

    # Solve the CAPTCHA
    captcha_text = solve_arabic_captcha_from_url(session, captcha_url)
    print(f"Arabic CAPTCHA solved: {captcha_text}")

    # Submit with the solved text
    form_data[captcha_field] = captcha_text
    response = session.post(form_url, data=form_data, timeout=30)

    return response


# --- Usage ---

# Simple Arabic image CAPTCHA
text = solve_arabic_captcha("arabic_captcha.png")
print(f"Arabic text: {text}")

# Form submission on Arabic site
response = submit_form_with_arabic_captcha(
    form_url="https://example.sa/registration",
    captcha_url="https://example.sa/captcha/generate",
    form_data={
        "name": "اسم المستخدم",
        "email": "[email protected]",
    },
)

CAPTCHA árabe e RTL em JavaScript (Playwright)

Em Node.js o padrão é o mesmo — muda como você localiza o CAPTCHA. Em layout RTL, o seletor por posição costuma falhar; use id/class, baixe o src, resolva e preencha o campo de resposta.

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

// Inject CAPTCHA token into RTL page with Playwright
async function solveAndInjectRTL(page) {
  // RTL pages may position the CAPTCHA differently
  const captchaImg = await page.locator("img[id*='captcha'], img[class*='captcha']");
  const imgSrc = await captchaImg.getAttribute("src");

  // Download the image
  const buffer = await (await fetch(imgSrc)).arrayBuffer();
  const imageB64 = Buffer.from(buffer).toString("base64");

  // Solve
  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) {
      // Fill the input — RTL input handles text direction automatically
      await page.locator("input[name*='captcha']").fill(poll.request);
      return poll.request;
    }
    throw new Error(`Solve: ${poll.request}`);
  }
}

// Usage
const text = await solveArabicCaptcha("arabic_captcha.png");
console.log(`Arabic text: ${text}`);

Problemas comuns e como corrigir

Estes são os erros mais relatados por quem já roda language=2 em produção — a maioria tem origem na exibição do texto no cliente, não no solver.

Problema Causa Correção
Texto árabe invertido na tela Cliente exibe texto RTL como LTR Envolva a saída em \u202B (incorporação RTL)
Faltam marcas diacríticas Imagem de baixa resolução Use imagens de CAPTCHA em resolução mais alta
Texto bidirecional fora de ordem Algoritmo BiDi inconsistente Trate caracteres direcionais com marcadores Unicode
Envio falha com texto em árabe Incompatibilidade de codificação Use charset=UTF-8 no Content-Type
Posição do CAPTCHA difere do esperado Layout RTL espelha a posição dos elementos Use seletores CSS (id/class), não posição

Perguntas frequentes

Estas são as dúvidas mais comuns de times que integram roteiros RTL pela primeira vez.

O CaptchaAI reconhece a escrita árabe conectada?

Sim. As letras árabes mudam de forma conforme a posição na palavra — inicial, medial, final ou isolada — e o solver Image/OCR da CaptchaAI reconhece essas variações, não apenas letras isoladas.

O mesmo parâmetro language=2 funciona para urdu, pashto e hebraico, ou preciso mudar algo?

Funciona para os três, além do árabe e do farsi. A CaptchaAI detecta o conjunto de caracteres estendido automaticamente — não é preciso trocar o parâmetro por idioma.

Como corrijo texto árabe que sai invertido depois de resolver o CAPTCHA?

O solver retorna o texto correto; o problema costuma estar na exibição. Envolva a string em \u202B (incorporação RTL) antes de mostrá-la, ou use um componente de UI já preparado para texto bidirecional.

Preciso tratar números misturados ao texto árabe de forma diferente?

Sim. Números dentro de uma string árabe seguem leitura da esquerda para a direita mesmo em texto RTL — isso é bidirecional. Se seu pipeline reformata a resposta antes do envio, trate isso com marcadores Unicode, em vez de assumir uma única direção.

Comece agora

Resolva CAPTCHA em árabe, farsi e hebraico sem reescrever seu pipeline — obtenha sua chave de API da CaptchaAI e ative language=2.

Guias relacionados:

Esses três guias cobrem os cenários mais comuns de expansão para roteiros não latinos e RTL.

Os comentários estão desativados para este artigo.