Anwendungsfälle

Erfassung von Gesundheitsdaten hinter CAPTCHA-Wänden

Öffentliche Gesundheitsdaten stecken oft hinter einer Suchmaske mit reCAPTCHA v2 oder einem Bild-CAPTCHA – und genau an dieser Stelle bricht ein automatisierter Abruf ab. CaptchaAI löst diese Abfragen per API und liefert das fertige Token zurück, sodass Ihr Python-Scraper Anbieterverzeichnisse, Arzneimittelpreise und Studienregister ohne manuelles Eingreifen ausliest. Dieser Leitfaden zeigt, wo die CAPTCHAs auf Gesundheitsportalen auftauchen, wie Sie sie im Code lösen und welche rechtlichen Grenzen dabei gelten.


Wo Gesundheitsportale CAPTCHAs einsetzen

Nicht jede Seite schützt gleich stark. Bevor Sie einen Scraper aufsetzen, lohnt sich der Blick, welcher CAPTCHA-Typ an welcher Datenquelle sitzt – denn davon hängt die passende Lösungsmethode ab.

Quelle CAPTCHA-Typ Daten Anwendungsfall
Anbieterverzeichnisse (NPI) Bild-CAPTCHA Ärzte- und Einrichtungssuche Netzabdeckung prüfen
Arzneimittelpreis-Portale reCAPTCHA v2 Medikamentenpreise Preistransparenz
Register für klinische Studien reCAPTCHA v2 Studiendaten, Ergebnisse Forschungsanalyse
Versicherungs-Formulare reCAPTCHA v2 Erstattungsfähige Präparate Formularvergleich
Zulassungs- und Approbationsbehörden Bild-CAPTCHA Zulassungsprüfung Nachweis der Qualifikation
Klinik-Qualitätsberichte Cloudflare Turnstile Qualitätskennzahlen Leistungsvergleich

reCAPTCHA v2 und Bild-CAPTCHAs decken damit den Großteil der Fälle ab. Cloudflare Turnstile taucht vor allem bei neueren Qualitätsportalen auf; CaptchaAI löst alle drei Typen über dieselbe API.


Gesundheitsdaten hinter CAPTCHA-Schutz im DACH-Raum

Die Codebeispiele in diesem Artikel orientieren sich an US-Portalen (NPI-Register, CMS-Preistransparenz), weil dort viele Datenquellen offen zugänglich sind. Das Muster überträgt sich aber direkt auf den deutschsprachigen Raum: Arztsuch-Portale der Kassenärztlichen Vereinigungen, Arzneimittel-Datenbanken wie die Gelbe Liste und das Deutsche Register Klinischer Studien (DRKS) folgen demselben Ablauf – Suchmaske aufrufen, CAPTCHA lösen, Formular absenden, Trefferliste parsen.

Ein Punkt verdient in der DACH-Region besondere Aufmerksamkeit: Sobald Sie über Proxys arbeiten, gelten IP-Adressen nach DSGVO als personenbezogene Daten. Prüfen Sie vor jedem größeren Lauf, welche Datenflüsse entstehen und auf welche Rechtsgrundlage Sie sich stützen. Das gilt als Sorgfaltspflicht auf Ihrer Seite – nicht als Compliance-Zusage von CaptchaAI. Beschränken Sie die Erfassung strikt auf öffentlich einsehbare, nicht patientenbezogene Informationen, dann bleiben Sie auf der sicheren Seite.


Anbieterverzeichnisse per Python-Scraper abrufen

Der folgende Collector kapselt beide Lösungswege in einer Klasse: solve_recaptcha für reCAPTCHA-v2-geschützte Preis- und Studienportale, solve_image_captcha für die klassischen Bild-CAPTCHAs der Arztverzeichnisse. Der Ablauf ist immer gleich – Suchseite laden, CAPTCHA lösen, Token oder Text ins Formular einfügen, Antwort parsen. Über batch_provider_lookup fahren Sie mehrere Fachrichtungen und Standorte in einem Durchlauf ab und schreiben die Treffer als CSV.

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 HealthcareDataCollector:
    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_providers(self, portal_url, specialty, location, sitekey=None):
        """Search provider directory with CAPTCHA handling."""
        resp = self.session.get(portal_url, timeout=30)

        data = {"specialty": specialty, "location": location}

        # Handle CAPTCHA
        if sitekey:
            token = solve_recaptcha(sitekey, portal_url)
            data["g-recaptcha-response"] = token
        else:
            captcha_img = re.search(r'src="(/captcha[^"]+)"', resp.text)
            if captcha_img:
                img_url = portal_url.rstrip("/") + captcha_img.group(1)
                img = self.session.get(img_url)
                data["captcha"] = solve_image_captcha(img.content)

        resp = self.session.post(portal_url, data=data)
        return self._parse_providers(resp.text)

    def lookup_drug_prices(self, pricing_url, drug_name, zip_code, sitekey):
        """Look up drug prices with CAPTCHA solving."""
        # Load search page
        self.session.get(pricing_url)

        # Solve CAPTCHA
        token = solve_recaptcha(sitekey, pricing_url)

        resp = self.session.post(pricing_url, data={
            "drug": drug_name,
            "zip": zip_code,
            "g-recaptcha-response": token,
        })

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

    def batch_provider_lookup(self, portal_url, specialties, locations, output_file):
        """Batch search across specialties and locations."""
        all_providers = []

        for specialty in specialties:
            for location in locations:
                try:
                    providers = self.search_providers(
                        portal_url, specialty, location,
                    )
                    for p in providers:
                        p["specialty_search"] = specialty
                        p["location_search"] = location
                    all_providers.extend(providers)
                    print(f"{specialty} / {location}: {len(providers)} providers")
                    time.sleep(5)
                except Exception as e:
                    print(f"Error: {specialty} / {location}: {e}")

        # Export
        if all_providers:
            keys = all_providers[0].keys()
            with open(output_file, "w", newline="", encoding="utf-8") as f:
                writer = csv.DictWriter(f, fieldnames=keys)
                writer.writeheader()
                writer.writerows(all_providers)

        return all_providers

    def _parse_providers(self, html):
        soup = BeautifulSoup(html, "html.parser")
        providers = []
        for card in soup.select(".provider-card, .doctor-result, tr.provider"):
            providers.append({
                "name": self._text(card, ".name, .provider-name"),
                "specialty": self._text(card, ".specialty"),
                "address": self._text(card, ".address"),
                "phone": self._text(card, ".phone"),
                "accepting": self._text(card, ".accepting-patients"),
            })
        return providers

    def _parse_prices(self, html):
        soup = BeautifulSoup(html, "html.parser")
        prices = []
        for row in soup.select(".pharmacy-row, .price-result"):
            prices.append({
                "pharmacy": self._text(row, ".pharmacy-name"),
                "price": self._text(row, ".price, .drug-price"),
                "quantity": self._text(row, ".quantity"),
            })
        return prices

    def _text(self, el, selector):
        found = el.select_one(selector)
        return found.get_text(strip=True) if found else ""


# Usage
collector = HealthcareDataCollector(
    proxy="http://user:pass@residential.proxy.com:5000"
)

# Provider search
providers = collector.search_providers(
    portal_url="https://provider-directory.example.com/search",
    specialty="Cardiology",
    location="New York, NY",
)

# Drug pricing
prices = collector.lookup_drug_prices(
    pricing_url="https://drug-prices.example.com/compare",
    drug_name="atorvastatin",
    zip_code="10001",
    sitekey="6Lc_xxxxxxx",
)

Für den reCAPTCHA-Fall genügt der sitekey der Seite – CaptchaAI liefert das g-recaptcha-response-Token, das Sie zusammen mit den Suchparametern absenden. Bei Bild-CAPTCHAs laden Sie die Grafik, reichen sie base64-kodiert ein und tragen den erkannten Text ins Formularfeld ein.


Studiendaten aus klinischen Registern erfassen

Register für klinische Studien liefern strukturierte Datensätze – Titel, Status, Sponsor, Studienphase, Teilnehmerzahl und Standort. Die Suchmaske ist typischerweise mit reCAPTCHA v2 abgesichert. Die folgende Funktion baut auf demselben Collector auf: Token lösen, Suchanfrage mit dem Status recruiting absenden, Trefferliste in eine Liste von Dictionaries überführen.

def collect_clinical_trials(search_url, condition, sitekey):
    """Collect clinical trial data for a medical condition."""
    collector = HealthcareDataCollector(
        proxy="http://user:pass@residential.proxy.com:5000"
    )

    token = solve_recaptcha(sitekey, search_url)
    resp = collector.session.post(search_url, data={
        "condition": condition,
        "status": "recruiting",
        "g-recaptcha-response": token,
    })

    if resp.status_code != 200:
        return []

    soup = BeautifulSoup(resp.text, "html.parser")
    trials = []
    for item in soup.select(".trial-item, .study-result"):
        trials.append({
            "title": collector._text(item, ".title, h3"),
            "status": collector._text(item, ".status"),
            "sponsor": collector._text(item, ".sponsor"),
            "phase": collector._text(item, ".phase"),
            "enrollment": collector._text(item, ".enrollment"),
            "location": collector._text(item, ".location"),
        })

    return trials

Weil beide Beispiele denselben HealthcareDataCollector nutzen, teilen sie sich Session, Proxy-Konfiguration und User-Agent. So bleibt Ihr Zugriffsverhalten über Verzeichnis-, Preis- und Studienabfragen hinweg konsistent.


Datenschutz: öffentliche Daten von personenbezogenen Daten trennen

Die entscheidende Linie verläuft zwischen öffentlich veröffentlichten und personenbezogenen Daten. Anbieterverzeichnisse, regulierte Arzneimittelpreise und Studienregister sind bewusst öffentlich – Patientenakten dagegen niemals. Die folgende Einordnung hilft bei der Priorisierung:

Datentyp Sensibilität Empfehlung
Anbieterverzeichnisse Niedrig (öffentliche Angaben) In der Regel unbedenklich zu erfassen
Arzneimittelpreise Niedrig (öffentliche Preise) Für Transparenzzwecke zulässig
Metadaten klinischer Studien Niedrig (öffentliche Register) Für Forschungszwecke geeignet
Patientenbewertungen Mittel Vor der Auswertung anonymisieren
Versicherungstarif-Details Niedrig (veröffentlichte Tarife) Für Vergleiche zulässig

Wichtig: Versuchen Sie niemals, geschützte Gesundheitsinformationen (PHI) zu erfassen. Konzentrieren Sie sich ausschließlich auf öffentlich verfügbare, nicht patientenbezogene Daten – und dokumentieren Sie bei DSGVO-relevanten Läufen Ihre Rechtsgrundlage.


Typische Fehler und ihre Lösung

Die meisten Probleme entstehen nicht am CAPTCHA selbst, sondern an der Reihenfolge und am Tempo der Abfragen.

Problem Ursache Lösung
Bild-CAPTCHA nicht lesbar Bild in schlechter Qualität Erneut anfordern – es wird ein neues Bild erzeugt
Anbietersuche liefert leeres Ergebnis CAPTCHA hat die Suche blockiert CAPTCHA vor dem Absenden lösen
Medikamentenpreis schwankt je nach Region Geobasierte Preise Proxy-Standort an die Postleitzahl anpassen
Sitzung läuft über mehrere Seiten ab Portal-Timeout Suchvorgänge zügig abschließen
Rate-Limiting bei Massenabfragen Zu viele Anfragen Verzögerungen von 5–10 Sekunden einbauen

Häufige Fragen

Welche CAPTCHA-Typen kommen auf Gesundheitsportalen am häufigsten vor?

Meist reCAPTCHA v2 und klassische Bild-CAPTCHAs, bei neueren Qualitätsportalen zunehmend Cloudflare Turnstile. CaptchaAI löst alle drei Typen über dieselbe API – Sie brauchen keinen separaten Dienst pro Portal.

Wie schnell löst CaptchaAI ein reCAPTCHA v2 auf einem Portal?

In der Regel in unter 60 Sekunden. Bild-CAPTCHAs sind mit unter 0,5 Sekunden deutlich schneller, Cloudflare Turnstile liegt bei unter 10 Sekunden. Auf den unterstützten Typen erreicht der Dienst eine hohe Erfolgsquote.

Ist das Scraping von Gesundheitsdaten mit der DSGVO vereinbar?

Öffentlich veröffentlichte Daten – Anbieterverzeichnisse, regulierte Arzneimittelpreise, Studienregister – gelten nicht als personenbezogene Gesundheitsdaten. Sobald Sie über Proxys arbeiten, sind IP-Adressen jedoch personenbezogen: prüfen Sie Ihre Rechtsgrundlage und erfassen Sie niemals einzelne Patientendaten.

Wie viele Portale kann ich parallel abfragen?

So viele, wie Ihre Thread-Anzahl zulässt. CaptchaAI rechnet pro gleichzeitigem Thread ab, nicht pro Lösung. Schon der BASIC-Tarif (15 $/Monat, 5 Threads) erlaubt mehrere parallele Abfragen; für größere Crawls skalieren Sie über STANDARD (30 $/Monat, 15 Threads) oder ADVANCE (90 $/Monat, 50 Threads).


Verwandte Leitfäden


Gesundheitsdaten effizient erfassen – holen Sie sich Ihren CaptchaAI-Schlüssel und automatisieren Sie Anbieter- und Preissuchen.

Kommentare sind für diesen Artikel deaktiviert.