Anwendungsfälle

CAPTCHA-Verwaltung für den Preisvergleich in der Apotheke

Preisvergleichsdienste für Medikamente stoßen früh auf dieselbe Hürde: Sobald das Suchformular abgesendet wird, erscheint ein reCAPTCHA v2. CaptchaAI löst diese Abfragen per API, sodass Ihre Pipeline die Preisdaten aus mehreren Apothekenportalen zusammenführen kann. Dieser Leitfaden zeigt den kompletten Workflow – vom Auslesen des Sitekeys über den parallelen Abruf bis zum Rate-Limiting – und ordnet ihn in den rechtlichen Rahmen der DACH-Region ein.

Preisvergleich bei Medikamenten in der DACH-Region

Bevor Sie Code schreiben, lohnt sich ein Blick auf den Kontext. In Deutschland sind die Abgabepreise verschreibungspflichtiger (Rx-)Arzneimittel durch die Arzneimittelpreisverordnung (AMPreisV) bundesweit festgelegt – hier gibt es schlicht nichts zu vergleichen. Interessant wird ein Preisvergleich vor allem bei rezeptfreien (OTC-)Produkten, bei Versandapotheken und bei Kostenzuzahlungen, wo die Preise tatsächlich variieren.

Zwei Punkte gehören in jede Planung: Erstens gelten IP-Adressen unter der DSGVO als personenbezogene Daten – prüfen Sie Ihre Rechtsgrundlage und Ihre Datenflüsse, bevor Sie Portale systematisch abfragen. Zweitens greifen Sie ausschließlich auf öffentlich zugängliche Preisdaten in eigenen oder autorisierten Umgebungen zu. CaptchaAI übernimmt dabei nur einen Teil: das automatische Lösen der CAPTCHA-Abfrage, die dem eigentlichen Datenabruf im Weg steht.

Wo Apothekenportale CAPTCHAs einsetzen

Nicht jede Seite schützt sich gleich. Diese Übersicht zeigt, welche CAPTCHA-Typen Ihnen typischerweise begegnen und was sie auslöst:

Seitentyp Typisches CAPTCHA Auslöser
Medikamenten-Suchformular reCAPTCHA v2 bei jeder Suchanfrage
Preisergebnisseite Cloudflare Turnstile bei Verdacht auf automatisierten Zugriff
Apotheken-Umkreissuche reCAPTCHA v2 bei standortbezogenen Abfragen
Gutschein-/Rabattsuche Bild-CAPTCHA vor Anzeige der Rabattcodes
Erstattungsformular der Krankenkasse reCAPTCHA v2 an Login- und Such-Gates

reCAPTCHA v2, Cloudflare Turnstile und Bild-CAPTCHAs decken damit den Großteil der Fälle ab – alle drei werden von CaptchaAI unterstützt.

reCAPTCHA v2 lösen: der Basis-Workflow

Der Ablauf ist immer gleich: Suchseite laden, den sitekey aus dem HTML auslesen, die Abfrage über CaptchaAI lösen und das zurückgegebene Token als g-recaptcha-response mit dem Formular absenden.

import requests
import time

session = requests.Session()
session.headers.update({
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
})

def solve_recaptcha(site_key, page_url):
    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": "YOUR_API_KEY",
        "method": "userrecaptcha",
        "googlekey": site_key,
        "pageurl": page_url,
        "json": 1
    })
    task_id = resp.json()["request"]

    for _ in range(60):
        time.sleep(3)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": "YOUR_API_KEY",
            "action": "get",
            "id": task_id,
            "json": 1
        })
        data = result.json()
        if data["status"] == 1:
            return data["request"]
    raise TimeoutError("Solve timed out")

def search_drug_prices(drug_name, zipcode):
    search_url = "https://pharmacy.example.com/search"

    # Load the search page to get the site key
    page = session.get(search_url)

    # Extract reCAPTCHA site key
    import re
    match = re.search(r'data-sitekey="([^"]+)"', page.text)
    site_key = match.group(1)

    # Solve the CAPTCHA
    token = solve_recaptcha(site_key, search_url)

    # Submit the search with token
    results = session.post(search_url, data={
        "drug": drug_name,
        "zip": zipcode,
        "g-recaptcha-response": token
    })

    return parse_prices(results.text)

Die for-Schleife fragt den Status im Abstand von drei Sekunden ab und bricht nach einem Timeout ab – so bleibt ein hängendes Portal nicht unbemerkt.

Preise über mehrere Apotheken hinweg vergleichen

Ein echter Vergleich zieht mehrere Quellen heran. Legen Sie pro Apotheke Portal-URL und CAPTCHA-Typ als Konfiguration ab und sammeln Sie die Ergebnisse ein – die günstigste Quelle steht am Ende oben:

PHARMACY_SOURCES = [
    {
        "name": "PharmacyA",
        "url": "https://pharmacya.example.com/pricing",
        "captcha_type": "recaptcha_v2"
    },
    {
        "name": "PharmacyB",
        "url": "https://pharmacyb.example.com/drugs",
        "captcha_type": "turnstile"
    },
    {
        "name": "PharmacyC",
        "url": "https://pharmacyc.example.com/search",
        "captcha_type": "image"
    }
]

def compare_drug_price(drug_name, zipcode):
    results = []

    for source in PHARMACY_SOURCES:
        try:
            prices = fetch_prices(
                source["url"],
                source["captcha_type"],
                drug_name,
                zipcode
            )
            results.append({
                "source": source["name"],
                "prices": prices,
                "status": "success"
            })
        except Exception as e:
            results.append({
                "source": source["name"],
                "error": str(e),
                "status": "failed"
            })

    return sorted(results, key=lambda r: r.get("prices", {}).get("lowest", float("inf")))

Schlägt eine Quelle fehl, landet der Fehler im Ergebnis statt den ganzen Lauf abzubrechen – bei einem Vergleich über viele Portale ist Robustheit wichtiger als ein sauberer Abbruch.

Verschiedene CAPTCHA-Typen in einer Pipeline

Da Portale unterschiedliche Anbieter einsetzen, wählt eine kleine Verzweigung je nach captcha_type die passende Lösungsfunktion und das korrekte Formularfeld – g-recaptcha-response für reCAPTCHA v2, cf-turnstile-response für Turnstile:

def fetch_prices(url, captcha_type, drug_name, zipcode):
    page = session.get(url)

    if captcha_type == "recaptcha_v2":
        import re
        match = re.search(r'data-sitekey="([^"]+)"', page.text)
        token = solve_recaptcha(match.group(1), url)
        field_name = "g-recaptcha-response"

    elif captcha_type == "turnstile":
        import re
        match = re.search(r'data-sitekey="(0x[^"]+)"', page.text)
        token = solve_turnstile(match.group(1), url)
        field_name = "cf-turnstile-response"

    elif captcha_type == "image":
        img_data = extract_captcha_image(page.text)
        token = solve_image_captcha(img_data)
        field_name = "captcha"

    return session.post(url, data={
        "drug": drug_name,
        "zip": zipcode,
        field_name: token
    })

Neue Quellen fügen Sie hinzu, ohne die Aufruflogik anzufassen – nur der Konfigurationseintrag und gegebenenfalls ein weiterer Zweig kommen hinzu.

Sitzungen wiederverwenden und CAPTCHAs reduzieren

Jede gelöste Abfrage kostet einen Thread und Zeit. Halten Sie die requests.Session() über mehrere Suchen offen, lösen viele Portale erst nach einigen Anfragen wieder eine Abfrage aus. Diese Klasse löst nur dann, wenn die Antwort tatsächlich eine CAPTCHA-Seite ist:

class PharmacySession:
    def __init__(self, base_url):
        self.session = requests.Session()
        self.base_url = base_url
        self.searches_since_captcha = 0

    def search(self, drug_name, zipcode):
        result = self.session.post(f"{self.base_url}/search", data={
            "drug": drug_name,
            "zip": zipcode
        })

        if self.is_captcha_page(result.text):
            token = self.solve_page_captcha(result.text)
            result = self.session.post(f"{self.base_url}/search", data={
                "drug": drug_name,
                "zip": zipcode,
                "g-recaptcha-response": token
            })
            self.searches_since_captcha = 0
        else:
            self.searches_since_captcha += 1

        return result

    def is_captcha_page(self, html):
        return "g-recaptcha" in html or "cf-turnstile" in html

    def solve_page_captcha(self, html):
        import re
        match = re.search(r'data-sitekey="([^"]+)"', html)
        return solve_recaptcha(match.group(1), self.base_url)

Der Zähler searches_since_captcha verrät Ihnen zudem, nach wie vielen Abfragen eine Quelle im Schnitt erneut prüft – eine nützliche Kennzahl für die Kostenschätzung.

Parallele Abfrage in Node.js

Wer den Vergleich lieber im JavaScript-Stack betreibt, löst alle Quellen mit Promise.all parallel und sortiert die erfolgreichen Antworten nach Preis:

async function comparePharmacyPrices(drugName, zipCode, sources) {
  const results = await Promise.all(
    sources.map(async (source) => {
      try {
        const price = await fetchDrugPrice(source, drugName, zipCode);
        return { source: source.name, price, status: 'success' };
      } catch (error) {
        return { source: source.name, error: error.message, status: 'failed' };
      }
    })
  );

  return results
    .filter(r => r.status === 'success')
    .sort((a, b) => a.price - b.price);
}

async function fetchDrugPrice(source, drugName, zipCode) {
  // Solve CAPTCHA for this source
  const token = await solveCaptcha(source.siteKey, source.url);

  const response = await fetch(source.url, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      drug: drugName,
      zip: zipCode,
      'g-recaptcha-response': token
    })
  });

  return parsePrice(await response.text());
}

Rate-Limiting und Zugriffsmuster

Apothekenportale beobachten Zugriffsmuster genau. Wer zu schnell und zu gleichförmig abfragt, provoziert mehr CAPTCHAs und riskiert eine Sperre. Diese vier Stellschrauben halten die Last unauffällig:

Strategie Umsetzung
Anfragen zeitlich strecken 5–10 Sekunden zwischen den Suchvorgängen
Sitzungen rotieren neue Sitzung alle 20–30 Abfragen
Suchmuster variieren nicht wiederholt nach demselben Medikament suchen
Residential-Proxys einsetzen Rechenzentrums-IPs lösen mehr CAPTCHAs aus

Häufige Probleme und Lösungen

Problem Ursache Lösung
Bei jeder Suche erscheint ein CAPTCHA Cookies der Sitzung werden nicht übernommen requests.Session() verwenden und Cookies über alle Anfragen halten
Preise laden trotz gelöster Abfrage nicht Der Parser liest eine alte oder unvollständige Ansicht aus Daten erst nach erfolgreicher Token-Anwendung in derselben Sitzung extrahieren
Sperre nach vielen Suchvorgängen Abrufrate oder Parallelität ist für die Quelle zu aggressiv Intervalle drosseln, Sitzungen stabil halten und Proxy-Qualität prüfen
Andere Preise als im Browser fehlende Session-Cookies oder Header Browser-Header exakt nachbilden

FAQ

Ist das Scrapen von Apothekenpreisen in Deutschland zulässig?

Das hängt vom Portal und von Ihrer Datenverarbeitung ab. Öffentlich einsehbare Preise abzurufen ist grundsätzlich etwas anderes, als sich über geschützte Bereiche hinwegzusetzen – prüfen Sie AGB und Nutzungsbedingungen der Quelle. Weil IP-Adressen unter der DSGVO personenbezogen sind, gehören Rechtsgrundlage und Datenflüsse in jede seriöse Planung. CaptchaAI löst nur die CAPTCHA-Abfrage; die rechtliche Bewertung des Zugriffs bleibt bei Ihnen.

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

In der Praxis dominiert reCAPTCHA v2 auf Suchformularen, gefolgt von Cloudflare Turnstile auf Ergebnisseiten und vereinzelt Bild-CAPTCHAs vor Rabattcodes. Alle drei unterstützt CaptchaAI, sodass Sie für einen Anbieterwechsel nur den captcha_type in Ihrer Konfiguration anpassen, nicht die gesamte Logik.

Was kostet das Lösen der CAPTCHAs bei einem Vergleich über viele Apotheken?

CaptchaAI rechnet pro gleichzeitigem Thread ab – nicht pro gelöstem CAPTCHA. Der Einstiegstarif BASIC (15 $/Monat, 5 Threads) erlaubt bereits unbegrenzte Lösungen und fünf parallele Abfragen; wer viele Portale gleichzeitig prüft, greift zu STANDARD (30 $/Monat, 15 Threads) oder ADVANCE (90 $/Monat, 50 Threads). Ihre Kosten hängen also von der gewünschten Parallelität ab, nicht von der Zahl der Suchvorgänge.

Wie vermeide ich, bei jeder Suche ein neues CAPTCHA zu erhalten?

Halten Sie eine Sitzung offen und übernehmen Sie deren Cookies über alle Anfragen hinweg, wie in der Klasse PharmacySession gezeigt. Viele Portale prüfen dann erst nach mehreren Abfragen erneut. Ergänzend helfen realistische Browser-Header, ruhige Abfrageintervalle und Residential-Proxys – Rechenzentrums-IPs lösen deutlich mehr Abfragen aus.


Verwandte Leitfäden

Kommentare sind für diesen Artikel deaktiviert.