API Tutorials

Instruções BLS CAPTCHA e aprofundamento dos parâmetros de código

Se a tarefa BLS CAPTCHA volta com ERROR_BAD_PARAMETERS mesmo com sitekey e pageurl corretos, o problema quase nunca está nesses dois campos — está em instructions e code, os parâmetros opcionais que a maioria dos tutoriais ignora. O BLS CAPTCHA aparece com frequência em portais de agendamento, como o fluxo de marcação de vistos operado pela BLS International, e cada implementação varia o texto do desafio conforme a sessão, o idioma ou a região atendida.

Este guia mostra como extrair instructions e code de uma página real, quando vale a pena enviá-los à API da CaptchaAI e como montar um fluxo completo com Selenium — do carregamento até o envio do formulário.


Parâmetros do BLS CAPTCHA: o que a API espera

A API da CaptchaAI aceita seis parâmetros para o método bls: três obrigatórios e três opcionais — instructions, code e json — que ajudam quando o desafio tem texto ambíguo ou uma variante pouco comum:

Parâmetro Obrigatório Tipo Descrição
method Sim String Deve ser bls
sitekey Sim String A chave BLS CAPTCHA do site
pageurl Sim String URL da página que exibe o CAPTCHA
instructions Não String Texto do enunciado extraído da imagem do CAPTCHA
code Não String Identificador da variante do desafio BLS CAPTCHA
json Não Inteiro Defina como 1 para receber a resposta em JSON

Na prática, method, sitekey e pageurl já bastam para a maioria dos desafios. Reserve os outros dois para quando a tarefa voltar rejeitada com frequência.


Como extrair sitekey, instructions e code da página

O primeiro passo é sempre ler o DOM antes de enviar qualquer coisa à CaptchaAI. A função abaixo abre a página com Selenium, localiza o widget pelo atributo data-sitekey, captura o texto de instrução visível e, quando o code está embutido no HTML ou em um script inline, extrai o valor com uma expressão regular. Se um campo não existir, a função o omite.

# extract_bls.py
import re
from selenium import webdriver
from selenium.webdriver.common.by import By


def extract_bls_params(url):
    """Extract BLS CAPTCHA parameters from a page."""
    driver = webdriver.Chrome()
    driver.get(url)

    params = {"pageurl": url}

    # Extract sitekey
    captcha_el = driver.find_element(By.CSS_SELECTOR, "[data-sitekey], .bls-captcha")
    sitekey = captcha_el.get_attribute("data-sitekey")
    if sitekey:
        params["sitekey"] = sitekey

    # Extract instructions if visible
    try:
        instructions_el = driver.find_element(
            By.CSS_SELECTOR, ".captcha-instructions, .captcha-text"
        )
        params["instructions"] = instructions_el.text.strip()
    except Exception:
        pass

    # Extract code from hidden input or script
    page_source = driver.page_source
    code_match = re.search(r'captcha_code["\']?\s*[:=]\s*["\']([^"\']+)', page_source)
    if code_match:
        params["code"] = code_match.group(1)

    driver.quit()
    return params


# Usage
params = extract_bls_params("https://bls-example.com/appointment")
print(params)

O resultado é um dicionário params pronto para a função de envio da próxima seção. Rode essa extração a cada sessão: os campos opcionais podem mudar de uma página para outra.


Enviando o BLS CAPTCHA para a CaptchaAI

Envio básico

Com os parâmetros em mãos, o envio segue o padrão de qualquer tarefa CaptchaAI: POST para in.php, seguido de polling em res.php até o status mudar para 1. A função abaixo só adiciona instructions e code quando eles existem.

# solve_bls_basic.py
import requests
import time
import os


def solve_bls(sitekey, pageurl, instructions=None, code=None):
    """Solve BLS CAPTCHA via CaptchaAI API."""
    api_key = os.environ["CAPTCHAAI_API_KEY"]

    payload = {
        "key": api_key,
        "method": "bls",
        "sitekey": sitekey,
        "pageurl": pageurl,
        "json": 1,
    }

    # Add optional parameters for higher accuracy
    if instructions:
        payload["instructions"] = instructions
    if code:
        payload["code"] = code

    resp = requests.post(
        "https://ocr.captchaai.com/in.php",
        data=payload,
        timeout=30,
    )
    result = resp.json()

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

    task_id = result["request"]

    # Poll for result
    time.sleep(10)
    for _ in range(30):
        resp = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": api_key,
            "action": "get",
            "id": task_id,
            "json": 1,
        }, timeout=15)
        data = resp.json()

        if data.get("status") == 1:
            return data["request"]
        if data["request"] != "CAPCHA_NOT_READY":
            raise RuntimeError(data["request"])
        time.sleep(5)

    raise TimeoutError("BLS solve timeout")


# Usage
solution = solve_bls(
    sitekey="your-bls-sitekey",
    pageurl="https://bls-example.com/appointment",
    instructions="Select images in the correct order",
)
print(f"Solution: {solution}")

Repare no intervalo de 10 segundos antes do primeiro polling: o BLS CAPTCHA resolve em menos de 1 segundo no worker da CaptchaAI, com alta taxa de sucesso nos tipos suportados — a espera cobre a fila e a latência de rede, não o tempo de resolução.


O parâmetro instructions: quando ele muda o resultado

instructions existe porque nem todo desafio BLS embute o enunciado na própria imagem. Quando o texto aparece como elemento HTML separado, repassá-lo à CaptchaAI reduz a ambiguidade e melhora a precisão. Em portais de agendamento de visto, o enunciado costuma mudar conforme o idioma do consulado ou a região — por isso a extração deve rodar a cada sessão, nunca a partir de um valor fixo salvo no código.

# Common BLS instruction patterns:
instructions_examples = [
    "Select images in the correct order",
    "Click the images in order from left to right",
    "Arrange the images by number",
    "Select the matching image",
    "Click in the order shown",
]

# Extract instructions from the CAPTCHA image area
def get_instructions_from_page(driver):
    """Try multiple selectors to find instruction text."""
    selectors = [
        ".captcha-instructions",
        ".bls-captcha-text",
        "#captcha-prompt",
        ".challenge-text",
    ]

    for sel in selectors:
        try:
            el = driver.find_element(By.CSS_SELECTOR, sel)
            text = el.text.strip()
            if text:
                return text
        except Exception:
            continue

    return None

Times de QA de agências de viagens no Brasil e em Portugal costumam replicar esse fluxo em staging antes de liberar uma nova versão do formulário: variam o enunciado do desafio e conferem se o instructions extraído bate com o texto exibido na tela — uma forma barata de detectar mudanças de redação antes que derrubem a taxa de acerto em produção.


O parâmetro code: identificando a variante do desafio

Nota: algumas implementações de BLS CAPTCHA usam mais de uma variante de desafio na mesma página, cada uma identificada por um code. A função abaixo tenta três padrões comuns — captchaType no JavaScript, o atributo data-captcha-code no HTML e a variável bls_code no script — e retorna o primeiro valor encontrado. Assim como instructions, o code não é constante: sites multi-idioma costumam alternar a variante conforme a sessão, então reextrair a cada carregamento é a prática correta.

# Detect BLS CAPTCHA code from page
def detect_bls_code(page_source):
    """Detect which BLS CAPTCHA code/type is being used."""
    patterns = [
        (r'captchaType["\']?\s*[:=]\s*["\'](\w+)', "captchaType"),
        (r'data-captcha-code["\']?\s*=\s*["\'](\w+)', "data attribute"),
        (r'bls_code["\']?\s*[:=]\s*["\'](\w+)', "bls_code"),
    ]

    for pattern, source in patterns:
        match = re.search(pattern, page_source)
        if match:
            return match.group(1)

    return None

Fluxo completo com Selenium: da página ao envio do formulário

Juntando as peças anteriores, a função abaixo cobre o ciclo inteiro: abre a página, preenche o formulário (quando houver), extrai sitekey e instructions do widget, resolve o desafio via API, injeta o token no campo esperado e envia. A injeção cobre dois cenários — um input já existente ou a criação de um campo oculto — porque cada implementação nomeia o campo de resposta de um jeito diferente.

# full_bls_flow.py
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import os
import re


def solve_bls_with_selenium(url, form_data=None):
    """Complete BLS CAPTCHA flow using Selenium."""
    driver = webdriver.Chrome()
    driver.get(url)

    wait = WebDriverWait(driver, 15)

    # Fill any form fields before CAPTCHA
    if form_data:
        for field_id, value in form_data.items():
            el = wait.until(EC.presence_of_element_located((By.ID, field_id)))
            el.clear()
            el.send_keys(value)

    # Extract CAPTCHA parameters
    captcha_container = wait.until(
        EC.presence_of_element_located((By.CSS_SELECTOR, "[data-sitekey], .bls-captcha"))
    )
    sitekey = captcha_container.get_attribute("data-sitekey")

    # Get instructions
    instructions = None
    try:
        inst_el = driver.find_element(By.CSS_SELECTOR, ".captcha-instructions")
        instructions = inst_el.text.strip()
    except Exception:
        pass

    # Solve via API
    solution = solve_bls(
        sitekey=sitekey,
        pageurl=driver.current_url,
        instructions=instructions,
    )

    # Inject solution
    driver.execute_script("""
        var input = document.querySelector('input[name="captcha-response"], #captcha-response');
        if (input) {
            input.value = arguments[0];
        } else {
            var hidden = document.createElement('input');
            hidden.type = 'hidden';
            hidden.name = 'captcha-response';
            hidden.value = arguments[0];
            document.forms[0].appendChild(hidden);
        }
    """, solution)

    # Submit form
    submit_btn = driver.find_element(By.CSS_SELECTOR, "button[type='submit'], #submit")
    submit_btn.click()

    # Wait for confirmation
    wait.until(EC.url_changes(url))
    result_url = driver.current_url
    driver.quit()

    return result_url

Essa função depende de solve_bls, definida na seção de envio básico — mantenha as duas no mesmo módulo antes de rodar o fluxo completo.


Resolvendo problemas comuns

A maioria dos erros de BLS CAPTCHA cai em uma destas quatro categorias. Antes de investigar mais fundo, confirme se sitekey e pageurl foram extraídos do elemento correto — é a causa mais comum de rejeição.

Problema Causa provável Como corrigir
ERROR_BAD_PARAMETERS Faltou sitekey ou pageurl no payload Confirme a extração de ambos antes do envio
Solução rejeitada pelo site Desafio ambíguo sem instructions Envie instructions para desafios com enunciado pouco claro
Tipo de CAPTCHA errado detectado O widget não é um BLS CAPTCHA Verifique se não é reCAPTCHA ou um desafio personalizado do site
sitekey não encontrado O widget carrega de forma assíncrona Aguarde o elemento renderizar antes de extrair os parâmetros

Perguntas frequentes

Preciso enviar instructions em toda tarefa BLS?

Não. A CaptchaAI resolve a maioria dos BLS CAPTCHAs sem esse parâmetro. Envie instructions quando o desafio tiver um enunciado específico e a solução voltar rejeitada com frequência — é aí que ele realmente melhora a precisão.

O parâmetro code muda entre sessões ou por região atendida?

Sim, com frequência. Trate code como um valor de sessão, não como uma constante: reextraia-o a cada carregamento de página, principalmente em portais que atendem mais de um idioma ou país.

Por que a CaptchaAI retorna ERROR_BAD_PARAMETERS mesmo com a sitekey preenchida?

Confira o pageurl: ele precisa ser exatamente a URL onde o widget foi renderizado, sem parâmetros de sessão soltos nem redirecionamentos intermediários — é a segunda causa mais comum desse erro.

BLS CAPTCHA e reCAPTCHA aparecem no mesmo fluxo de agendamento. Como sei qual estou resolvendo?

Verifique o atributo do widget antes de montar o payload: o BLS CAPTCHA expõe data-sitekey numa div com classe .bls-captcha, enquanto o reCAPTCHA usa atributos próprios do Google. Enviar method=bls para um desafio que é reCAPTCHA sempre resulta em erro.


Guias relacionados

Para aprofundar em tópicos vizinhos ao BLS CAPTCHA, veja também:


Domine os parâmetros do BLS CAPTCHA — comece com a CaptchaAI.

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