L'automatisation d'une démarche administrative bute presque toujours sur le même obstacle : un CAPTCHA à l'entrée du formulaire, du rendez-vous ou du registre. CaptchaAI le résout — reCAPTCHA v2, CAPTCHA image/OCR, BLS CAPTCHA — via une seule API, pour que vos tests QA et démarches autorisées aillent jusqu'au bout sans saisie manuelle.
Périmètre sûr. Ce guide vise les équipes QA, la civic tech et les professions juridiques. Restez sur vos propres dossiers et environnements autorisés, et vérifiez vos obligations RGPD.
Quels CAPTCHA sur quels portails publics
Identifiez d'abord le type de défi. Les administrations ont souvent une génération de retard : l'image et le reCAPTCHA v2 dominent, rarement les défis récents.
| Catégorie de portail | CAPTCHA courant | Exemples de sites | Cas d'usage |
|---|---|---|---|
| Visas / immigration | BLS, reCAPTCHA v2 | Portails BLS, USCIS | Rendez-vous |
| Immatriculation | reCAPTCHA v2, image | Préfectures, DMV | Renouvellement |
| Dossiers judiciaires | reCAPTCHA v2 | PACER, tribunaux | Consultation |
| Permis / licences | CAPTCHA image | Portails municipaux | Dépôt de demande |
| Portails fiscaux | reCAPTCHA v2 | Sites des impôts | Suivi |
| Registres publics | CAPTCHA image, reCAPTCHA | Registres fonciers | Titres de propriété |
Prise de rendez-vous sur les portails de visa BLS
Les portails BLS, au cœur du parcours de visa en Afrique du Nord francophone, utilisent leur propre CAPTCHA. CaptchaAI le prend en charge avec un taux de réussite élevé :
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"),
}
La classe télécharge l'image, la résout, puis rejoue la réponse à la connexion et à la réservation. Réservez-la à votre propre dossier : elle fiabilise une démarche légitime, pas la monopolisation de créneaux.
Consultation des dossiers judiciaires (reCAPTCHA v2)
Les greffes et bases de jurisprudence protègent souvent leur recherche par reCAPTCHA v2. Vous soumettez le sitekey, puis replacez le token g-recaptcha-response dans la requête :
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
Un schéma reCAPTCHA v2 réutilisable
Le même schéma — récupérer, injecter, analyser — vaut pour tout portail protégé par reCAPTCHA v2.
CAPTCHA image sur les portails de permis municipaux
De nombreux portails de communes utilisent de simples CAPTCHA image, résolus par OCR depuis l'image en base64 :
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")
Recherche en lot dans les registres publics
Pour croiser plusieurs adresses dans un registre foncier, résolvez le CAPTCHA une fois par lot, en espaçant les appels :
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
Réflexe RGPD. Sur des données personnelles, n'extrayez que le strict nécessaire et gardez une trace de votre base légale.
Gérer les délais de session courts
Les portails gouvernementaux expirent vite. Adaptez le rythme au délai constaté et gardez la même IP (proxy collant) sur un formulaire multi-pages :
| Type de portail | Expiration de session | Recommandation |
|---|---|---|
| Portails de visa | 5–10 min | Terminez le flux rapidement |
| Sites d'immatriculation | 15 min | Rafraîchissez le cookie à mi-parcours |
| Dossiers judiciaires | 20–30 min | Groupez les recherches par lots |
| Portails fiscaux | 10–15 min | Proxy collant, même IP |
| Portails de permis | 30 min | Sauvegardez à chaque étape |
Dépannage
| Problème | Cause | Correctif |
|---|---|---|
| L'image du CAPTCHA ne se charge pas | Cookie de session expiré | Ouvrez une nouvelle session |
| « Session expirée » pendant le formulaire | Résolution trop lente | Résolvez juste avant l'envoi |
| Réponse CAPTCHA erronée | Image déformée | Signalez l'image via l'API |
| Le portail bloque l'automatisation | Détection IP/UA | Proxy résidentiel + User-Agent réel |
| Erreur de validation après le CAPTCHA | Token expiré côté serveur | Résolvez et soumettez dans la foulée |
FAQ
L'automatisation d'un portail public est-elle autorisée ?
Oui, pour vos propres formulaires et la consultation de données publiques ; les tests QA de civic tech sont un usage légitime. Vérifiez les conditions du portail et le RGPD.
CaptchaAI prend-il en charge les BLS CAPTCHA ?
Oui. Utilisez method=bls en direct ou method=base64 pour l'image ; le taux de réussite est élevé. Le guide du solveur BLS détaille le paramétrage.
Quel plan CaptchaAI convient à des démarches ponctuelles ?
Pour un volume faible et non parallèle, l'offre BASIC ($15/mois, 5 threads) suffit : chaque thread traite un CAPTCHA à la fois, sans limite mensuelle de résolutions. Montez en gamme pour du parallèle.
Faut-il un proxy résidentiel pour ces portails ?
Souvent : les administrations filtrent les IP datacenter et vérifient la cohérence IP/User-Agent. Un proxy résidentiel à IP collante et un User-Agent réel réduisent les blocages.
Comment fiabiliser un rendez-vous qui expire trop vite ?
Résolvez le CAPTCHA au dernier moment, gardez la session et la même IP, et sauvegardez la progression pour reprendre sans tout refaire.