Une recherche de jurisprudence automatisée s'arrête presque toujours au même endroit : le CAPTCHA qui protège le portail. Sur PACER, SEC EDGAR ou un système judiciaire d'État, chaque série de requêtes un peu soutenue déclenche un reCAPTCHA v2 ou un CAPTCHA image, et le script se bloque avant même d'avoir récupéré une décision. CaptchaAI résout ces défis via son API pour que la collecte de jurisprudence, de dépôts et de données réglementaires reste continue.
Ce guide montre comment l'intégrer dans un scraper Python : détecter le défi, obtenir le token, réinjecter la réponse, puis reprendre l'extraction — sans perdre de vue le débit ni le RGPD.
Quelles sources juridiques déclenchent des CAPTCHA
Avant d'écrire du code, cartographiez vos portails cibles : chacun présente un défi différent et impose son propre rythme d'accès. Le tableau ci-dessous recense les sources les plus fréquentes d'une veille juridique et le CAPTCHA qu'elles opposent.
| Source | Type de CAPTCHA | Données | Utilisateurs |
|---|---|---|---|
| PACER | reCAPTCHA v2 | Dépôts devant les tribunaux fédéraux | Équipes contentieux |
| Systèmes judiciaires d'État | CAPTCHA image / reCAPTCHA | Dossiers d'État | Avocats |
| SEC EDGAR | reCAPTCHA v2 | Dépôts d'entreprises | Conformité |
| Bases de brevets | reCAPTCHA v2 | Dossiers de brevets | Chercheurs en PI |
| Portails réglementaires | CAPTCHA image | Règles, avis | Conformité |
| Bases de citations juridiques | reCAPTCHA v2 | Références de décisions | Legal tech |
| Annuaires des barreaux | reCAPTCHA v2 | Fiches d'avocats | Diligence raisonnable |
Une équipe legal tech à Paris qui surveille la jurisprudence RGPD combine souvent plusieurs de ces sources — un portail européen pour les décisions, EDGAR pour les dépôts d'entreprises cotées, une base de brevets pour la PI — et chacune ajoute son propre défi CAPTCHA au pipeline.
Ce que CaptchaAI résout sur ces portails
Deux familles de CAPTCHA couvrent la quasi-totalité des portails juridiques, et CaptchaAI prend les deux en charge :
- reCAPTCHA v2 (case « Je ne suis pas un robot » et défis image) via la méthode
userrecaptcha. C'est le défi dominant sur PACER, EDGAR et la plupart des bases de citations. - CAPTCHA image / OCR, encore répandu sur les vieux systèmes judiciaires d'État, résolu en envoyant l'image encodée en base64.
CaptchaAI résout aussi reCAPTCHA v3, Cloudflare Turnstile et GeeTest v3, mais ces types restent rares côté juridique. À l'inverse, hCaptcha et FunCaptcha ne sont pas pris en charge : si un portail les impose, prévoyez une autre voie d'accès.
La facturation se fait au thread — un thread correspond à un CAPTCHA en cours de résolution, avec un nombre de résolutions illimité dans le mois. Une veille ponctuelle tient dans le plan BASIC ($15/mois, 5 threads) ; une collecte parallèle sur plusieurs portails passe au plan ADVANCE ($90/mois, 50 threads).
Construire le moteur de recherche de jurisprudence
Le cœur du scraper est une classe qui garde une session HTTP, détecte un CAPTCHA dans la réponse, appelle le solveur adapté, puis relance la requête avec le token. La logique reste identique d'une base à l'autre : parcourir les résultats, extraire les décisions, s'arrêter dès qu'une page est vide.
import requests
import time
import re
import base64
from bs4 import BeautifulSoup
import csv
CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"
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")
def solve_image_captcha(image_bytes):
img_b64 = base64.b64encode(image_bytes).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("Timeout")
class LegalResearchScraper:
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, search_url, query, sitekey=None, max_pages=5):
"""Search case law database."""
all_cases = []
for page in range(max_pages):
url = f"{search_url}?q={query}&page={page + 1}"
resp = self.session.get(url, timeout=30)
if self._has_captcha(resp.text):
if sitekey:
token = solve_recaptcha(sitekey, url)
resp = self.session.post(url, data={
"q": query,
"g-recaptcha-response": token,
})
else:
resp = self._solve_image_and_retry(resp.text, url, query)
cases = self._parse_cases(resp.text)
if not cases:
break
all_cases.extend(cases)
print(f"Page {page + 1}: {len(cases)} cases")
time.sleep(5)
return all_cases
def get_case_details(self, case_url):
"""Fetch full case details."""
resp = self.session.get(case_url, timeout=30)
if self._has_captcha(resp.text):
sitekey = self._extract_sitekey(resp.text)
if sitekey:
token = solve_recaptcha(sitekey, case_url)
resp = self.session.post(case_url, data={
"g-recaptcha-response": token,
})
soup = BeautifulSoup(resp.text, "html.parser")
return {
"title": self._text(soup, "h1, .case-title"),
"citation": self._text(soup, ".citation, .case-cite"),
"court": self._text(soup, ".court, .jurisdiction"),
"date": self._text(soup, ".decision-date, .date-decided"),
"judge": self._text(soup, ".judge, .authored-by"),
"summary": self._text(soup, ".summary, .headnote"),
"url": case_url,
}
def monitor_docket(self, docket_url, case_number, sitekey=None):
"""Monitor a specific case docket for new filings."""
resp = self.session.get(docket_url, timeout=30)
data = {"case_number": case_number}
if sitekey and self._has_captcha(resp.text):
token = solve_recaptcha(sitekey, docket_url)
data["g-recaptcha-response"] = token
resp = self.session.post(docket_url, data=data)
return self._parse_docket(resp.text)
def export_results(self, cases, filename):
"""Export case results to CSV."""
if not cases:
return
with open(filename, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=cases[0].keys())
writer.writeheader()
writer.writerows(cases)
def _has_captcha(self, html):
return any(tag in html.lower() for tag in [
'data-sitekey', 'g-recaptcha', 'captcha',
])
def _extract_sitekey(self, html):
match = re.search(r'data-sitekey="([^"]+)"', html)
return match.group(1) if match else None
def _solve_image_and_retry(self, html, url, query):
match = re.search(r'src="(/captcha[^"]+)"', html)
if match:
img_url = url.split("?")[0].rstrip("/") + match.group(1)
img = self.session.get(img_url)
answer = solve_image_captcha(img.content)
return self.session.post(url, data={
"q": query,
"captcha": answer,
})
return self.session.get(url)
def _parse_cases(self, html):
soup = BeautifulSoup(html, "html.parser")
cases = []
for item in soup.select(".case-result, .search-result, tr.result"):
title_el = item.select_one("a, .case-name")
if title_el:
cases.append({
"title": title_el.get_text(strip=True),
"url": title_el.get("href", ""),
"citation": self._text(item, ".citation, .cite"),
"date": self._text(item, ".date"),
"court": self._text(item, ".court"),
})
return cases
def _parse_docket(self, html):
soup = BeautifulSoup(html, "html.parser")
entries = []
for row in soup.select(".docket-entry, tr.filing"):
entries.append({
"date": self._text(row, ".date, td:first-child"),
"entry": self._text(row, ".description, td:nth-child(2)"),
"filed_by": self._text(row, ".filer, td:nth-child(3)"),
})
return entries
def _text(self, el, selector):
found = el.select_one(selector)
return found.get_text(strip=True) if found else ""
# Usage
scraper = LegalResearchScraper(
proxy="http://user:pass@residential.proxy.com:5000"
)
# Search case law
cases = scraper.search_cases(
search_url="https://caselaw.example.com/search",
query="data privacy GDPR",
max_pages=5,
)
# Get details for relevant cases
for case in cases[:10]:
if case["url"]:
details = scraper.get_case_details(case["url"])
print(f"{details['citation']}: {details['title']}")
time.sleep(3)
# Export results
scraper.export_results(cases, "gdpr_cases.csv")
Trois méthodes portent l'essentiel du travail. search_cases parcourt les résultats et, dès qu'un défi apparaît, appelle solve_recaptcha ou bascule sur la résolution d'image. get_case_details répète la même logique pour ouvrir une décision, en extrayant le sitekey du HTML au besoin. export_results écrit le tout dans un CSV UTF-8. Le token reCAPTCHA revient toujours dans le champ g-recaptcha-response, exactement comme l'attend le formulaire.
Surveiller les nouveaux dépôts réglementaires
La recherche ponctuelle ne suffit pas quand il faut détecter le jour même une nouvelle décision ou un nouveau dépôt. La classe suivante réutilise le scraper pour interroger périodiquement une liste de flux et ne remonter que les entrées jamais vues, en s'appuyant sur la référence ou le titre comme clé de déduplication.
class RegulatoryMonitor:
def __init__(self, proxy=None):
self.scraper = LegalResearchScraper(proxy=proxy)
self.seen_entries = set()
def check_new_filings(self, feeds):
"""Check regulatory portals for new filings."""
new_filings = []
for feed in feeds:
try:
cases = self.scraper.search_cases(
feed["url"], feed["query"],
sitekey=feed.get("sitekey"),
max_pages=2,
)
for case in cases:
key = case.get("citation") or case.get("title")
if key and key not in self.seen_entries:
self.seen_entries.add(key)
case["source"] = feed["name"]
new_filings.append(case)
except Exception as e:
print(f"Error checking {feed['name']}: {e}")
time.sleep(5)
return new_filings
Chaque passage se limite à deux pages par flux (max_pages=2) : sur une surveillance récurrente, l'objectif est de repérer les nouveautés en tête de liste, pas de re-parcourir l'historique. En production, remplacez l'ensemble seen_entries par un magasin persistant (Redis, SQLite) pour survivre aux redémarrages.
Débit, proxys et conformité RGPD
Un scraper juridique qui fonctionne en démo peut se faire bannir en production s'il ignore le rythme du portail. Trois réglages font la différence :
- Espacez les requêtes. Les temporisations (
time.sleep) ne sont pas décoratives : PACER et les portails d'État coupent l'accès dès qu'ils détectent une cadence anormale. Un délai entre les pages et un backoff exponentiel après erreur gardent la collecte stable. - Faites tourner les proxys résidentiels. L'exemple injecte un proxy dans la session ; pour un volume soutenu, une rotation évite qu'une seule adresse concentre tout le trafic.
- Minimisez les données personnelles. Les dossiers contiennent des noms, des adresses, parfois des données sensibles. Côté RGPD, ne collectez que les champs utiles à votre finalité et vérifiez vos obligations avant de constituer une base durable.
L'accès à un portail public ne vaut d'ailleurs pas autorisation d'en aspirer tout le contenu : respectez les conditions d'utilisation.
Dépannage
| Problème | Cause | Correctif |
|---|---|---|
| Le CAPTCHA image échoue à répétition | Texte trop déformé | Signalez la réponse et relancez : une nouvelle image est servie |
| PACER bloque l'accès | Limite de débit dépassée | Attendez 30 min et réduisez la fréquence des requêtes |
| Détails de décision incomplets | Contenu derrière un paywall | Réglez les frais par page lorsque c'est requis |
| La recherche ne renvoie rien | Une page CAPTCHA est renvoyée à la place | Vérifiez la présence d'un CAPTCHA avant de parser |
| La veille rate des dépôts | Intervalle de vérification trop long | Rapprochez les passages de surveillance |
FAQ
Quels types de CAPTCHA CaptchaAI résout-il sur les portails juridiques ?
Surtout reCAPTCHA v2 (méthode userrecaptcha) et les CAPTCHA image/OCR, qui couvrent PACER, EDGAR et les vieux systèmes d'État. hCaptcha et FunCaptcha ne sont pas pris en charge.
Comment éviter de se faire bloquer par PACER ou un portail judiciaire ?
Espacez les requêtes de plusieurs secondes, ajoutez un backoff exponentiel après chaque erreur et répartissez le trafic sur des proxys résidentiels. Un accès trop rapide déclenche des limites de débit, pas seulement des CAPTCHA.
Quel plan CaptchaAI choisir pour une collecte juridique en masse ?
Une veille légère tient dans le plan BASIC ($15/mois, 5 threads). Pour interroger plusieurs portails en parallèle, ADVANCE ($90/mois, 50 threads) autorise cinquante résolutions simultanées, sans limite du nombre de résolutions dans le mois.
La collecte automatisée de jurisprudence est-elle compatible avec le RGPD ?
Les décisions publiques sont généralement consultables, mais elles contiennent des données personnelles. Minimisez les champs collectés, documentez votre finalité et vérifiez vos obligations RGPD avant de constituer une base durable.
Guides connexes
- Automatiser les portails gouvernementaux
- Scraper la recherche universitaire
- Faire tourner des proxys résidentiels
Fluidifiez votre veille juridique : récupérez votre clé CaptchaAI et automatisez vos recherches de jurisprudence et de dépôts.