Anwendungsfälle

Web Scraping für Rechtsrecherche mit CAPTCHA-Verwaltung

Wer Rechtsprechung, Registerauszüge oder Patentschriften programmatisch auswertet, stößt früher oder später auf dieselbe Hürde: Das Portal blendet ein reCAPTCHA v2 oder ein Bild-CAPTCHA ein, und der Scraper bleibt stehen. Die Antwort ist ein Solver in der Pipeline. CaptchaAI nimmt die CAPTCHA-Abfrage entgegen und liefert das Token zurück, mit dem Ihr Skript die Suche fortsetzt. Dieser Leitfaden zeigt, wie Sie reCAPTCHA v2 und Bild-CAPTCHAs auf Rechtsportalen in Python verarbeiten – für Kanzleien, Legal-Tech-Teams und Compliance im DACH-Raum.

Kurz gesagt: Sie brauchen keinen manuellen Trick, sondern lösen jede CAPTCHA-Abfrage sauber und reichen die Antwort an der richtigen Stelle wieder ein. Der Rest ist normales Web-Scraping – HTTP-Anfragen, HTML-Parsing und ein Export der Treffer.


So funktioniert das CAPTCHA-Handling bei der Rechtsrecherche

Das Grundprinzip ist bei jedem Rechtsportal gleich: Ihr Scraper erkennt die CAPTCHA-Abfrage, übergibt sie per API an CaptchaAI, fragt das Ergebnis ab und reicht das gelöste Token oder die erkannte Zeichenfolge mit dem Formular wieder ein. Erst danach liefert das Portal die eigentlichen Suchergebnisse. Zwei CAPTCHA-Typen decken dabei nahezu alle Fälle ab.

reCAPTCHA v2 auf Gerichtsportalen

reCAPTCHA v2 ist der häufigste Typ auf Bundesgerichts- und Registerportalen. Ihr Skript liest den data-sitekey aus dem HTML, sendet ihn zusammen mit der pageurl per userrecaptcha an in.php und fragt res.php im Fünf-Sekunden-Takt ab, bis das g-recaptcha-response-Token vorliegt. Dieses Token fügen Sie in das Suchformular ein und senden es ab – der Ablauf bleibt für PACER, SEC EDGAR oder Patentdatenbanken identisch.

Bild-CAPTCHAs per OCR

Ältere staatliche Gerichtsportale setzen häufig verzerrte Bild-CAPTCHAs ein. Hier lädt der Scraper die Bilddatei, kodiert sie als Base64 und übergibt sie an die OCR-Methode. Nach wenigen Sekunden liefert die Abfrage die erkannte Zeichenfolge zurück, die Sie wie ein normales Formularfeld übermitteln. Schlägt die Erkennung bei stark verzerrten Bildern fehl, fordern Sie ein neues Bild an und wiederholen den Versuch.


Welche Rechtsportale welche CAPTCHAs einsetzen

Rechtsdatenbanken und Gerichtsaktensysteme schützen ihre Inhalte fast durchgängig mit CAPTCHAs – meist reCAPTCHA v2 oder Bild-CAPTCHAs, die CaptchaAI beide abdeckt.

Quelle CAPTCHA-Typ Daten Nutzer
PACER (US-Bundesgerichte) reCAPTCHA v2 Bundesgerichtsakten Prozessteams
Staatliche US-Gerichte Bild-CAPTCHA / reCAPTCHA Fallakten der Bundesstaaten Anwälte
SEC EDGAR reCAPTCHA v2 Unternehmensmeldungen Compliance
DPMA / Patentdatenbanken reCAPTCHA v2 Patent- und Markenschriften IP-Recherche
Regulierungs- und Behördenportale Bild-CAPTCHA Vorschriften, Leitlinien Compliance
Juristische Zitationsdatenbanken reCAPTCHA v2 Fundstellen, Zitate Legal Tech
Anwalts- und Kammerverzeichnisse reCAPTCHA v2 Anwaltsdaten Due Diligence

Rechtsprechung durchsuchen: der Scraper

LegalResearchScraper kapselt Suche, Detailabruf und Docket-Überwachung. Bei einem reCAPTCHA v2 ruft die Klasse solve_recaptcha auf, bei einem Bild-CAPTCHA die OCR-Methode; das g-recaptcha-response-Token geht mit dem Formular ab.

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")

_has_captcha prüft vor dem Parsen stets, ob statt der Treffer eine CAPTCHA-Seite kam.


Neue Einreichungen automatisch überwachen

RegulatoryMonitor erweitert den Scraper um eine Deduplizierung: Gesehene Fundstellen liegen in einem Set, sodass nur neue Einreichungen gemeldet werden.

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

Proxies und Rate-Limiting für Rechtsportale

Rechtsportale reagieren empfindlich auf ungewöhnliche Zugriffsmuster. PACER etwa sperrt bei zu vielen Anfragen pro Zeitfenster den Zugriff und verlangt eine mehrminütige Pause. Drei Maßnahmen halten Ihre Pipeline stabil:

  • Anfragen drosseln: Bauen Sie mit time.sleep bewusste Pausen zwischen Seiten und Detailabrufen ein, statt so schnell wie möglich zu paginieren.
  • Residential-Proxys nutzen: Rotierende Residential-Proxys verteilen die Last über mehrere IPs und senken das Sperrrisiko bei größeren Recherche-Läufen.
  • Sitzungen wiederverwenden: Eine persistente requests.Session hält Cookies über mehrere Anfragen und reduziert unnötige erneute CAPTCHA-Abfragen.

So bleibt der CAPTCHA-Solver der Engpass Ihrer Pipeline – nicht das Rate-Limit des Portals.


Rechtskonformität und DSGVO im DACH-Raum

Öffentlicher Zugriff heißt nicht automatisch unbedenklich: Prüfen Sie die Nutzungsbedingungen des Portals, halten Sie die Rate-Limits ein und dokumentieren Sie Ihre Rechtsgrundlage. Personenbezogene Angaben aus Fallakten fallen unter die DSGVO – erheben und speichern Sie nur das, was Sie für den Zweck tatsächlich benötigen, und legen Sie Löschfristen fest. Achten Sie außerdem darauf, ob Sie nur öffentlich zugängliche Bereiche abfragen oder authentifizierte Inhalte hinter einem Login berühren; die rechtliche Bewertung fällt sehr unterschiedlich aus. Wer Rechercheergebnisse vergleichend veröffentlicht, sollte zudem an die Anforderungen an sachliche, belegbare Aussagen nach UWG § 6 denken. CaptchaAI löst dabei ausschließlich die CAPTCHA-Abfrage; die rechtliche Bewertung Ihres Workflows bleibt Ihre Aufgabe.


Typische Probleme und Lösungen

Problem Ursache Lösung
Bild-CAPTCHA schlägt wiederholt fehl Stark verzerrter Text Neues Bild anfordern und erneut lösen
PACER sperrt den Zugriff Rate-Limit überschritten 30 Minuten warten, Anfragefrequenz senken
Falldetails unvollständig Inhalt hinter Paywall Anfallende Gebühren pro Seite einplanen
Suche liefert keine Treffer Statt Ergebnissen kam die CAPTCHA-Seite Vor dem Parsen auf CAPTCHA prüfen
Docket-Überwachung verpasst Einreichungen Prüfintervall zu lang Prüfhäufigkeit erhöhen

Häufige Fragen

Welche CAPTCHA-Typen kommen auf Rechtsportalen vor?

Fast ausschließlich reCAPTCHA v2 und Bild-CAPTCHAs. PACER und Bundesgerichte setzen reCAPTCHA v2 ein, ältere Portale oft verzerrte Bild-CAPTCHAs. Beide deckt CaptchaAI ab – reCAPTCHA v2 über userrecaptcha, Bild-CAPTCHAs über die OCR-Methode.

Wie wirkt sich die Thread-basierte Abrechnung auf große Recherche-Jobs aus?

CaptchaAI rechnet pro gleichzeitigem Thread ab, nicht pro Lösung; die Lösungen pro Monat sind unbegrenzt. Für Einzelrecherchen genügt BASIC (15 $/Monat, 5 Threads); für viele parallele Docket-Feeds skaliert ADVANCE (90 $/Monat, 50 Threads).

Ist automatisiertes Scraping von Gerichtsdaten nach DSGVO zulässig?

Öffentliche Rechtsdaten sind grundsätzlich zugänglich, doch die Verarbeitung personenbezogener Angaben unterliegt der DSGVO. Klären Sie Ihre Rechtsgrundlage und respektieren Sie Nutzungsbedingungen und Rate-Limits.

Was tun, wenn ein Bild-CAPTCHA wiederholt falsch gelöst wird?

Fordern Sie ein neues Bild an und übermitteln Sie es erneut – verzerrte Bilder scheitern gelegentlich. Eine kurze Wartezeit zwischen den Versuchen schont das Rate-Limit.

Wie vermeide ich, dass Portale wie PACER den Zugriff sperren?

Drosseln Sie die Anfragefrequenz, verteilen Sie größere Läufe über rotierende Residential-Proxys und halten Sie sich strikt an die dokumentierten Limits des Portals. Löst ein Portal dennoch eine Sperre aus, warten Sie das angegebene Zeitfenster ab, bevor Sie fortfahren.


Verwandte Leitfäden


Rechtsrecherche automatisieren – Holen Sie sich Ihren CaptchaAI-Schlüssel und starten Sie mit der Rechtsprechungssuche.

Kommentare sind für diesen Artikel deaktiviert.