Anwendungsfälle

Web Scraping für akademische Forschung mit CAPTCHA-Lösung

Wer für eine Literaturrecherche, eine Metastudie oder eine bibliometrische Auswertung tausende Treffer aus Google Scholar, PubMed oder Web of Science ziehen will, stößt früher oder später auf ein reCAPTCHA oder eine Cloudflare-Turnstile-Abfrage – und die Sammel-Pipeline steht still. CaptchaAI löst genau diese Abfragen per API: reCAPTCHA v2, reCAPTCHA v3 und Cloudflare Turnstile werden automatisch verarbeitet, Ihr Scraper übermittelt das Token und setzt die Suche fort.

Dieser Leitfaden zeigt, welche Wissenschaftsportale welche CAPTCHA-Typen einsetzen, wie ein belastbarer Sammel-Workflow in Python aussieht und wie Sie Anfragen so drosseln, dass Ihre IP nicht gesperrt wird.


Welche Wissenschaftsportale CAPTCHAs auslösen

Nicht jedes Portal schützt sich gleich. Google Scholar reagiert auf umfangreiche Abfragen mit reCAPTCHA v3, PubMed und die großen Verlagsdatenbanken setzen meist auf reCAPTCHA v2, und Web of Science schaltet bei Massen-Downloads Cloudflare Turnstile vor. Die folgende Übersicht fasst zusammen, wo Sie mit welcher Abfrage rechnen müssen:

Quelle CAPTCHA-Typ Auslöser Daten
Google Scholar reCAPTCHA v3 Umfangreiche Abfragen Zitate, Aufsätze
PubMed reCAPTCHA v2 Wiederholte Suche Biomedizinische Literatur
Web of Science Cloudflare Turnstile Massen-Downloads Zitiermetriken
Scopus reCAPTCHA v2 Exportvorgänge Bibliometrische Daten
IEEE Xplore reCAPTCHA v2 Suchen + Herunterladen Technische Arbeiten
JSTOR reCAPTCHA v2 Zugriffsseiten Geistes- und Sozialwissenschaften

Alle drei Typen – reCAPTCHA v2, reCAPTCHA v3 und Cloudflare Turnstile – deckt CaptchaAI ab. hCaptcha und FunCaptcha werden dagegen nicht unterstützt; treffen Sie auf ein Portal mit diesen Abfragen, brauchen Sie einen anderen Weg (etwa eine offizielle API).


Zitationsdaten trotz CAPTCHA automatisch erfassen

Das folgende Beispiel kapselt den kompletten Ablauf: Suche, CAPTCHA-Erkennung, Lösung über CaptchaAI und Export nach CSV. Die Hilfsfunktion solve_captcha übermittelt Sitekey und Page-URL an den Endpunkt in.php, fragt anschließend res.php per Polling ab und gibt das fertige Token zurück. Die Klasse AcademicScraper erkennt über _has_captcha, ob eine Trefferseite eine Abfrage enthält, und wiederholt die Anfrage mit eingefügtem Token.

import requests
import time
import re
from bs4 import BeautifulSoup
import csv

CAPTCHAAI_KEY = "YOUR_API_KEY"
CAPTCHAAI_URL = "https://ocr.captchaai.com"


def solve_captcha(method, sitekey, pageurl, **kwargs):
    data = {
        "key": CAPTCHAAI_KEY, "method": method,
        "googlekey": sitekey, "pageurl": pageurl, "json": 1,
    }
    data.update(kwargs)
    resp = requests.post(f"{CAPTCHAAI_URL}/in.php", data=data)
    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,
        })
        r = result.json()
        if r["request"] != "CAPCHA_NOT_READY":
            return r["request"]
    raise TimeoutError("Timeout")


class AcademicScraper:
    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",
            "Accept-Language": "en-US,en;q=0.9",
        })

    def search_papers(self, search_url, query, max_pages=10):
        """Search academic database for papers matching query."""
        all_papers = []

        for page in range(max_pages):
            url = f"{search_url}?q={query}&start={page * 10}"
            resp = self.session.get(url, timeout=30)

            # Handle CAPTCHA
            if self._has_captcha(resp.text):
                resp = self._solve_and_retry(resp.text, url)

            papers = self._parse_results(resp.text)
            if not papers:
                break  # No more results

            all_papers.extend(papers)
            print(f"Page {page + 1}: {len(papers)} papers")
            time.sleep(5)  # Respectful delay

        return all_papers

    def get_paper_details(self, paper_url):
        """Get detailed metadata for a single paper."""
        resp = self.session.get(paper_url, timeout=30)

        if self._has_captcha(resp.text):
            resp = self._solve_and_retry(resp.text, paper_url)

        soup = BeautifulSoup(resp.text, "html.parser")
        return {
            "title": self._safe_text(soup, "h1, .article-title"),
            "authors": self._safe_text(soup, ".authors, .author-list"),
            "abstract": self._safe_text(soup, ".abstract, #abstract"),
            "doi": self._safe_text(soup, ".doi, [data-doi]"),
            "journal": self._safe_text(soup, ".journal-name, .publication"),
            "year": self._safe_text(soup, ".pub-date, .year"),
            "citations": self._safe_text(soup, ".citation-count, .cited-by"),
        }

    def export_to_csv(self, papers, filename):
        """Export collected papers to CSV."""
        if not papers:
            return
        keys = papers[0].keys()
        with open(filename, "w", newline="", encoding="utf-8") as f:
            writer = csv.DictWriter(f, fieldnames=keys)
            writer.writeheader()
            writer.writerows(papers)
        print(f"Exported {len(papers)} papers to {filename}")

    def _has_captcha(self, html):
        return any(tag in html.lower() for tag in [
            'data-sitekey', 'g-recaptcha', 'cf-turnstile',
        ])

    def _solve_and_retry(self, html, url):
        match = re.search(r'data-sitekey="([^"]+)"', html)
        if not match:
            return self.session.get(url)

        sitekey = match.group(1)
        if 'cf-turnstile' in html:
            token = solve_captcha("turnstile", sitekey, url)
            return self.session.post(url, data={"cf-turnstile-response": token})
        else:
            token = solve_captcha("userrecaptcha", sitekey, url)
            return self.session.post(url, data={"g-recaptcha-response": token})

    def _parse_results(self, html):
        soup = BeautifulSoup(html, "html.parser")
        papers = []
        for item in soup.select(".gs_r, .search-result, article.result"):
            title_el = item.select_one("h3 a, .result-title a")
            if title_el:
                papers.append({
                    "title": title_el.get_text(strip=True),
                    "url": title_el.get("href", ""),
                    "snippet": self._safe_text(item, ".gs_rs, .abstract-snippet"),
                    "authors": self._safe_text(item, ".gs_a, .author-info"),
                })
        return papers

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


# Usage — Literature review
scraper = AcademicScraper(
    proxy="http://user:pass@residential.proxy.com:5000"
)

papers = scraper.search_papers(
    "https://scholar.example.com/scholar",
    query="machine learning CAPTCHA solving",
    max_pages=5,
)

# Get details for top papers
detailed = []
for paper in papers[:20]:
    if paper["url"]:
        detail = scraper.get_paper_details(paper["url"])
        detailed.append(detail)
        time.sleep(3)

scraper.export_to_csv(detailed, "literature_review.csv")

Der Proxy im Beispiel ist bewusst gesetzt: Läuft die Erfassung über einen Residential-Proxy, verteilt sich der Traffic und einzelne IPs werden seltener markiert. CaptchaAI und Ihr Proxy arbeiten dabei unabhängig – der Proxy trägt die Browsersitzung, CaptchaAI übernimmt ausschließlich die CAPTCHA-Lösung.


Zitationsnetzwerke bibliometrisch auswerten

Für eine bibliometrische Analyse reicht eine flache Trefferliste nicht. Sie folgen den „cited by"-Verweisen in die Tiefe und bauen so schrittweise ein Zitationsnetzwerk auf. Die folgende Funktion begrenzt sowohl die Tiefe (depth) als auch die Breite pro Ebene, damit der Crawl nicht exponentiell explodiert – und ruft bei jeder Ebene denselben CAPTCHA-Mechanismus aus dem ersten Beispiel auf.

def bibliometric_analysis(scraper, seed_papers, depth=2):
    """Follow citations to build a citation network."""
    visited = set()
    network = []

    def _crawl(paper_url, current_depth):
        if current_depth > depth or paper_url in visited:
            return
        visited.add(paper_url)

        try:
            details = scraper.get_paper_details(paper_url)
            network.append(details)

            # Follow "cited by" links
            resp = scraper.session.get(f"{paper_url}/citations", timeout=30)
            if scraper._has_captcha(resp.text):
                resp = scraper._solve_and_retry(resp.text, f"{paper_url}/citations")

            citations = scraper._parse_results(resp.text)
            for cite in citations[:5]:  # Limit breadth
                if cite["url"]:
                    _crawl(cite["url"], current_depth + 1)
                    time.sleep(3)

        except Exception as e:
            print(f"Error crawling {paper_url}: {e}")

    for paper in seed_papers:
        _crawl(paper["url"], 0)

    return network

Ein Rechenbeispiel zur Kapazität: Wer mehrere Portale parallel abfragt, profitiert von mehr gleichzeitigen Threads. Der Tarif STANDARD (30 $/Monat, 15 Threads) erlaubt 15 gleichzeitig laufende Lösungen mit unbegrenzten Lösungen pro Thread; für kleinere Projekte genügt oft BASIC (15 $/Monat, 5 Threads). Abgerechnet wird pro Thread – nicht pro gelöstem CAPTCHA.


Rate-Limits für wissenschaftliches Scraping

Wissenschaftsportale sperren IPs schnell und dauerhaft. Konservative Verzögerungen sind hier kein Nice-to-have, sondern Voraussetzung dafür, dass eine Erfassung überhaupt bis zum Ende läuft. Die folgenden Richtwerte haben sich in der Praxis bewährt:

Quelle Empfohlene Verzögerung Max. Seiten/Std.
Google Scholar 10–15 s 40–50
PubMed 3–5 s 100
Web of Science 5–10 s 60
Scopus 5–10 s 60
IEEE 3–5 s 100
JSTOR 5–10 s 60

Google Scholar ist mit Abstand am empfindlichsten. Bleiben Sie hier bei 10–15 s Pause zwischen Anfragen und rotieren Sie Residential-Proxys, statt die Frequenz zu erhöhen.


Typische Fehler beim Scraping beheben

Die meisten Probleme lassen sich auf drei Ursachen zurückführen: eine markierte IP, eine unbemerkt zurückgegebene CAPTCHA-Seite oder eine Paywall. Diese Tabelle ordnet Symptom, Ursache und Gegenmaßnahme zu:

Problem Ursache Lösung
CAPTCHA bei jeder Suche Portal hat die IP markiert Proxy wechseln, Verzögerung auf 15+ s erhöhen
Keine Ergebnisse zurückgegeben Statt der Trefferseite kam die CAPTCHA-Seite Vor dem Parsen auf CAPTCHA prüfen
Abstract fehlt Inhalt hinter Paywall Institutionellen Proxy oder Open Access nutzen
Google Scholar sperrt die IP Rate-Limit überschritten 30 Minuten warten, andere IP verwenden
Export begrenzt Portal deckelt Massen-Downloads In kleineren Chargen herunterladen

Rechtliches und DSGVO für Forschende

Sauberes wissenschaftliches Scraping endet nicht beim Code. Öffentliche Metadaten – Titel, Autoren, Abstracts – sind in der Regel frei zugänglich, der Volltext hängt dagegen an Lizenzen Ihrer Einrichtung. Nutzen Sie, wo vorhanden, immer zuerst die offizielle Schnittstelle: PubMed bietet mit den E-Utilities eine dokumentierte API, viele Verlage haben vergleichbare Zugänge.

Für Forschende im DACH-Raum kommt die DSGVO hinzu: Sobald Sie personenbezogene Daten erfassen – dazu zählen bereits IP-Adressen in Ihren Logs – sollten Sie Zweck und Rechtsgrundlage dokumentieren und die Datensparsamkeit im Blick behalten. Prüfen Sie außerdem die Nutzungsbedingungen des jeweiligen Portals; das ist eine Frage der Sorgfaltspflicht, keine, die CaptchaAI für Sie beantwortet. Preise von CaptchaAI verstehen sich in US-Dollar.


Häufige Fragen

Welche CAPTCHA-Typen der Wissenschaftsportale löst CaptchaAI?

reCAPTCHA v2, reCAPTCHA v3 und Cloudflare Turnstile – also genau die Abfragen, die Google Scholar, PubMed, Scopus und Web of Science einsetzen. hCaptcha und FunCaptcha werden nicht unterstützt.

Wie viele Anfragen kann ich parallel abarbeiten?

So viele, wie Ihr Tarif Threads bietet: BASIC (15 $/Monat) 5 Threads, STANDARD (30 $/Monat) 15 Threads, jeweils mit unbegrenzten Lösungen pro Thread. Abgerechnet wird pro Thread, nicht pro gelöstem CAPTCHA.

Ersetzt CaptchaAI die offizielle API von PubMed oder Scopus?

Nein. Wo eine offizielle API existiert, ist sie der bessere Weg – stabiler und ausdrücklich erlaubt. CaptchaAI ist für Quellen gedacht, die keinen programmatischen Zugang bieten und stattdessen ein CAPTCHA vorschalten.

Ist Web Scraping für Forschungszwecke DSGVO-konform?

Das hängt davon ab, welche Daten Sie erfassen. Reine Publikationsmetadaten sind unkritisch; sobald personenbezogene Daten ins Spiel kommen, brauchen Sie Zweck und Rechtsgrundlage. Prüfen Sie die Nutzungsbedingungen der Quelle im Zweifel vorab.


Verwandte Leitfäden

  • Residential-Proxys rotieren
  • Wie Proxy-Qualität die Lösungsrate beeinflusst

Beschleunigen Sie Ihre Literaturrecherche – holen Sie sich Ihren CaptchaAI-Schlüssel und automatisieren Sie die akademische Datenerfassung.

Kommentare sind für diesen Artikel deaktiviert.