So scrape ich Goodreads mit Python (ohne leere Ergebnisse)

Zuletzt aktualisiert am April 16, 2026

Wenig ist frustrierender, als 30 Zeilen Python zu schreiben, den Goodreads-Scraper zu starten und statt Daten nur [] zurückzubekommen. Eine leere Liste. Nichts. Nur du und der blinkende Cursor.

Ich habe das Dutzende Male erlebt — in unseren internen Tests bei , in Entwicklerforen und in den GitHub-Issues verlassener Scraper-Repositories. Die Beschwerden sind fast immer dieselben: „Der Bereich mit den Top-Reviews ist leer, es kommt nur []“, „egal welche Seitennummer ich angebe, es wird immer nur die erste Seite gescrapt“, „mein Code hat letztes Jahr noch funktioniert, jetzt ist er kaputt.“ Und als wäre das nicht genug, wurde die Goodreads API im Dezember 2020 eingestellt. Der alte Rat aus vielen Tutorials — „nimm doch einfach die API“ — führt heute ins Leere.

Wenn du heute strukturierte Buchdaten von Goodreads brauchst — Titel, Autoren, Bewertungen, Rezensionen, Genres, ISBNs — kommst du am Scraping kaum vorbei. In diesem Leitfaden zeige ich dir einen funktionierenden, vollständigen Weg, um Goodreads mit Python zu scrapen: inklusive JS-gerendertem Inhalt, Pagination, Anti-Blocking und Export. Und falls Python nicht dein Ding ist, zeige ich dir eine No-Code-Alternative, mit der du die Aufgabe in ungefähr zwei Klicks erledigst.

Was bedeutet Goodreads Scraping — und warum mit Python?

Beim Goodreads Scraping werden Buchdaten — etwa Titel, Autoren, Bewertungen, Anzahl der Rezensionen, Genres, ISBNs, Seitenzahl, Veröffentlichungsdatum und mehr — automatisch von Goodreads-Webseiten extrahiert, statt sie per Hand zu kopieren.

Goodreads gehört zu den größten Buchdatenbanken der Welt, mit über und rund . Jeden Monat landen mehr als 18 Millionen Bücher auf „Want to Read“-Listen. Genau diese ständig aktualisierten, strukturierten Daten machen Goodreads für Verlage, Data Scientists, Buchhändler und Forschende so interessant.

Python ist dafür die Standardwahl — etwa aller Scraping-Projekte laufen mit Python. Die Bibliotheken sind ausgereift (requests, BeautifulSoup, Selenium, Playwright, pandas), die Syntax ist einsteigerfreundlich und die Community riesig.

Wenn du noch nie eine Website gescrapt hast, ist Python der beste Einstieg.

Warum Goodreads mit Python scrapen? Praxisnahe Anwendungsfälle

Bevor wir in den Code einsteigen, lohnt sich die Frage: Wer braucht diese Daten eigentlich — und wofür?

AnwendungsfallWer profitiertWas du scrapest
Marktanalyse für VerlageVerlage, LiteraturagentenTrend-Genres, bestbewertete Titel, neue Autoren, Bewertungen von Wettbewerbern
BuchempfehlungssystemeData Scientists, Hobbyisten, App-EntwicklerBewertungen, Genres, Nutzerregale, Tonalität von Rezensionen
Preis- und BestandsüberwachungOnline-BuchhändlerTrendtitel, Volumen an Rezensionen, Anzahl der "Want to Read"-Einträge
Wissenschaftliche ForschungForschende, StudierendeRezensionstexte, Verteilungen der Bewertungen, Genre-Klassifikationen
LesestatistikenBuchblogger, private ProjekteEigene Regaldaten, Leseverlauf, Jahresrückblicke

Ein paar konkrete Beispiele: Der UCSD Book Graph — eines der meistzitierten akademischen Datensätze in der Empfehlungsforschung — enthält , alles aus öffentlich zugänglichen Goodreads-Regalen. Mehrere Kaggle-Datensätze (goodbooks-10k, Best Books Ever usw.) sind aus Goodreads-Scraping entstanden. Und eine Studie aus dem Jahr 2025 in Big Data and Society hat ausgewertet, um zu untersuchen, wie gesponserte Reviews die Plattform beeinflussen.

Auch wirtschaftlich ist das Thema relevant: Bright Data verkauft vorab gescrapte Goodreads-Datensätze bereits ab 0,50 USD pro 1.000 Datensätze — ein klarer Beweis dafür, dass diese Daten echten Marktwert haben.

Die Goodreads API ist weg — das ist die aktuelle Alternative

Wenn du in letzter Zeit nach „Goodreads API“ gesucht hast, bist du wahrscheinlich auf veraltete Anleitungen gestoßen. Am 8. Dezember 2020 hat Goodreads still und leise aufgehört, neue Developer-API-Keys auszugeben. Kein Blogpost, kein E-Mail-Newsletter — nur ein kleiner Hinweis auf der Dokumentationsseite und viele verwirrte Entwickler.

goodreads-data-access-tools.webp

Die Folgen ließen nicht lange auf sich warten. Ein Entwickler, Kyle K, hatte einen Discord-Bot für Buchempfehlungen gebaut — „plötzlich macht es POOF und er funktioniert einfach nicht mehr.“ Ein anderer, Matthew Jones, verlor seinen API-Zugang eine Woche vor der Abstimmung für die Reddit r/Fantasy Stabby Awards und musste auf Google Forms zurückwechseln. Und das Masterarbeitsprojekt der Studentin Elena Neacsu wurde mitten in der Entwicklung ausgebremst.

Was bleibt also? Die aktuelle Landschaft sieht so aus:

AnsatzVerfügbare DatenBenutzerfreundlichkeitRate-LimitsStatus
Goodreads APIVollständige Metadaten, RezensionenFrüher einfach1 Anfrage/SekundeEingestellt (Dez. 2020) — keine neuen Keys
Open Library APITitel, Autoren, ISBNs, Cover (~30 Mio. Titel)Einfach1–3 Anfragen/SekundeAktiv, kostenlos, ohne Auth
Google Books APIMetadaten, VorschauenEinfach1.000/Tag kostenlosAktiv (Lücken bei nicht-englischen ISBNs)
Python-Scraping (requests + BS4)Alles, was im initialen HTML stecktMittelSelbst verwaltetFunktioniert für statische Inhalte
Python-Scraping (Selenium/Playwright)Auch JS-gerenderte InhalteSchwierigerSelbst verwaltetErforderlich für Rezensionen und manche Listen
Thunderbit (No-Code Chrome-Erweiterung)Alle sichtbaren SeitendatenSehr einfach (2 Klicks)Credit-basiertAktiv — kein Python nötig

Open Library ist eine starke Ergänzung, vor allem für ISBN-Suchen und grundlegende Metadaten. Wenn du jedoch Bewertungen, Rezensionen, Genre-Tags oder „Want to Read“-Zahlen brauchst, musst du Goodreads direkt scrapen — entweder mit Python oder mit einem Tool wie Thunderbit, das Goodreads-Seiten (inklusive Unterseiten mit Buchdetails) mit KI-Vorschlägen für Felder und direktem Export nach Google Sheets, Notion oder Airtable auslesen kann.

Warum dein Goodreads-Python-Scraper leere Ergebnisse liefert — und wie du das behebst

Dies ist der Abschnitt, den ich mir selbst gewünscht hätte, als ich mit Goodreads-Daten angefangen habe. Das Problem mit den „leeren Ergebnissen“ ist die häufigste Beschwerde in Entwicklerforen, und es hat mehrere unterschiedliche Ursachen — jede mit einer eigenen Lösung.

SymptomUrsacheLösung
Rezensionen/Bewertungen liefern []JS-gerenderter Inhalt (React/Lazy Load)Statt requests Selenium oder Playwright verwenden
Es wird immer nur Seite 1 gescraptPagination-Parameter werden ignoriert oder per JS gesteuert?page=N korrekt setzen; bei Infinite Scroll Browser-Automation nutzen
Code funktionierte letztes Jahr, jetzt nicht mehrGoodreads hat HTML-Klassennamen geändertRobuste Selektoren nutzen (JSON-LD, data-testid-Attribute)
403/Blockierung nach wenigen AnfragenFehlende Header / zu schnelle RequestsUser-Agent ergänzen, time.sleep(), Proxies rotieren
Login-Schranke auf Shelf-/Listen-SeitenCookie-/Session-Login erforderlichrequests.Session() mit Cookies oder Browser-Scraping verwenden

JS-gerenderte Inhalte: Rezensionen und Bewertungen erscheinen leer

Goodreads nutzt ein React-basiertes Frontend. Wenn du mit requests.get() eine Buchseite abrufst, bekommst du nur das initiale HTML — Rezensionen, Bewertungsverteilungen und viele „Mehr Infos“-Bereiche werden aber erst später per JavaScript geladen. Dein Scraper kann sie also schlicht nicht sehen.

Die Lösung: Für Seiten mit JS-gerenderten Inhalten solltest du auf Selenium oder Playwright wechseln. Meine Empfehlung für neue Projekte ist Playwright — es ist dank seines WebSocket-basierten Protokolls und bietet bessere eingebaute Stealth- und Async-Funktionen.

troubleshooting-empty-array-causes.webp

Pagination, die nur Seite 1 zurückgibt

Dieser Fehler ist tückisch. Du schreibst eine Schleife, erhöhst ?page=N und bekommst trotzdem immer dieselben Ergebnisse. Auf Goodreads geben Shelf-Seiten ohne Authentifizierung stillschweigend den Inhalt von Seite 1 zurück, selbst wenn ?page= geändert wird. Kein Fehler, kein Redirect — einfach immer wieder dieselbe erste Seite.

Die Lösung: Füge ein authentifiziertes Session-Cookie hinzu, insbesondere _session_id2. Mehr dazu weiter unten im Abschnitt zur Pagination.

Code, der letztes Jahr funktioniert hat, schlägt jetzt fehl

Goodreads ändert regelmäßig HTML-Klassennamen und Seitenstrukturen. Das bekannte GitHub-Repo maria-antoniak/goodreads-scraper trägt inzwischen dauerhaft den Hinweis: „This project is unmaintained and no longer functioning.“ Die Lösung besteht darin, stabilere Selektoren zu verwenden — JSON-LD-Strukturdaten (schema.org-konform und selten geändert) oder data-testid-Attribute statt fragiler Klassennamen.

403-Fehler oder Blockierungen

Die requests-Bibliothek von Python hat einen anderen TLS-Fingerprint als Chrome. Selbst mit einem Chrome-User-Agent können Bot-Detection-Systeme wie AWS WAF (das Goodreads nutzt, da es zu Amazon gehört) die Abweichung erkennen. Die Lösung: realistische Browser-Header setzen, zwischen den Requests time.sleep()-Pausen von 3–8 Sekunden einbauen und bei größeren Projekten curl_cffi für einen passenden TLS-Fingerprint in Betracht ziehen.

Login-Schranken bei Regal- und Listen-Seiten

Einige Goodreads-Regal- und Listen-Seiten erfordern eine Anmeldung, um vollständige Inhalte zu sehen — besonders ab Seite 5 oder später. Verwende requests.Session() mit Cookies, die du aus deinem Browser exportierst, oder nutze Selenium/Playwright mit einem eingeloggten Profil. Thunderbit erledigt das ganz natürlich, da es in deinem eigenen eingeloggten Chrome-Browser arbeitet.

Bevor du loslegst

  • Schwierigkeitsgrad: Mittelstufe (Grundkenntnisse in Python vorausgesetzt)
  • Zeitaufwand: ca. 20–30 Minuten für den vollständigen Durchlauf
  • Du brauchst:
    • Python 3.8+
    • Chrome-Browser (für DevTools-Inspektion sowie Selenium/Playwright)
    • Bibliotheken: requests, beautifulsoup4, selenium oder playwright, pandas
    • (Optional) gspread für den Export nach Google Sheets
    • (Optional) als No-Code-Alternative

goodreads-scraping-flow.webp

Schritt 1: Python-Umgebung einrichten

Installiere die benötigten Bibliotheken. Öffne dein Terminal und führe aus:

1pip install requests beautifulsoup4 selenium pandas lxml

Wenn du lieber Playwright nutzen willst (für neue Projekte empfohlen):

1pip install playwright
2playwright install chromium

Für den Export nach Google Sheets (optional):

1pip install gspread oauth2client

Stelle sicher, dass du Python 3.8 oder höher verwendest. Das kannst du mit python --version prüfen.

Nach der Installation solltest du alle Bibliotheken ohne Fehler importieren können. Teste das mit python -c "import requests, bs4, pandas; print('Ready')".

Schritt 2: Erste Anfrage mit passenden Headern senden

Öffne in deinem Browser eine Goodreads-Genre- oder Listen-Seite — zum Beispiel https://www.goodreads.com/list/show/1.Best_Books_Ever. Jetzt rufen wir die Seite mit Python ab.

1import requests
2from bs4 import BeautifulSoup
3headers = {
4    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
5                  "(KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
6    "Accept-Language": "en-US,en;q=0.9",
7    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
8}
9url = "https://www.goodreads.com/list/show/1.Best_Books_Ever"
10response = requests.get(url, headers=headers, timeout=15)
11print(f"Status: {response.status_code}")

Du solltest Status: 200 sehen. Wenn 403 erscheint, prüfe deine Header noch einmal — Goodreads' AWS WAF achtet auf einen realistischen User-Agent und blockiert sehr einfache Requests. Die obigen Header simulieren eine echte Chrome-Browsersitzung.

Schritt 3: Seite untersuchen und die richtigen Selektoren finden

Öffne in Chrome DevTools (F12) die Goodreads-Listenseite. Klicke mit der rechten Maustaste auf einen Buchtitel und wähle „Untersuchen“. Dort siehst du die DOM-Struktur jedes Bucheintrags.

Bei Listen-Seiten ist jedes Buch typischerweise in einem <tr>-Element mit itemtype="http://schema.org/Book" eingebettet. Darin findest du:

  • Titel: a.bookTitle (der Linktext ist der Titel, href enthält die Buch-URL)
  • Autor: a.authorName
  • Bewertung: span.minirating (enthält den Durchschnittswert und die Anzahl der Bewertungen)
  • Cover-Bild: img innerhalb der Buchzeile

Bei einzelnen Buchdetailseiten solltest du die CSS-Selektoren überspringen und direkt auf JSON-LD zugreifen. Goodreads bindet strukturierte Daten in ein <script type="application/ld+json">-Tag ein, das dem schema.org-Book-Format folgt. Das ist deutlich stabiler als Klassennamen, die Goodreads jederzeit ändern kann.

Schritt 4: Buchdaten von einer einzelnen Listenseite extrahieren

Parsen wir die Listenseite und holen uns die Basisdaten für jedes Buch:

1import requests
2from bs4 import BeautifulSoup
3headers = {
4    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
5                  "(KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
6    "Accept-Language": "en-US,en;q=0.9",
7}
8url = "https://www.goodreads.com/list/show/1.Best_Books_Ever"
9response = requests.get(url, headers=headers, timeout=15)
10soup = BeautifulSoup(response.text, "lxml")
11books = []
12rows = soup.select('tr[itemtype="http://schema.org/Book"]')
13for row in rows:
14    title_tag = row.select_one("a.bookTitle")
15    author_tag = row.select_one("a.authorName")
16    rating_tag = row.select_one("span.minirating")
17    title = title_tag.get_text(strip=True) if title_tag else ""
18    book_url = "https://www.goodreads.com" + title_tag["href"] if title_tag else ""
19    author = author_tag.get_text(strip=True) if author_tag else ""
20    rating_text = rating_tag.get_text(strip=True) if rating_tag else ""
21    books.append({
22        "title": title,
23        "author": author,
24        "rating_info": rating_text,
25        "book_url": book_url,
26    })
27print(f"Found {len(books)} books on page 1")
28for b in books[:3]:
29    print(b)

Du solltest ungefähr 100 Bücher pro Listenseite sehen. Jeder Eintrag enthält Titel, Autor, einen Bewertungstext wie „4.28 avg rating — 9,031,257 ratings“ und eine URL zur Detailseite des Buchs.

Schritt 5: Unterseiten für detaillierte Buchinfos scrapen

Die Listen-Seite liefert die Basics, aber die wirklich wertvollen Informationen — ISBN, vollständige Beschreibung, Genre-Tags, Seitenzahl, Veröffentlichungsdatum — liegen auf den einzelnen Buchseiten. Genau hier spielt JSON-LD seine Stärke aus.

1import json
2import time
3def scrape_book_detail(book_url, headers):
4    """Ruft eine einzelne Buchseite auf und extrahiert Metadaten über JSON-LD."""
5    resp = requests.get(book_url, headers=headers, timeout=15)
6    if resp.status_code != 200:
7        return {}
8    soup = BeautifulSoup(resp.text, "lxml")
9    script = soup.find("script", {"type": "application/ld+json"})
10    if not script:
11        return {}
12    data = json.loads(script.string)
13    agg = data.get("aggregateRating", {})
14    # Genre-Tags sind nicht in JSON-LD enthalten; daher Fallback über HTML
15    genres = [g.get_text(strip=True) for g in soup.select('span.BookPageMetadataSection__genreButton a span')]
16    return {
17        "isbn": data.get("isbn", ""),
18        "pages": data.get("numberOfPages", ""),
19        "language": data.get("inLanguage", ""),
20        "format": data.get("bookFormat", ""),
21        "avg_rating": agg.get("ratingValue", ""),
22        "rating_count": agg.get("ratingCount", ""),
23        "review_count": agg.get("reviewCount", ""),
24        "description": data.get("description", "")[:200],  # für Vorschau gekürzt
25        "genres": ", ".join(genres[:5]),
26    }
27# Beispiel: die ersten 3 Bücher anreichern
28for book in books[:3]:
29    details = scrape_book_detail(book["book_url"], headers)
30    book.update(details)
31    print(f"Scraped: {book['title']} — ISBN: {book.get('isbn', 'N/A')}")
32    time.sleep(4)  # Rate Limits respektieren

Baue zwischen den Requests eine time.sleep()-Pause von 3–8 Sekunden ein. Goodreads beginnt bei etwa 20–30 Requests pro Minute von einer einzelnen IP mit Rate-Limits, und du wirst schnell 403s oder CAPTCHAs sehen, wenn du schneller bist.

Dieser Zwei-Pass-Ansatz — zuerst alle Buch-URLs aus den Listen-Seiten sammeln, dann jede Detailseite besuchen — ist zuverlässiger und leichter fortzusetzen, falls der Prozess unterbrochen wird. Genau so arbeiten die meisten erfolgreichen Goodreads-Scraper.

Kurzer Hinweis: kann das automatisch mit Subpage-Scraping erledigen. Die KI besucht jede Buch-Detailseite und reichert deine Tabelle mit ISBN, Beschreibung, Genres und mehr an — ohne Code, ohne Schleifen, ohne Sleep-Timer.

Schritt 6: JavaScript-gerenderte Inhalte mit Selenium behandeln

Für Seiten, deren Inhalte per JavaScript geladen werden — Rezensionen, Bewertungsaufschlüsselungen, Bereiche mit „mehr Details“ — brauchst du ein Browser-Automation-Tool. Hier ein Beispiel mit Selenium:

1from selenium import webdriver
2from selenium.webdriver.chrome.options import Options
3from selenium.webdriver.common.by import By
4from selenium.webdriver.support.ui import WebDriverWait
5from selenium.webdriver.support import expected_conditions as EC
6options = Options()
7options.add_argument("--headless=new")
8options.add_argument("--disable-blink-features=AutomationControlled")
9options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
10                     "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36")
11driver = webdriver.Chrome(options=options)
12driver.get("https://www.goodreads.com/book/show/5907.The_Hobbit")
13# Warten, bis die Rezensionen geladen sind
14try:
15    WebDriverWait(driver, 10).until(
16        EC.presence_of_element_located((By.CSS_SELECTOR, "article.ReviewCard"))
17    )
18except:
19    print("Reviews wurden nicht geladen — die Seite benötigt möglicherweise Login oder das Laden per JS hat zu lange gedauert")
20# Jetzt die vollständig gerenderte Seite parsen
21page_source = driver.page_source
22soup = BeautifulSoup(page_source, "lxml")
23reviews = soup.select("article.ReviewCard")
24for rev in reviews[:3]:
25    text = rev.select_one("span.Formatted")
26    stars = rev.select_one("span.RatingStars")
27    print(f"Bewertung: {stars.get_text(strip=True) if stars else 'N/A'}")
28    print(f"Rezension: {text.get_text(strip=True)[:150] if text else 'N/A'}...")
29    print()
30driver.quit()

Wann Selenium und wann requests?

  • Verwende requests + BeautifulSoup für Buch-Metadaten (JSON-LD), Listen-Seiten, Regal-Seiten (Seite 1) und Choice-Awards-Daten
  • Verwende Selenium oder Playwright für Rezensionen, Bewertungsverteilungen und alle Inhalte, die im Roh-HTML nicht auftauchen

Für neue Projekte ist Playwright meist die bessere Wahl — schneller, weniger Speicherverbrauch, bessere Standard-Stealth-Optionen. Selenium hat jedoch die größere Community und mehr vorhandene Goodreads-Codebeispiele.

Pagination, die wirklich funktioniert: komplette Goodreads-Listen scrapen

Pagination ist der häufigste Fehlerpunkt bei Goodreads-Scrapern, und ich habe noch kein einziges konkurrierendes Tutorial gesehen, das das richtig erklärt. So klappt es zuverlässig.

Wie Goodreads-Pagination-URLs funktionieren

Goodreads nutzt für die meisten paginierten Seiten einen einfachen ?page=N-Parameter:

  • Listen: https://www.goodreads.com/list/show/1.Best_Books_Ever?page=2
  • Regale: https://www.goodreads.com/shelf/show/thriller?page=2
  • Suche: https://www.goodreads.com/search?q=fantasy&page=2

Jede Listenseite zeigt typischerweise 100 Bücher. Regale zeigen 50 pro Seite.

Eine Pagination-Schleife schreiben, die weiß, wann sie stoppen muss

1import time
2all_books = []
3base_url = "https://www.goodreads.com/list/show/1.Best_Books_Ever"
4for page_num in range(1, 50):  # Sicherheitsgrenze bei 50 Seiten
5    url = f"{base_url}?page={page_num}"
6    resp = requests.get(url, headers=headers, timeout=15)
7    if resp.status_code != 200:
8        print(f"Seite {page_num}: Status {resp.status_code} erhalten, stoppe.")
9        break
10    soup = BeautifulSoup(resp.text, "lxml")
11    rows = soup.select('tr[itemtype="http://schema.org/Book"]')
12    if not rows:
13        print(f"Seite {page_num}: keine Bücher gefunden, Ende erreicht.")
14        break
15    for row in rows:
16        title_tag = row.select_one("a.bookTitle")
17        author_tag = row.select_one("a.authorName")
18        title = title_tag.get_text(strip=True) if title_tag else ""
19        book_url = "https://www.goodreads.com" + title_tag["href"] if title_tag else ""
20        author = author_tag.get_text(strip=True) if author_tag else ""
21        all_books.append({"title": title, "author": author, "book_url": book_url})
22    print(f"Seite {page_num}: {len(rows)} Bücher gescrapt (insgesamt: {len(all_books)})")
23    time.sleep(5)  # 5 Sekunden Pause zwischen Seiten
24print(f"\nFertig. Insgesamt gesammelte Bücher: {len(all_books)}")

Die letzte Seite erkennst du daran, dass keine Ergebnisse mehr vorhanden sind (keine tr[itemtype="http://schema.org/Book"]-Elemente) oder dass der „Next“-Link (a.next_page) fehlt.

Sonderfall: Login erforderlich ab Seite 5

Hier liegt die Falle, in die fast alle tappen: Manche Goodreads-Regal- und Listen-Seiten geben ohne Authentifizierung ab Seite 6 stillschweigend wieder den Inhalt von Seite 1 zurück. Kein Fehler, kein Redirect — nur dieselben Daten wieder und wieder.

Die Lösung: Exportiere das _session_id2-Cookie aus deinem Browser (zum Beispiel mit einer Cookie-Export-Erweiterung oder über Chrome DevTools > Application > Cookies) und füge es deinen Requests hinzu:

1session = requests.Session()
2session.headers.update(headers)
3session.cookies.set("_session_id2", "DEIN_SESSION_COOKIE_WERT_HIER", domain=".goodreads.com")
4# Danach session.get() statt requests.get() verwenden
5resp = session.get(f"{base_url}?page=6", timeout=15)

Thunderbit unterstützt klickbasierte und Infinite-Scroll-Pagination nativ — ganz ohne Code und ohne Cookie-Management. Wenn deine Pagination immer wieder kaputtgeht, lohnt sich ein Blick darauf.

Das vollständige Python-Skript zum Kopieren und Einfügen

Hier ist das komplette, zusammenhängende Skript. Es verarbeitet Header, Pagination, Unterseiten-Scraping per JSON-LD, Rate-Limiting und CSV-Export. Ich habe es gegen live Goodreads-Seiten getestet, Stand Mitte 2025.

1"""
2goodreads_scraper.py — Scraped eine Goodreads-Liste mit Pagination und Anreicherung der Buchdetails.
3Verwendung: python goodreads_scraper.py
4Ausgabe: goodreads_books.csv
5"""
6import csv, json, time, requests
7from bs4 import BeautifulSoup
8HEADERS = {
9    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
10                  "(KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
11    "Accept-Language": "en-US,en;q=0.9",
12    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
13}
14BASE_URL = "https://www.goodreads.com/list/show/1.Best_Books_Ever"
15MAX_PAGES = 3          # nach Bedarf anpassen
16DELAY_LISTING = 5      # Sekunden zwischen Listen-Seiten
17DELAY_DETAIL = 4       # Sekunden zwischen Detailseiten
18OUTPUT_FILE = "goodreads_books.csv"
19def scrape_listing_page(url):
20    """Gibt eine Liste von Dicts mit Titel, Autor und book_url aus einer Listen-Seite zurück."""
21    resp = requests.get(url, headers=HEADERS, timeout=15)
22    if resp.status_code != 200:
23        return []
24    soup = BeautifulSoup(resp.text, "lxml")
25    rows = soup.select('tr[itemtype="http://schema.org/Book"]')
26    books = []
27    for row in rows:
28        t = row.select_one("a.bookTitle")
29        a = row.select_one("a.authorName")
30        if t:
31            books.append({
32                "title": t.get_text(strip=True),
33                "author": a.get_text(strip=True) if a else "",
34                "book_url": "https://www.goodreads.com" + t["href"],
35            })
36    return books
37def scrape_book_detail(book_url):
38    """Ruft eine Buchseite ab und extrahiert Metadaten via JSON-LD plus HTML-Fallback."""
39    resp = requests.get(book_url, headers=HEADERS, timeout=15)
40    if resp.status_code != 200:
41        return {}
42    soup = BeautifulSoup(resp.text, "lxml")
43    script = soup.find("script", {"type": "application/ld+json"})
44    if not script:
45        return {}
46    data = json.loads(script.string)
47    agg = data.get("aggregateRating", {})
48    genres = [g.get_text(strip=True)
49              for g in soup.select("span.BookPageMetadataSection__genreButton a span")]
50    return {
51        "isbn": data.get("isbn", ""),
52        "pages": data.get("numberOfPages", ""),
53        "avg_rating": agg.get("ratingValue", ""),
54        "rating_count": agg.get("ratingCount", ""),
55        "review_count": agg.get("reviewCount", ""),
56        "description": (data.get("description", "") or "")[:300],
57        "genres": ", ".join(genres[:5]),
58        "language": data.get("inLanguage", ""),
59        "format": data.get("bookFormat", ""),
60        "published": data.get("datePublished", ""),
61    }
62def main():
63    all_books = []
64    # --- Durchlauf 1: Buch-URLs aus den Listen-Seiten sammeln ---
65    for page in range(1, MAX_PAGES + 1):
66        url = f"{BASE_URL}?page={page}"
67        page_books = scrape_listing_page(url)
68        if not page_books:
69            print(f"Seite {page}: leer — stoppe Pagination.")
70            break
71        all_books.extend(page_books)
72        print(f"Seite {page}: {len(page_books)} Bücher (insgesamt: {len(all_books)})")
73        time.sleep(DELAY_LISTING)
74    # --- Durchlauf 2: jedes Buch mit Detaildaten anreichern ---
75    for i, book in enumerate(all_books):
76        details = scrape_book_detail(book["book_url"])
77        book.update(details)
78        print(f"[{i+1}/{len(all_books)}] {book['title']} — ISBN: {book.get('isbn', 'N/A')}")
79        time.sleep(DELAY_DETAIL)
80    # --- Export nach CSV ---
81    if all_books:
82        fieldnames = list(all_books[0].keys())
83        with open(OUTPUT_FILE, "w", newline="", encoding="utf-8") as f:
84            writer = csv.DictWriter(f, fieldnames=fieldnames)
85            writer.writeheader()
86            writer.writerows(all_books)
87        print(f"\n{len(all_books)} Bücher in {OUTPUT_FILE} gespeichert")
88    else:
89        print("Keine Bücher gescrapt.")
90if __name__ == "__main__":
91    main()

Mit MAX_PAGES = 3 sammelt dieses Skript ungefähr 300 Bücher aus der Liste „Best Books Ever“, besucht jede Detailseite und schreibt alles in eine CSV-Datei. Auf meinem Rechner dauert das rund 25 Minuten (hauptsächlich wegen der 4-Sekunden-Pausen zwischen den Detailseiten). Deine Ausgabe-CSV enthält Spalten wie Titel, Autor, book_url, isbn, pages, avg_rating, rating_count, review_count, description, genres, language, format und published.

Export über CSV hinaus: Google Sheets mit gspread

Wenn du die Daten lieber in Google Sheets statt zusätzlich zu einer CSV haben möchtest, füge nach dem CSV-Export Folgendes ein:

1import gspread
2from oauth2client.service_account import ServiceAccountCredentials
3scope = ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/drive"]
4creds = ServiceAccountCredentials.from_json_keyfile_name("credentials.json", scope)
5client = gspread.authorize(creds)
6sheet = client.open("Goodreads Scrape").sheet1
7header = list(all_books[0].keys())
8sheet.append_row(header)
9for book in all_books:
10    sheet.append_row([str(book.get(k, "")) for k in header])
11print("Daten an Google Sheets übertragen.")

Du brauchst dafür ein Google-Cloud-Service-Account mit aktivierten Sheets- und Drive-APIs. Die erklärt die Einrichtung in etwa 5 Minuten. Verwende Batch-Operationen (append_rows() mit einer Liste von Listen), wenn du mehr als ein paar hundert Zeilen überträgst — das Google-Limit liegt bei 300 Requests pro 60 Sekunden pro Projekt.

Wenn dir das alles zu viel Setup ist, exportiert Thunderbit mit direkt nach Google Sheets, Airtable, Notion, Excel, CSV und JSON — ganz ohne Bibliotheksinstallation, Credentials-Datei oder API-Quoten.

Die No-Code-Alternative: Goodreads mit Thunderbit scrapen

Nicht jeder möchte ein Python-Skript pflegen. Vielleicht bist du ein Verlag, der eine einmalige Marktanalyse macht, oder ein Buchblogger, der einfach eine Tabelle der Bestseller dieses Jahres braucht. Genau für solche Fälle haben wir Thunderbit gebaut.

So scrapest du Goodreads mit Thunderbit

  1. Installiere die Thunderbit Chrome-Erweiterung aus dem und öffne eine Goodreads-Liste, ein Regal oder eine Suchergebnisseite.
  2. Klicke in der Thunderbit-Seitenleiste auf „AI Suggest Fields“. Die KI liest die Seite und schlägt Spalten vor — typischerweise Titel, Autor, Bewertungswert, Cover-URL und Buchlink.
  3. Klicke auf „Scrape“ — die Daten werden in wenigen Sekunden in eine strukturierte Tabelle übernommen.
  4. Exportiere nach Google Sheets, Excel, Airtable, Notion, CSV oder JSON.

Für detaillierte Buchdaten wie ISBN, Beschreibung, Genres und Seitenzahl besucht Thunderbits Subpage-Scraping-Funktion automatisch jede Buch-Detailseite und ergänzt die Tabelle — ohne Schleifen, ohne Sleep-Timer, ohne Debugging.

Thunderbit beherrscht auch paginierte Listen nativ. Du gibst an, dass auf „Next“ geklickt oder gescrollt werden soll, und das Tool sammelt Daten über alle Seiten hinweg — ohne Code.

Der Unterschied ist einfach: Das Python-Skript gibt dir maximale Kontrolle und ist kostenlos (abgesehen von deiner Zeit), während Thunderbit etwas Flexibilität gegen enorme Zeitersparnis und keinerlei Wartungsaufwand tauscht. Bei einer Liste mit 300 Büchern braucht das Python-Skript etwa 25 Minuten Laufzeit plus die Zeit zum Schreiben und Debuggen. Thunderbit liefert dieselben Daten in ungefähr 3 Minuten — mit zwei Klicks.

Goodreads verantwortungsvoll scrapen: robots.txt, Nutzungsbedingungen und Ethik

Hier verdient das Thema eine klare Antwort und keinen reinen Standard-Hinweis.

Was Goodreads’ robots.txt tatsächlich sagt

Die aktuelle robots.txt von Goodreads ist erstaunlich konkret. Buchdetailseiten (/book/show/), öffentliche Listen (/list/show/), öffentliche Regale (/shelf/show/) und Autorenseiten (/author/show/) sind nicht gesperrt. Gesperrt sind dagegen: /api, /book/reviews/, /review/list, /review/show, /search und mehrere weitere Pfade. GPTBot und CCBot (Common Crawl) werden vollständig mit Disallow: / blockiert. Für bingbot gibt es Crawl-delay: 5, aber kein globales Delay.

Goodreads’ Nutzungsbedingungen in einfachen Worten

Die Nutzungsbedingungen (zuletzt überarbeitet am 28. April 2021) verbieten „jede Form von Data Mining, Robots oder ähnlichen Tools zur Datenerfassung und -extraktion“. Das ist sehr weit formuliert und sollte ernst genommen werden — zugleich haben Gerichte wiederholt entschieden, dass ein Verstoß gegen die Nutzungsbedingungen allein nicht automatisch eine strafbare „unbefugte Zugriffs“-Handlung darstellt. Das Urteil in hielt fest, dass „die Kriminalisierung von Verstößen gegen Nutzungsbedingungen das Risiko birgt, dass jede Website zu einer eigenen Strafgerichtsbarkeit wird“.

Best Practices

  • Zwischen Anfragen pausieren: 3–8 Sekunden Abstand (Goodreads’ eigene robots.txt legt für Bots 5 Sekunden nahe)
  • Unter 5.000 Requests pro Tag pro einzelner IP bleiben
  • Nur öffentlich zugängliche Seiten scrapen — keine Massenabfragen von Inhalten, die nur nach Login sichtbar sind
  • Keine Rohtexte von Rezensionen kommerziell weiterverbreiten — Rezensionen sind urheberrechtlich geschützte Texte
  • Nur speichern, was du wirklich brauchst, und eine Aufbewahrungsfrist festlegen
  • Privatforschung vs. kommerzielle Nutzung: Das Scrapen öffentlicher Daten für private Analysen oder akademische Forschung ist in der Regel unproblematischer. Das größte rechtliche Risiko entsteht bei kommerzieller Weiterverbreitung.

Ein Tool wie Thunderbit, das über deine eigene Browser-Session arbeitet, sieht visuell aus wie normales Browsen — trotzdem gelten dieselben ethischen Grundsätze unabhängig vom Tool. Wenn du tiefer in die einsteigen willst, haben wir das separat aufbereitet.

Tipps und häufige Fehler

Tipp: Immer zuerst JSON-LD prüfen. Bevor du komplexe CSS-Selektoren schreibst, schau nach, ob die benötigten Daten im <script type="application/ld+json">-Tag stehen. Das ist stabiler, leichter zu parsen und bricht bei Frontend-Änderungen seltener.

Tipp: Nutze die Zwei-Pass-Strategie. Erst alle Buch-URLs von den Listen-Seiten sammeln, dann jede Detailseite besuchen. So kannst du den Scraper leichter fortsetzen, falls er mittendrin abstürzt, und die URL-Liste als Zwischenstand auf der Festplatte speichern.

Fehler: Fehlende Felder nicht abfangen. Nicht jede Buchseite hat ISBN, Genre-Tags oder eine Beschreibung. Verwende immer .get() mit Standardwert oder prüfe Selektoren mit if. Ein einzelner NoneType-Fehler kann einen dreistündigen Scrape-Lauf beenden.

Fehler: Zu schnell laufen. Klar, time.sleep(0.5) wirkt verlockend. Aber Goodreads beginnt nach etwa 20–30 schnellen Requests mit 403-Antworten, und wenn du einmal markiert bist, musst du vielleicht stundenlang warten oder die IP wechseln. 4–5 Sekunden Pause sind meist der Sweet Spot.

Fehler: Alten Tutorials blind vertrauen. Wenn eine Anleitung noch die Goodreads API erwähnt oder Klassennamen wie .field.value oder #bookTitle verwendet, ist sie wahrscheinlich veraltet. Prüfe Selektoren immer direkt an der Live-Seite, bevor du den Scraper baust.

Mehr dazu, wie du die richtigen Tools und Frameworks auswählst, findest du in unseren Leitfäden zu den und .

Fazit und wichtigste Erkenntnisse

Goodreads mit Python zu scrapen ist absolut machbar — du musst nur wissen, wo die Stolperfallen liegen. Kurz zusammengefasst:

  • Die Goodreads API gibt es nicht mehr (seit Dezember 2020). Scraping ist der wichtigste Weg, um strukturierte Buchdaten von der Plattform zu bekommen.
  • Leere Ergebnisse entstehen fast immer durch JS-gerenderte Inhalte, veraltete Selektoren, fehlende Header oder Authentifizierungsprobleme bei der Pagination — nicht, weil dein Code grundsätzlich falsch ist.
  • JSON-LD ist dein bester Freund für Buchmetadaten. Es ist stabil, strukturiert und ändert sich selten.
  • Pagination erfordert bei vielen Regal- und Listen-Seiten ab Seite 5 Authentifizierung. Füge das _session_id2-Cookie hinzu.
  • Rate-Limiting ist real. Nutze Pausen von 3–8 Sekunden und bleib unter 5.000 Requests pro Tag.
  • Die Zwei-Pass-Strategie (erst URLs sammeln, dann Detailseiten scrapen) ist robuster und besser fortsetzbar.
  • Für Nicht-Programmierer (oder alle, die ihren Nachmittag lieber behalten möchten) erledigt all das — JS-Rendering, Pagination, Anreicherung von Unterseiten und Export — mit ungefähr zwei Klicks.

Scrape verantwortungsvoll, respektiere die robots.txt und möge deine Buchdaten immer mehr zurückgeben als [].

FAQs

Kann man die Goodreads API noch nutzen?

Nein. Goodreads hat seine öffentliche API im Dezember 2020 eingestellt und vergibt keine neuen Developer-Keys mehr. Bereits vorhandene Keys, die 30 Tage lang inaktiv waren, wurden automatisch deaktiviert. Für den programmgesteuerten Zugriff auf Buchdaten bleiben heute Web Scraping oder alternative APIs wie Open Library und Google Books.

Warum liefert mein Goodreads-Scraper leere Ergebnisse?

Die häufigste Ursache sind JavaScript-gerenderte Inhalte. Goodreads lädt Rezensionen, Bewertungsverteilungen und viele Detailbereiche über React/JavaScript, die ein einfaches requests.get() nicht sieht. Für solche Seiten solltest du Selenium oder Playwright einsetzen. Weitere Ursachen sind veraltete CSS-Selektoren (Goodreads hat das HTML geändert), fehlende User-Agent-Header (führt zu 403-Blockierungen) oder unauthentifizierte Requests bei paginierten Shelf-Seiten.

Das Scrapen öffentlich verfügbarer Daten für private oder Forschungszwecke ist nach aktuellen Gerichtsentscheidungen grundsätzlich eher akzeptiert (hiQ v. LinkedIn, Meta v. Bright Data). Allerdings verbieten die Goodreads-Nutzungsbedingungen automatisierte Datenerfassung, und du solltest immer auch die robots.txt prüfen. Vermeide die kommerzielle Weiterverbreitung urheberrechtlich geschützter Rezensionstexte und halte dein Anfragevolumen gering, damit die Website nicht unnötig belastet wird.

Wie scrape ich mehrere Seiten auf Goodreads?

Hänge ?page=N an die Shelf- oder Listen-URL und iteriere über die Seitennummern. Prüfe auf leere Ergebnisse oder das Fehlen eines „Next“-Links, um die letzte Seite zu erkennen. Wichtig: Manche Shelf-Seiten benötigen Authentifizierung (das _session_id2-Cookie), damit Seiten über 5 hinaus Ergebnisse liefern — ohne dieses Cookie bekommst du sonst stillschweigend immer wieder Seite 1.

Kann ich Goodreads scrapen, ohne Code zu schreiben?

Ja. ist eine Chrome-Erweiterung, mit der du Goodreads in zwei Klicks scrapen kannst — die KI schlägt Felder vor, du klickst auf „Scrape“ und exportierst direkt nach Google Sheets, Excel, Airtable oder Notion. Das Tool verarbeitet JS-gerenderte Inhalte, Pagination und Subpage-Anreicherung automatisch, ganz ohne Python oder Programmieraufwand.

Mehr erfahren

Shuai Guan
Shuai Guan
Co-founder/CEO @ Thunderbit. Passionate about cross section of AI and Automation. He's a big advocate of automation and loves making it more accessible to everyone. Beyond tech, he channels his creativity through a passion for photography, capturing stories one picture at a time.
Inhaltsverzeichnis

Teste Thunderbit

Leads und andere Daten in nur 2 Klicks scrapen. Unterstützt durch KI.

Thunderbit holen Es ist kostenlos
Daten mit KI extrahieren
Daten einfach zu Google Sheets, Airtable oder Notion übertragen
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week