Reddit ist eines der größten öffentlichen Diskussionsarchive im Web, und die öffentlichen Listings eines Subreddits sind ein nützliches Signal für Forschungszwecke: welche Themen im Trend liegen, wie eine Community Links bewertet, welche externen Quellen geteilt werden und wie sich Scores und Kommentarzahlen im Laufe der Zeit entwickeln. Dieser Leitfaden zeigt, wie man öffentliche Reddit-Daten mit Python scrapt und die Crawlbase Crawling API nutzt, wobei die gesamte Anleitung auf öffentliche Listing-Seiten beschränkt ist.

Um es vorab klarzustellen: Alles hier bleibt bei öffentlichen, aggregierten Daten aus öffentlichen Subreddits und Topic-Listings. Das bedeutet Post-Titel, Scores und Upvote-Zählungen, Kommentarzahlen, das Subreddit, zu dem ein Post gehört, und der Link, auf den jeder Post verweist. Es behandelt nichts hinter einem Login, private Subreddits, Direktnachrichten oder persönliche Daten einzelner Nutzer. Reddits Nutzungsbedingungen schränken den automatisierten Zugriff ein, also den Abschnitt zur Rechtslage am Ende lesen, bevor man das auf etwas Echtes anwendet, und die offizielle Reddit API für jede produktive Nutzung bevorzugen.

Was Sie bauen werden

Ein kleines Python-Skript, das eine öffentliche Subreddit- oder Topic-Listing-URL nimmt, die vollständig gerenderte Seite über die Crawling API mit einem JavaScript-Token abruft und eine Handvoll öffentlicher Felder von jedem Post im Listing parst:

  • Titel der öffentliche Überschriftentext jedes Posts.
  • Score / Upvotes die aggregierte Stimmenzahl, die ein Post anzeigt.
  • Kommentaranzahl wie viele Kommentare der Post hat, als Zahl.
  • Subreddit die Community, zu der der Post gehört (zum Beispiel r/technology).
  • Link der Permalink oder ausgehende URL, auf den der Post verweist.

Beachten Sie, was bewusst fehlt: keine Nutzernamen, keine Autorenprofile, kein Kommentartext, keine stimmenweisen Aufschlüsselungen. Das sind persönliche Daten von Einzelpersonen, und deren Erfassung liegt hier absichtlich außerhalb des Umfangs. Wir aggregieren auf der Ebene des Posts und der Community, nie der Person.

Warum eine einfache Anfrage bei Reddit scheitert

Eine Reddit-Listing-URL mit einem einfachen HTTP-Client anfragen und man bekommt meist etwas nahezu Nutzloses: eine JavaScript-Shell, eine Cookie-Consent-Zwischenseite oder eine Challenge-Seite. Reddits aktuelles Frontend rendert Post-Listings clientseitig, sodass Titel, Scores und Links erst erscheinen, nachdem die Skripte der Seite in einem Browser ausgeführt wurden. Dazu kommt, dass Reddit automatisierten Traffic schnell erkennt. Datacenter-IP-Bereiche, fehlendes Browser-Verhalten und sich wiederholende Anfrage-Muster werden rate-limited oder blockiert, lange bevor das Listing jemals lädt.

Ein funktionierender Reddit-Scraper benötigt also zwei Dinge in derselben Anfrage: einen echten Browser, der die Seite rendert, und eine IP-Adresse, die Reddit als gewöhnlichen Besucher liest. Man kann das selbst mit einem Headless-Browser und einem Pool rotierender Residential-Proxies aufbauen, aber diesen Stack gesund zu halten ist der Großteil der Arbeit. Die Crawling API fasst beides in einem Aufruf zusammen. Man sendet ihr eine URL mit einem JavaScript-Token, sie rendert die Seite hinter einer vertrauenswürdigen Residential-IP und gibt fertiges HTML zurück, das man parsen kann. Den tieferen Hintergrund gibt es in unserem Leitfaden über how to crawl JavaScript websites.

Why the JS token

Crawlbase bietet zwei Token-Typen an. Der normale Token ruft statisches HTML ab; der JavaScript-Token (JS) rendert die Seite zuerst in einem echten Browser. Reddit-Listings sind clientseitig gerendert, also braucht man hier den JS-Token. Der normale Token gibt dieselbe Shell zurück, die ein einfacher Abruf ergeben würde, mit nichts Nützlichem darin zu parsen.

Voraussetzungen

Einige Dinge, die zuerst eingerichtet sein müssen. Keines davon dauert lange.

Grundlegendes Python. Man sollte mit dem Ausführen eines Skripts und dem Installieren von Paketen mit pip vertraut sein. Wer neu im HTML-Parsen ist, deckt unser Primer über how to use BeautifulSoup in Python die Extraktionsseite ab.

Python 3.8 oder höher. Mit python --version bestätigen. Wenn nicht vorhanden, von python.org installieren.

Ein Crawlbase-Konto und JS-Token. Anmelden, das Dashboard öffnen und den JavaScript-Token (JS) von der Konto-Docs-Seite kopieren. Wie ein Passwort behandeln: Er authentifiziert Anfragen, also aus der Versionskontrolle heraushalten. Der kostenlose Tarif gibt bis zu 20.000 Anfragen zum Testen.

Das Projekt einrichten

Eine isolierte virtuelle Umgebung erstellen, dann die zwei Bibliotheken installieren, die der Scraper benötigt.

bash
python --version

python -m venv reddit_env
source reddit_env/bin/activate

pip install crawlbase beautifulsoup4

Unter Windows mit reddit_env\Scripts\activate statt der source-Zeile aktivieren. Zwei Abhängigkeiten übernehmen die Arbeit: crawlbase ist der offizielle Client für die Crawling API, und beautifulsoup4 parst das zurückgegebene HTML, sodass man einzelne Felder per Selektor herausziehen kann.

Schritt 1: Das gerenderte Listing abrufen

Zunächst die fertige Seite holen. CrawlingAPI importieren, mit dem JS-Token initialisieren und eine öffentliche Listing-URL anfordern. Die ältere Version dieses Tutorials verwies auf ein öffentliches Topic-Listing, https://www.reddit.com/t/technology/, was ein gutes unpersönliches Ziel zum Starten ist. Den Statuscode vor dem Parsen prüfen, damit Fehler laut statt still bleiben.

python
from crawlbase import CrawlingAPI

crawlbase_token = "YOUR_CRAWLBASE_TOKEN"
api = CrawlingAPI({"token": crawlbase_token})

def crawl(page_url):
    options = {"ajax_wait": "true", "page_wait": 5000}
    response = api.get(page_url, options)
    if response["status_code"] == 200:
        return response["body"].decode("utf-8")
    print(f"Request failed: {response['status_code']}")
    return None

if __name__ == "__main__":
    listing_url = "https://www.reddit.com/t/technology/"
    html = crawl(listing_url)
    print(html[:500] if html else "No HTML returned")

Die zwei Wait-Optionen sind wichtig für ein clientseitig gerendertes Ziel. ajax_wait weist die API an, auf das vollständige Laden asynchroner Inhalte zu warten, und page_wait hält nach dem Laden eine feste Anzahl Millisekunden an, damit spät gerenderte Posts erscheinen, bevor die Seite erfasst wird. Fünf Sekunden sind ein vernünftiger Ausgangspunkt; erhöhen, wenn das Listing leer zurückkommt. Das Skript ausführen und man sollte echtes Listing-Markup sehen, was bestätigt, dass das Rendering funktioniert, bevor man einen einzigen Selektor schreibt.

Crawlbase Reddit Scraper

Dieses Listing hat sich nur gefüllt, weil die Seite hinter einer vertrauenswürdigen IP in einem Aufruf gerendert wurde. Die Crawling API nimmt einen JS-Token, führt die Seite in einem echten Browser aus, rotiert serverseitig durch Residential-IPs und gibt fertiges HTML zurück, sodass man keine eigene Headless-Flotte und keinen Proxy-Pool betreiben muss. Im kostenlosen Tarif zunächst auf ein öffentliches Subreddit zeigen.

Schritt 2: Die öffentlichen Post-Felder parsen

Mit fertigem HTML lädt man es in BeautifulSoup und zieht die öffentlichen Felder aus jedem Post. Reddits Listing-Markup gruppiert jeden Post in einem benutzerdefinierten shreddit-post-Element, und die nützlichen Werte liegen in den Attributen dieses Elements statt in tief verschachtelten, häufig umbenannten CSS-Klassen. Die gewünschten Attribute sind post-title, score, comment-count, subreddit-prefixed-name und permalink. Attribute zu lesen ist weit dauerhafter als das Verfolgen gerenderter Widgets.

python
from bs4 import BeautifulSoup

BASE = "https://www.reddit.com"

def to_int(value):
    try:
        return int(value)
    except (TypeError, ValueError):
        return None

def scrape_listing(html):
    soup = BeautifulSoup(html, "html.parser")
    posts = []
    for post in soup.select("shreddit-post"):
        permalink = post.get("permalink", "")
        link = f"{BASE}{permalink}" if permalink.startswith("/") else permalink
        posts.append({
            "title": post.get("post-title"),
            "score": to_int(post.get("score")),
            "comment_count": to_int(post.get("comment-count")),
            "subreddit": post.get("subreddit-prefixed-name"),
            "link": link,
        })
    return posts

Jeder Post wird zu einem flachen Datensatz öffentlicher, aggregierter Felder. Die Attribute score und comment-count kommen als Strings zurück, also konvertiert to_int sie zu Zahlen und gibt None zurück, wenn ein Attribut fehlt, statt den Durchlauf zum Absturz zu bringen. Das permalink-Attribut ist ein site-relativer Pfad wie /r/technology/comments/<id>/<slug>/, also verbinden wir ihn mit dem Basis-Host zu einem vollständigen Link. Beachten Sie, dass es in diesem Datensatz absichtlich kein Autorenfeld gibt.

Selectors drift

Reddit ändert sein Markup ohne Vorankündigung, weshalb dieser Code Attribute auf dem shreddit-post-Element liest statt spröder verschachtelter Klassen. Wenn ein Feld als None zurückkommt, die Live-Seite in den Entwicklerwerkzeugen des Browsers erneut inspizieren und den Attributnamen aktualisieren. Periodische Wartung ist für jeden Produktions-Scraper normal, kein Zeichen, dass etwas kaputt ist. Für eine Auffrischung zur Auswahl robuster Selektoren, siehe scrape websites without getting blocked.

Schritt 3: Alles mit Paginierung zusammenfügen

Eine einzelne Listing-Seite zeigt nur die erste Charge von Posts. Reddits ältere, render-freundliche Listing-Endpunkte akzeptieren ein after-Token für die nächste Seite, aber der zuverlässigste Weg, ein JS-gerendertes Listing durch die Crawling API zu paginieren, ist, den .json-Begleitendpunkt des Listings anzufragen, der öffentlich ist und dieselben Posts plus einen after-Cursor zurückgibt. Hier halten wir es einfach und paginieren das öffentliche Subreddit-Listing, sammeln eine feste Anzahl von Seiten und pausieren zwischen Anfragen.

python
import json
import time
from crawlbase import CrawlingAPI
from bs4 import BeautifulSoup

crawlbase_token = "YOUR_CRAWLBASE_TOKEN"
api = CrawlingAPI({"token": crawlbase_token})
BASE = "https://www.reddit.com"

def crawl(page_url):
    options = {"ajax_wait": "true", "page_wait": 5000}
    response = api.get(page_url, options)
    if response["status_code"] == 200:
        return response["body"].decode("utf-8")
    print(f"Request failed: {response['status_code']}")
    return None

def scrape_subreddit(subreddit, max_pages=3):
    records = []
    after = None
    for _ in range(max_pages):
        url = f"{BASE}/r/{subreddit}/.json?limit=25"
        if after:
            url += f"&after={after}"
        body = crawl(url)
        if not body:
            break
        data = json.loads(body)["data"]
        for child in data["children"]:
            post = child["data"]
            records.append({
                "title": post.get("title"),
                "score": post.get("score"),
                "comment_count": post.get("num_comments"),
                "subreddit": f"r/{post.get('subreddit')}",
                "link": f"{BASE}{post.get('permalink', '')}",
            })
        after = data.get("after")
        if not after:
            break
        time.sleep(3)
    return records

if __name__ == "__main__":
    posts = scrape_subreddit("technology", max_pages=3)
    print(json.dumps(posts, indent=2, ensure_ascii=False))

Der öffentliche .json-Endpunkt gibt dieselben aggregierten Felder mit saubereren Schlüsseln zurück: title, score, num_comments, subreddit und permalink. Der after-Cursor in jeder Antwort ist der einzige Zustand, den die Paginierung benötigt, also fordert die Schleife die nächste Seite an, bis Reddit keinen Cursor mehr zurückgibt oder man max_pages erreicht. Das time.sleep(3) zwischen Seiten ist keine Dekoration: Drosselung ist der wichtigste Faktor dafür, ob ein Durchlauf gesund bleibt. Wenn man das gerenderte HTML stattdessen bevorzugt, crawl(url) gegen die Listing-URL aus Schritt 1 tauschen und den Body an scrape_listing übergeben.

Wie die Ausgabe aussieht

Das Skript ausführen und man erhält eine saubere Liste öffentlicher, aggregierter Datensätze, bereit zum Schreiben in JSON oder CSV.

json
[
  {
    "title": "Researchers demo a swallowable device that tracks vital signs",
    "score": 8421,
    "comment_count": 312,
    "subreddit": "r/technology",
    "link": "https://www.reddit.com/r/technology/comments/17xmvmg/swallowable_device_tracking_vital_signs_inside/"
  }
]

Um diese Datensätze zu speichern, wandeln ein paar Standard-Bibliothekszeilen die Liste in eine CSV- oder JSON-Datei um. Die Felder sind flach und einheitlich, sodass keine besondere Behandlung erforderlich ist.

python
import csv
import json

def save_json(records, path="reddit_posts.json"):
    with open(path, "w", encoding="utf-8") as f:
        json.dump(records, f, indent=2, ensure_ascii=False)

def save_csv(records, path="reddit_posts.csv"):
    fields = ["title", "score", "comment_count", "subreddit", "link"]
    with open(path, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=fields)
        writer.writeheader()
        writer.writerows(records)

Von hier aus sind die Daten zur Analyse bereit: Subreddits nach dem Median-Score ranken, verfolgen, welche externen Domains eine Community verlinkt, oder Kommentaraktivität über ein Fenster von Seiten darstellen. Wenn sie später in ein Modell eingespeist werden sollen, behandelt unser Leitfaden über how to structure and clean web-scraped data for AI and ML den Normalisierungsschritt, und web scraping for machine learning erklärt, was man danach damit macht.

Rate-Limits und Fehler behandeln

Zwei Ebenen können einen Reddit-Durchlauf drosseln, und ein resilientes Skript berücksichtigt beide. Reddit rate-limitet automatisierten Traffic und gibt 429 Too Many Requests oder 403 Forbidden zurück, wenn man zu stark drückt; die Crawling API hat ebenfalls Plan-Limits. Die folgenden Gewohnheiten halten einen Durchlauf innerhalb beider.

  • Anfragen drosseln. Das time.sleep(3) zwischen Seiten ist die Untergrenze, kein Maximum. Das Listing in einer engen Schleife zu hämmern ist der schnellste Weg, gedrosselt zu werden, also echte Verzögerungen hinzufügen und aggressives Parallelisieren vermeiden.
  • Statuscodes lesen. Ein Durchlauf, der beginnt, 429 oder 403 zurückzugeben, signalisiert, dass die aktuelle Rate nicht mehr ausreicht. Zurückweichen statt härter drücken, und exponentielles Backoff mit einigen Wiederholungsversuchen in Betracht ziehen.
  • Auf Rotation setzen. Ein Pool von Residential-IPs verteilt Anfragen über viele echte Benutzeradressen, sodass keine einzelne ein Limit auslöst. Die Crawling API handhabt das; wenn man einen eigenen Stack aufbaut, ist das der Teil, den man richtig machen muss.
  • Volumen niedrig und Ziele abwechslungsreich halten. Public-Data-Forschung erfordert kein Crawlen der gesamten Geschichte eines Subreddits. Die benötigten Seiten sampeln und aufhören.

Das ist der Abschnitt, den man lesen sollte, bevor man Produktionscode schreibt. Reddits Nutzervereinbarung und seine Public Content Policy schränken den automatisierten Zugriff und die Massenerfassung von Inhalten ein, und Reddits robots.txt legt fest, was Crawler berühren dürfen. Automatisches Scraping kann gegen diese Bedingungen verstoßen, unabhängig davon, wie sorgfältig das Tooling ist, und keiner der obigen Codes ändert das. Er macht nur den technischen Teil funktionsfähig. Reddits Bedingungen und robots.txt zuerst lesen und beide als Grenze für das Gesammelte betrachten.

Die ehrlichen, restriktiven Regeln, an die man sich halten sollte. Nur öffentliche, aggregierte Daten sammeln: Post-Titel, Scores, Kommentarzahlen, das Subreddit und der Link, die jeder ohne Anmeldung sehen kann. Nutzernamen, Autorenidentifikatoren, Profildetails und den Text einzelner Kommentare als persönliche Daten behandeln und sie nicht ernten, keine Profile identifizierbarer Personen erstellen oder Inhalte auf eine Person zurückführen. Niemals private Subreddits, login-gesperrte Inhalte, Direktnachrichten oder alles, was eine Authentifizierung erfordert, scrapen, und niemals einen Login oder eine Herausforderung umgehen, um darauf zuzugreifen. Wenn persönliche Daten beteiligt sind, gelten Datenschutzgesetze wie die DSGVO und der CCPA: man braucht eine Rechtsgrundlage für die Verarbeitung und muss Löschanfragen nachkommen. Der Scraper in diesem Leitfaden bleibt absichtlich auf der aggregierten, nicht-persönlichen Seite all dieser Grenzen.

Für jede echte oder kommerzielle Nutzung ist die offizielle Reddit API das richtige Werkzeug. Sie ist für sanktionierten Zugriff gebaut, bietet garantierte Struktur, stellt klare Rate-Limits bereit und hält einen innerhalb von Reddits Bedingungen. Dieser Artikel ist ein technischer Leitfaden, der eng auf öffentliche, aggregierte Listing-Daten beschränkt ist. Er ist keine Empfehlung für die Massenerfassung persönlicher Daten und behandelt nichts hinter einem Login. Wenn das Projekt mehr als eine kleine Stichprobe öffentlicher Felder benötigt, ist die Reddit API oder eine formale Datenvereinbarung der richtige Weg, kein cleverer Scraper.

Zusammenfassung

Wichtigste Erkenntnisse

  • Reddit-Listings sind clientseitig gerendert und bot-geschützt. Ein einfacher Abruf gibt eine Shell oder eine Herausforderung zurück, also muss die Seite gerendert werden, bevor man sie parst.
  • Rendering und eine vertrauenswürdige IP gehören in einen Aufruf. Die Crawling API mit einem JS-Token tut beides; ajax_wait und page_wait steuern, wie lange auf Inhalte gewartet wird.
  • Stabile Signale parsen. Die shreddit-post-Attribute (oder die öffentlichen .json-Endpunktschlüssel) sind dauerhafter als spröde verschachtelte Klassen.
  • Nur aggregierte, öffentliche Felder. Titel, Score, Kommentaranzahl, Subreddit und Link extrahieren; niemals Nutzernamen, Autorenprofile oder Kommentartext.
  • Drosseln, rotieren und die offizielle API bevorzugen. Volumen niedrig halten, auf Residential-Rotation setzen und die Reddit API für alles Echte oder Kommerzielle nutzen.

Häufig gestellte Fragen

Warum gibt eine einfache Anfrage keine Daten von Reddit zurück?

Weil Reddits aktuelles Frontend Post-Listings clientseitig mit JavaScript rendert und automatisierten Traffic herausfordert. Eine rohe HTTP-Anfrage gibt eine nahezu leere Shell, eine Cookie-Zwischenseite oder eine Blockierungsseite zurück. Um echte öffentliche Daten zu erhalten, muss die Seite zuerst gerendert werden, was der JS-Token der Crawling API für einen übernimmt.

Brauche ich den normalen Token oder den JS-Token für Reddit?

Den JS-Token für die gerenderten Listing-Seiten, da der normale Token dieselbe Shell zurückgibt, die ein einfacher Abruf ergeben würde. Wenn man stattdessen den öffentlichen .json-Endpunkt paginiert, ist diese Antwort einfaches JSON, aber das Leiten durch die Crawling API profitiert immer noch von der vertrauenswürdigen IP und Rotation, die den Durchlauf vor dem Blockieren bewahren.

Welche Reddit-Daten sind sicher zu scrapen?

Nur öffentliche, aggregierte Daten: Post-Titel, Scores und Upvote-Zählungen, Kommentarzahlen, das Subreddit und der Link, auf den jeder Post verweist. Nutzernamen, Autorenprofile und der Text einzelner Kommentare sind persönliche Daten und hier tabu. Private Subreddits, login-gesperrte Inhalte und Direktnachrichten liegen vollständig außerhalb des Umfangs.

Sollte ich die offizielle Reddit API nutzen oder die Seite scrapen?

Für jede echte, fortlaufende oder kommerzielle Nutzung die offizielle Reddit API nutzen. Sie ist der sanktionierte Weg, bietet garantierte Struktur und veröffentlicht klare Rate-Limits. Das Scrapen einer kleinen Stichprobe öffentlicher Listing-Felder mit dem hier beschriebenen Ansatz passt zu leichtgewichtiger Public-Data-Forschung, bei der kein API-Zugang vorhanden ist, solange man Reddits Bedingungen, robots.txt und Rate-Limits respektiert.

Wie handhabe ich die Paginierung über viele Seiten?

Den öffentlichen .json-Endpunkt des Listings mit einem limit anfordern, den after-Cursor aus jeder Antwort lesen und ihn als &after=<cursor> in der nächsten Anfrage übergeben. Die Schleife läuft, bis Reddit keinen Cursor mehr zurückgibt oder man das Seitenlimit erreicht, und ein paar Sekunden zwischen Seiten schlafen, um innerhalb der Rate-Limits zu bleiben.

Wie vermeide ich das Blockieren beim Scrapen von Reddit?

Die Anfragerate niedrig halten, echte Verzögerungen zwischen Seiten hinzufügen, Ziele abwechseln statt die gesamte Geschichte eines Subreddits zu crawlen, und durch rotierende Residential-IPs leiten, sodass keine einzelne Adresse ein Limit auslöst. Die Crawling API verwaltet Rotation und einen vertrauenswürdigen IP-Pool. Bei 429- und 403-Antworten sofort zurückweichen.

Jetzt loslegen

Crawlen Sie jede Website im großen Maßstab, ohne gegen die Infrastruktur zu kämpfen.

Crawlbase übernimmt Proxys, Fingerprints und CAPTCHAs, damit Ihr Team Datenpipelines ausliefert, statt Crawl-Infrastruktur zu pflegen. 1.000 Anfragen kostenlos, keine Karte erforderlich.

Self-Service · Kein Verkaufsgespräch erforderlich · Enterprise-Crawl-Volumen verfügbar