Casos de Uso

Automatización del portal gubernamental con resolución CAPTCHA

¿Automatizar una cita de visa en un centro BLS, consultar un expediente judicial o descargar un registro público? El muro casi nunca es el formulario: es el CAPTCHA que salta justo antes de enviarlo. Aquí resuelves las tres familias —BLS, reCAPTCHA v2 y CAPTCHA de imagen— con la API de CaptchaAI y Python.

En el mundo hispanohablante domina el visado BLS, que atiende a toda América Latina, junto con la cita previa en España, el SAT en México o AFIP en Argentina. Todos repiten el patrón: sesión corta, CAPTCHA en el punto crítico y validación de IP entre páginas. Automatiza solo flujos propios o pruebas autorizadas y respeta los términos de servicio y la protección de datos.


Qué CAPTCHA usa cada tipo de portal gubernamental

Saber qué esquema protege cada trámite te dice a qué te enfrentas:

Categoría del portal CAPTCHA habitual Caso de uso
Visa / inmigración BLS, reCAPTCHA v2 Reserva de cita
DMV / vehículos reCAPTCHA v2, imagen Renovación
Registros judiciales reCAPTCHA v2 Expedientes
Permisos / licencias Imagen Solicitudes
Portales fiscales reCAPTCHA v2 Declaración
Registros públicos Imagen, reCAPTCHA Propiedades
  • Los portales públicos suelen ir por detrás en tecnología de CAPTCHA.
  • Con tres métodos cubres casi todo: bls, userrecaptcha y base64 para imagen.

Gestión de sesiones: conoce el reloj de cada portal

Estos portales imponen tiempos de espera cortos: la sesión caduca justo cuando ibas a enviar. Márgenes típicos:

  • Visado: 5-10 minutos; completa el flujo sin pausas.
  • DMV: 15 minutos; refresca la cookie a mitad del flujo.
  • Registros judiciales: 20-30 minutos; agrupa por lotes.
  • Fiscales: 10-15 minutos; proxy fijo, misma IP.
  • Permisos: 30 minutos; guarda el progreso.

Automatizar citas de visa en portales BLS

Los portales BLS usan su propio esquema. CaptchaAI lo resuelve con una alta tasa de éxito mediante bls, o con base64 cuando trabajas con la imagen:

import requests
import time
import base64

CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"


def solve_bls_captcha(captcha_image_url, session):
    """Solve BLS-specific CAPTCHA."""
    # Download CAPTCHA image
    img_resp = session.get(captcha_image_url)
    img_b64 = base64.b64encode(img_resp.content).decode()

    resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data={
        "key": CAPTCHAAI_KEY,
        "method": "base64",
        "body": img_b64,
        "json": 1,
    })
    task_id = resp.json()["request"]

    for _ in range(30):
        time.sleep(3)
        result = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
            "key": CAPTCHAAI_KEY, "action": "get",
            "id": task_id, "json": 1,
        })
        data = result.json()
        if data["request"] != "CAPCHA_NOT_READY":
            return data["request"]

    raise TimeoutError("BLS CAPTCHA timeout")


class BLSAppointmentBooker:
    def __init__(self):
        self.session = requests.Session()
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36",
        })

    def login(self, portal_url, email, password):
        """Login to BLS portal with CAPTCHA."""
        resp = self.session.get(portal_url)

        # Extract CAPTCHA image URL from page
        import re
        match = re.search(r'src="(/captcha[^"]+)"', resp.text)
        if match:
            captcha_url = portal_url.rstrip("/") + match.group(1)
            captcha_text = solve_bls_captcha(captcha_url, self.session)
        else:
            captcha_text = ""

        login_resp = self.session.post(portal_url, data={
            "email": email,
            "password": password,
            "captcha": captcha_text,
        })
        return login_resp.status_code == 200

    def check_appointment_slots(self, slots_url):
        """Check available appointment slots."""
        resp = self.session.get(slots_url)
        if resp.status_code == 200:
            return resp.json().get("available_slots", [])
        return []

    def book_slot(self, booking_url, slot_id, applicant_data):
        """Book an appointment slot, handling any CAPTCHA."""
        resp = self.session.get(booking_url)

        # Check for CAPTCHA on booking page
        import re
        match = re.search(r'src="(/captcha[^"]+)"', resp.text)
        if match:
            captcha_url = booking_url.rstrip("/") + match.group(1)
            captcha_text = solve_bls_captcha(captcha_url, self.session)
        else:
            captcha_text = ""

        resp = self.session.post(booking_url, data={
            "slot_id": slot_id,
            "captcha": captcha_text,
            **applicant_data,
        })

        return {
            "success": resp.status_code == 200,
            "confirmation": resp.json().get("confirmation_number"),
        }

El patrón se repite: descargas la imagen desde la sesión, la envías a in.php, sondeas res.php y adjuntas el texto resuelto al formulario. La misma Session conserva las cookies de principio a fin.


Consultar expedientes judiciales con reCAPTCHA v2

Los registros judiciales casi siempre esconden un reCAPTCHA v2. No hay imagen: resuelves un token con userrecaptcha, lo adjuntas como g-recaptcha-response y envías la búsqueda.

def solve_recaptcha(sitekey, pageurl):
    resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data={
        "key": CAPTCHAAI_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": pageurl,
        "json": 1,
    })
    task_id = resp.json()["request"]

    for _ in range(60):
        time.sleep(5)
        result = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
            "key": CAPTCHAAI_KEY, "action": "get",
            "id": task_id, "json": 1,
        })
        data = result.json()
        if data["request"] != "CAPCHA_NOT_READY":
            return data["request"]
    raise TimeoutError("Timeout")


class CourtRecordSearcher:
    def __init__(self, proxy=None):
        self.session = requests.Session()
        if proxy:
            self.session.proxies = {"http": proxy, "https": proxy}
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36",
        })

    def search_cases(self, court_url, search_params, sitekey):
        """Search court records with reCAPTCHA handling."""
        # Load search page
        self.session.get(court_url)

        # Solve CAPTCHA
        token = solve_recaptcha(sitekey, court_url)

        # Submit search with token
        resp = self.session.post(court_url, data={
            **search_params,
            "g-recaptcha-response": token,
        })

        if resp.status_code == 200:
            return self._parse_results(resp.text)
        return []

    def _parse_results(self, html):
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html, "html.parser")
        cases = []
        for row in soup.select("table.results tr")[1:]:
            cols = row.select("td")
            if len(cols) >= 4:
                cases.append({
                    "case_number": cols[0].get_text(strip=True),
                    "parties": cols[1].get_text(strip=True),
                    "date": cols[2].get_text(strip=True),
                    "status": cols[3].get_text(strip=True),
                })
        return cases
  • Lees el sitekey una sola vez del HTML de la página.
  • Si el tribunal exige una IP estable, pasa un proxy al constructor.

CAPTCHA de imagen en portales de permisos y licencias

Muchos portales municipales se quedan en el CAPTCHA de imagen clásico: texto distorsionado que la API resuelve por OCR. El método vuelve a ser base64, sin reserva de cita:

def solve_image_captcha(image_url, session):
    """Solve image-based CAPTCHA common on local government sites."""
    img = session.get(image_url)
    img_b64 = base64.b64encode(img.content).decode()

    resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data={
        "key": CAPTCHAAI_KEY,
        "method": "base64",
        "body": img_b64,
        "json": 1,
    })
    task_id = resp.json()["request"]

    for _ in range(20):
        time.sleep(3)
        result = requests.get(f"{CAPTCHAAI_URL}/res.php", params={
            "key": CAPTCHAAI_KEY, "action": "get",
            "id": task_id, "json": 1,
        })
        data = result.json()
        if data["request"] != "CAPCHA_NOT_READY":
            return data["request"]

    raise TimeoutError("Image CAPTCHA timeout")

Consultas por lotes en registros públicos

Para revisar muchos registros de una tanda, resuelve un token, reutilízalo mientras sea válido y deja un intervalo entre solicitudes:

import csv


def batch_property_lookup(addresses, portal_url, sitekey, output_file):
    """Look up multiple property records, solving CAPTCHA per batch."""
    session = requests.Session()
    session.headers.update({
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
        "AppleWebKit/537.36 Chrome/126.0.0.0 Safari/537.36",
    })

    results = []
    for i, address in enumerate(addresses):
        try:
            # Solve CAPTCHA every 5th request (or when required)
            if i % 5 == 0:
                token = solve_recaptcha(sitekey, portal_url)

            resp = session.post(portal_url, data={
                "address": address,
                "g-recaptcha-response": token,
            })

            if resp.status_code == 200:
                results.append({
                    "address": address,
                    "data": resp.json(),
                })

            time.sleep(3)  # Be respectful

        except Exception as e:
            results.append({"address": address, "error": str(e)})

    # Save results
    with open(output_file, "w", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=["address", "data", "error"])
        writer.writeheader()
        writer.writerows(results)

    return results
  • Resolver el token cada cinco solicitudes recorta el gasto sin que caduque a mitad del lote.
  • CaptchaAI factura por thread, no por resolución: un hilo del plan BASIC ($15/mes, 5 threads) basta para lotes moderados.

Errores frecuentes y cómo resolverlos

La causa número uno de fallos no es el CAPTCHA, sino el desajuste de tiempos: el token se resuelve, pasan los segundos y el servidor lo rechaza. Resuelve lo más cerca posible del POST. Tropiezos habituales:

Problema Causa Solución
La imagen no carga Cookie caducada Inicia sesión nueva
"Sesión expirada" en el formulario Tardaste en resolver Resuelve por adelantado
Respuesta incorrecta Imagen distorsionada Repórtala por la API
El portal bloquea la automatización Detección de IP/UA Red autorizada y UA real
Validación falla tras el CAPTCHA Token caducado en el servidor Resuelve justo antes de enviar

Preguntas frecuentes

¿CaptchaAI resuelve el CAPTCHA de los portales BLS?

Sí. CaptchaAI resuelve el BLS CAPTCHA con una alta tasa de éxito usando method=bls para resolución directa, o method=base64 con la imagen del desafío.

¿Cuánto cuesta resolver el CAPTCHA de estos portales?

CaptchaAI cobra por thread, no por resolución, con resoluciones ilimitadas al mes. El plan BASIC ($15/mes, 5 threads) cubre QA y consultas moderadas.

¿Qué hago si la sesión expira antes de enviar el formulario?

Resuelve el CAPTCHA justo antes del POST y mantén viva la cookie con la misma requests.Session. Si el portal caduca en minutos, precarga el formulario y deja la resolución para el final.


Guías relacionadas



Siguientes pasos

Los comentarios están deshabilitados para este artículo.