Du tippst 30 Zeilen Python, startest deinen Goodreads-Scraper — und zurück kommt statt Daten ein nacktes []. Eine leere Liste. Sonst nichts. Nur du und der blinkende Cursor. Viel frustrierender wird Scraping selten.
Ich habe diese Szene dutzendfach erlebt: in unseren internen Tests bei Thunderbit, in Entwicklerforen und in den GitHub-Issues längst aufgegebener Scraper-Repos. Der Wortlaut der Klagen wiederholt sich: „Der Top-Reviews-Bereich bleibt leer, nur [] kommt zurück“, „egal welche Seitenzahl ich übergebe, gescrapt wird immer nur Seite eins“, „letztes Jahr lief mein Code noch, jetzt ist er tot.“ Und obendrauf kommt: Die Goodreads API wurde im Dezember 2020 abgeschaltet. Der gut gemeinte Tutorial-Klassiker „nimm doch einfach die API“ läuft heute also ins Leere.
Wer heute strukturierte Buchdaten von Goodreads will — Titel, Autoren, Bewertungen, Rezensionen, Genres, ISBNs —, kommt am Scraping praktisch nicht vorbei. Der folgende Leitfaden zeigt einen kompletten, funktionierenden Weg, Goodreads mit Python zu scrapen: JS-gerenderte Inhalte, Pagination, Anti-Blocking und Export inklusive. Und falls Python nicht deine Welt ist, zeige ich dir am Ende eine No-Code-Alternative, die dieselbe Aufgabe in rund zwei Klicks erledigt.
Was bedeutet Goodreads Scraping — und warum mit Python?
Goodreads Scraping heißt: Buchdaten — Titel, Autoren, Bewertungen, Anzahl der Rezensionen, Genres, ISBNs, Seitenzahl, Veröffentlichungsdatum und so weiter — automatisch von Goodreads-Seiten herausziehen, statt sie mühsam von Hand zu kopieren.
Goodreads zählt zu den größten Buchdatenbanken der Welt: über 150 Millionen registrierte Mitglieder und rund 90 Millionen nutzergenerierte Rezensionen. Monat für Monat wandern mehr als 18 Millionen Bücher auf „Want to Read“-Listen. Genau diese permanent aktualisierten, strukturierten Daten machen die Plattform für Verlage, Data Scientists, Buchhändler und Forschende so wertvoll.
Python ist dafür die Standardwahl — rund 69,6 % aller Scraping-Projekte laufen über Python. Die Bibliotheken sind ausgereift (requests, BeautifulSoup, Selenium, Playwright, pandas), die Syntax ist einsteigerfreundlich, die Community gewaltig.
Wer noch nie eine Website gescrapt hat, steigt mit Python am leichtesten ein.
Warum Goodreads mit Python scrapen? Praxisnahe Anwendungsfälle
Bevor wir in den Code springen, lohnt eine kurze Vorfrage: Wer braucht diese Daten überhaupt — und wofür?
| Anwendungsfall | Wer profitiert | Was du scrapest |
|---|---|---|
| Marktanalyse für Verlage | Verlage, Literaturagenten | Trend-Genres, bestbewertete Titel, neue Autoren, Bewertungen von Wettbewerbern |
| Buchempfehlungssysteme | Data Scientists, Hobbyisten, App-Entwickler | Bewertungen, Genres, Nutzerregale, Tonalität von Rezensionen |
| Preis- und Bestandsüberwachung | Online-Buchhändler | Trendtitel, Volumen an Rezensionen, Anzahl der "Want to Read"-Einträge |
| Wissenschaftliche Forschung | Forschende, Studierende | Rezensionstexte, Verteilungen der Bewertungen, Genre-Klassifikationen |
| Lesestatistiken | Buchblogger, private Projekte | Eigene Regaldaten, Leseverlauf, Jahresrückblicke |
Ein paar handfeste Beispiele: Der UCSD Book Graph — einer der meistzitierten akademischen Datensätze in der Empfehlungsforschung — umfasst 2,36 Millionen eindeutige Bücher und über 228 Millionen Nutzer-Buch-Interaktionen, allesamt aus öffentlich zugänglichen Goodreads-Regalen. Mehrere Kaggle-Datensätze (goodbooks-10k, Best Books Ever usw.) sind aus Goodreads-Scraping hervorgegangen. Und eine Studie aus dem Jahr 2025 in Big Data and Society hat 331.211 bezahlte Buchrezensionen durchleuchtet, um den Einfluss gesponserter Reviews auf die Plattform zu untersuchen.
Auch wirtschaftlich hat das Gewicht: Bright Data verkauft vorab gescrapte Goodreads-Datensätze schon ab 0,50 USD pro 1.000 Datensätze — ein klarer Beleg, dass diese Daten einen realen Marktwert haben.
Die Goodreads API ist weg — das ist die aktuelle Alternative
Wer zuletzt nach „Goodreads API“ gegoogelt hat, ist vermutlich über veraltete Anleitungen gestolpert. Am 8. Dezember 2020 stellte Goodreads klammheimlich die Vergabe neuer Developer-API-Keys ein. Kein Blogpost, kein Newsletter — bloß eine knappe Notiz auf der Dokumentationsseite und reihenweise ratlose Entwickler.

Die Folgen kamen prompt. Ein Entwickler, Kyle K, hatte einen Discord-Bot für Buchempfehlungen gebaut — und „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 ausweichen. Und das Masterarbeitsprojekt der Studentin Elena Neacsu wurde mitten in der Entwicklung ausgebremst.
Was bleibt also? So sieht die aktuelle Landschaft aus:
| Ansatz | Verfügbare Daten | Benutzerfreundlichkeit | Rate-Limits | Status |
|---|---|---|---|---|
| Goodreads API | Vollständige Metadaten, Rezensionen | Früher einfach | 1 Anfrage/Sekunde | Eingestellt (Dez. 2020) — keine neuen Keys |
| Open Library API | Titel, Autoren, ISBNs, Cover (~30 Mio. Titel) | Einfach | 1–3 Anfragen/Sekunde | Aktiv, kostenlos, ohne Auth |
| Google Books API | Metadaten, Vorschauen | Einfach | 1.000/Tag kostenlos | Aktiv (Lücken bei nicht-englischen ISBNs) |
| Python-Scraping (requests + BS4) | Alles, was im initialen HTML steckt | Mittel | Selbst verwaltet | Funktioniert für statische Inhalte |
| Python-Scraping (Selenium/Playwright) | Auch JS-gerenderte Inhalte | Schwieriger | Selbst verwaltet | Erforderlich für Rezensionen und manche Listen |
| Thunderbit (No-Code Chrome-Erweiterung) | Alle sichtbaren Seitendaten | Sehr einfach (2 Klicks) | Credit-basiert | Aktiv — kein Python nötig |
Open Library ist eine starke Ergänzung, gerade für ISBN-Suchen und grundlegende Metadaten. Sobald es aber um Bewertungen, Rezensionen, Genre-Tags oder „Want to Read“-Zahlen geht, führt kein Weg daran vorbei, Goodreads direkt zu scrapen — per Python oder mit einem Tool wie Thunderbit, das Goodreads-Seiten (inklusive Unterseiten mit Buchdetails) über KI-Feldvorschläge ausliest und direkt nach Google Sheets, Notion oder Airtable exportiert.
Warum dein Goodreads-Python-Scraper leere Ergebnisse liefert — und wie du das behebst
Diesen Abschnitt hätte ich mir selbst gewünscht, als ich mit Goodreads-Daten anfing. Das „leere Ergebnisse“-Problem ist die mit Abstand häufigste Klage in Entwicklerforen. Es hat mehrere völlig verschiedene Ursachen, jede mit eigener Lösung.
| Symptom | Ursache | Lösung |
|---|---|---|
Rezensionen/Bewertungen liefern [] | JS-gerenderter Inhalt (React/Lazy Load) | Statt requests Selenium oder Playwright verwenden |
| Es wird immer nur Seite 1 gescrapt | Pagination-Parameter werden ignoriert oder per JS gesteuert | ?page=N korrekt setzen; bei Infinite Scroll Browser-Automation nutzen |
| Code funktionierte letztes Jahr, jetzt nicht mehr | Goodreads hat HTML-Klassennamen geändert | Robuste Selektoren nutzen (JSON-LD, data-testid-Attribute) |
| 403/Blockierung nach wenigen Anfragen | Fehlende Header / zu schnelle Requests | User-Agent ergänzen, time.sleep(), Proxies rotieren |
| Login-Schranke auf Shelf-/Listen-Seiten | Cookie-/Session-Login erforderlich | requests.Session() mit Cookies oder Browser-Scraping verwenden |
JS-gerenderte Inhalte: Rezensionen und Bewertungen erscheinen leer
Goodreads setzt auf ein React-basiertes Frontend. Holst du eine Buchseite mit requests.get(), bekommst du nur das initiale HTML — Rezensionen, Bewertungsverteilungen und viele „Mehr Infos“-Bereiche werden aber erst nachträglich per JavaScript nachgeladen. Dein Scraper bekommt sie schlicht nie zu Gesicht.
Die Lösung: Bei Seiten mit JS-gerenderten Inhalten steigst du auf Selenium oder Playwright um. Für neue Projekte rate ich zu Playwright — dank seines WebSocket-basierten Protokolls ist es 35–45 % schneller als Selenium und bringt bessere Stealth- und Async-Funktionen von Haus aus mit.

Pagination, die nur Seite 1 zurückgibt
Dieser Fehler ist heimtückisch. Du baust eine Schleife, erhöhst ?page=N, und erhältst trotzdem jedes Mal dieselben Treffer. Goodreads-Shelf-Seiten geben ohne Authentifizierung stillschweigend den Inhalt von Seite 1 zurück, selbst wenn du ?page= veränderst. Kein Fehler, kein Redirect — einfach immer wieder dieselbe erste Seite.
Die Lösung: Ein authentifiziertes Session-Cookie ergänzen, konkret _session_id2. Mehr dazu weiter unten im Pagination-Abschnitt.
Code, der letztes Jahr lief, scheitert jetzt
Goodreads ändert in schöner Regelmäßigkeit HTML-Klassennamen und Seitenstrukturen. Das bekannte GitHub-Repo maria-antoniak/goodreads-scraper trägt mittlerweile dauerhaft den Vermerk: „This project is unmaintained and no longer functioning.“ Die Antwort darauf sind stabilere Selektoren — JSON-LD-Strukturdaten (schema.org-konform und selten verändert) oder data-testid-Attribute statt brüchiger Klassennamen.
403-Fehler oder Blockierungen
Pythons requests-Bibliothek hat einen anderen TLS-Fingerprint als Chrome. Selbst mit einem Chrome-User-Agent erkennen Bot-Detection-Systeme wie AWS WAF (das Goodreads als Amazon-Tochter einsetzt) die Abweichung. Die Lösung: realistische Browser-Header setzen, zwischen den Requests time.sleep()-Pausen von 3–8 Sekunden einlegen und bei größeren Vorhaben curl_cffi für einen passenden TLS-Fingerprint erwägen.
Login-Schranken bei Regal- und Listen-Seiten
Manche Goodreads-Regal- und Listen-Seiten verlangen eine Anmeldung, um vollständige Inhalte zu zeigen — vor allem ab Seite 5. Greif zu requests.Session() mit Cookies, die du aus deinem Browser exportierst, oder zu Selenium/Playwright mit eingeloggtem Profil. Thunderbit löst das ganz nebenbei, weil es in deinem eigenen, bereits eingeloggten Chrome 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,seleniumoderplaywright,pandas - (Optional)
gspreadfür den Export nach Google Sheets - (Optional) Thunderbit Chrome Extension als No-Code-Alternative

Schritt 1: Python-Umgebung einrichten
Installiere die nötigen Bibliotheken. Öffne dein Terminal und führe aus:
pip install requests beautifulsoup4 selenium pandas lxml
Wenn du lieber mit Playwright arbeitest (für neue Projekte empfohlen):
pip install playwright
playwright install chromium
Für den Export nach Google Sheets (optional):
pip install gspread oauth2client
Achte darauf, dass du Python 3.8 oder höher fährst. Prüfen kannst du das mit python --version.
Nach der Installation sollten sich alle Bibliotheken fehlerfrei importieren lassen. Probier es mit python -c "import requests, bs4, pandas; print('Ready')".
Schritt 2: Erste Anfrage mit passenden Headern senden
Öffne im Browser eine Goodreads-Genre- oder Listen-Seite — etwa https://www.goodreads.com/list/show/1.Best_Books_Ever. Jetzt holen wir dieselbe Seite per Python.
import requests
from bs4 import BeautifulSoup
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"Accept-Language": "en-US,en;q=0.9",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
}
url = "https://www.goodreads.com/list/show/1.Best_Books_Ever"
response = requests.get(url, headers=headers, timeout=15)
print(f"Status: {response.status_code}")
Du solltest Status: 200 sehen. Erscheint stattdessen 403, kontrolliere deine Header — Goodreads' AWS WAF besteht auf einem realistischen User-Agent und blockt allzu nackte Requests. Die obigen Header geben eine echte Chrome-Sitzung vor.
Schritt 3: Seite untersuchen und die richtigen Selektoren finden
Öffne in Chrome die DevTools (F12) auf der Goodreads-Listenseite. Rechtsklick auf einen Buchtitel, dann „Untersuchen“. So siehst du die DOM-Struktur jedes Bucheintrags.
Auf Listen-Seiten steckt jedes Buch typischerweise in einem <tr>-Element mit itemtype="http://schema.org/Book". Darin findest du:
- Titel:
a.bookTitle(der Linktext ist der Titel,hrefenthält die Buch-URL) - Autor:
a.authorName - Bewertung:
span.minirating(enthält den Durchschnittswert und die Anzahl der Bewertungen) - Cover-Bild:
imginnerhalb der Buchzeile
Bei einzelnen Buchdetailseiten lässt du die CSS-Selektoren links liegen und gehst direkt an JSON-LD. Goodreads bettet strukturierte Daten in ein <script type="application/ld+json">-Tag ein, das dem schema.org-Book-Format folgt. Das ist deutlich robuster als Klassennamen, an denen Goodreads jederzeit schrauben kann.
Schritt 4: Buchdaten von einer einzelnen Listenseite extrahieren
Parsen wir die Listenseite und holen die Basisdaten für jedes Buch:
import requests
from bs4 import BeautifulSoup
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"Accept-Language": "en-US,en;q=0.9",
}
url = "https://www.goodreads.com/list/show/1.Best_Books_Ever"
response = requests.get(url, headers=headers, timeout=15)
soup = BeautifulSoup(response.text, "lxml")
books = []
rows = soup.select('tr[itemtype="http://schema.org/Book"]')
for row in rows:
title_tag = row.select_one("a.bookTitle")
author_tag = row.select_one("a.authorName")
rating_tag = row.select_one("span.minirating")
title = title_tag.get_text(strip=True) if title_tag else ""
book_url = "https://www.goodreads.com" + title_tag["href"] if title_tag else ""
author = author_tag.get_text(strip=True) if author_tag else ""
rating_text = rating_tag.get_text(strip=True) if rating_tag else ""
books.append({
"title": title,
"author": author,
"rating_info": rating_text,
"book_url": book_url,
})
print(f"Found {len(books)} books on page 1")
for b in books[:3]:
print(b)
Du solltest ungefähr 100 Bücher pro Listenseite sehen. Jeder Eintrag bringt Titel, Autor, einen Bewertungstext wie „4.28 avg rating — 9,031,257 ratings“ und eine URL zur Detailseite mit.
Schritt 5: Unterseiten für detaillierte Buchinfos scrapen
Die Listen-Seite liefert die Basics, doch die eigentlich wertvollen Infos — ISBN, vollständige Beschreibung, Genre-Tags, Seitenzahl, Veröffentlichungsdatum — liegen auf den einzelnen Buchseiten. Genau hier spielt JSON-LD seine Stärke aus.
import json
import time
def scrape_book_detail(book_url, headers):
"""Ruft eine einzelne Buchseite auf und extrahiert Metadaten über JSON-LD."""
resp = requests.get(book_url, headers=headers, timeout=15)
if resp.status_code != 200:
return {}
soup = BeautifulSoup(resp.text, "lxml")
script = soup.find("script", {"type": "application/ld+json"})
if not script:
return {}
data = json.loads(script.string)
agg = data.get("aggregateRating", {})
# Genre-Tags sind nicht in JSON-LD enthalten; daher Fallback über HTML
genres = [g.get_text(strip=True) for g in soup.select('span.BookPageMetadataSection__genreButton a span')]
return {
"isbn": data.get("isbn", ""),
"pages": data.get("numberOfPages", ""),
"language": data.get("inLanguage", ""),
"format": data.get("bookFormat", ""),
"avg_rating": agg.get("ratingValue", ""),
"rating_count": agg.get("ratingCount", ""),
"review_count": agg.get("reviewCount", ""),
"description": data.get("description", "")[:200], # für Vorschau gekürzt
"genres": ", ".join(genres[:5]),
}
# Beispiel: die ersten 3 Bücher anreichern
for book in books[:3]:
details = scrape_book_detail(book["book_url"], headers)
book.update(details)
print(f"Scraped: {book['title']} — ISBN: {book.get('isbn', 'N/A')}")
time.sleep(4) # Rate Limits respektieren
Leg zwischen den Requests eine time.sleep()-Pause von 3–8 Sekunden ein. Ab etwa 20–30 Requests pro Minute von einer einzelnen IP zieht Goodreads die Rate-Limit-Bremse, und du wirst rasch 403s oder CAPTCHAs ernten, wenn du schneller bist.
Dieser Zwei-Pass-Ansatz — erst alle Buch-URLs aus den Listen-Seiten einsammeln, dann jede Detailseite ansteuern — ist zuverlässiger und lässt sich leichter fortsetzen, falls der Lauf abbricht. Genau so gehen die meisten erfolgreichen Goodreads-Scraper vor.
Kurzer Hinweis: Thunderbit übernimmt das automatisch per Subpage-Scraping. Die KI klappert jede Buch-Detailseite ab und ergänzt deine Tabelle um ISBN, Beschreibung, Genres und mehr — ohne Code, ohne Schleifen, ohne Sleep-Timer.
Schritt 6: JavaScript-gerenderte Inhalte mit Selenium behandeln
Für Seiten, deren Inhalte per JavaScript nachgeladen werden — Rezensionen, Bewertungsaufschlüsselungen, „mehr Details“-Bereiche — brauchst du ein Browser-Automation-Tool. Hier ein Beispiel mit Selenium:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
options = Options()
options.add_argument("--headless=new")
options.add_argument("--disable-blink-features=AutomationControlled")
options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36")
driver = webdriver.Chrome(options=options)
driver.get("https://www.goodreads.com/book/show/5907.The_Hobbit")
# Warten, bis die Rezensionen geladen sind
try:
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((By.CSS_SELECTOR, "article.ReviewCard"))
)
except:
print("Reviews wurden nicht geladen — die Seite benötigt möglicherweise Login oder das Laden per JS hat zu lange gedauert")
# Jetzt die vollständig gerenderte Seite parsen
page_source = driver.page_source
soup = BeautifulSoup(page_source, "lxml")
reviews = soup.select("article.ReviewCard")
for rev in reviews[:3]:
text = rev.select_one("span.Formatted")
stars = rev.select_one("span.RatingStars")
print(f"Bewertung: {stars.get_text(strip=True) if stars else 'N/A'}")
print(f"Rezension: {text.get_text(strip=True)[:150] if text else 'N/A'}...")
print()
driver.quit()
Wann Selenium, wann requests?
- Nimm
requests+ BeautifulSoup für Buch-Metadaten (JSON-LD), Listen-Seiten, Regal-Seiten (Seite 1) und Choice-Awards-Daten - Nimm Selenium oder Playwright für Rezensionen, Bewertungsverteilungen und alles, was im Roh-HTML fehlt
Für neue Projekte ist Playwright meist die klügere Wahl — schneller, sparsamer beim Speicher, mit besseren Stealth-Voreinstellungen. Selenium punktet dafür mit der größeren Community und mehr fertigen Goodreads-Codebeispielen.
Pagination, die wirklich funktioniert: komplette Goodreads-Listen scrapen
Pagination ist der häufigste Bruchpunkt bei Goodreads-Scrapern, und ich habe noch kein konkurrierendes Tutorial gesehen, das es korrekt erklärt. So klappt es verlässlich.
Wie Goodreads-Pagination-URLs funktionieren
Goodreads nutzt für die meisten paginierten Seiten einen schlichten ?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
Eine Listenseite zeigt typischerweise 100 Bücher. Regale zeigen 50 pro Seite.
Eine Pagination-Schleife, die weiß, wann Schluss ist
import time
all_books = []
base_url = "https://www.goodreads.com/list/show/1.Best_Books_Ever"
for page_num in range(1, 50): # Sicherheitsgrenze bei 50 Seiten
url = f"{base_url}?page={page_num}"
resp = requests.get(url, headers=headers, timeout=15)
if resp.status_code != 200:
print(f"Seite {page_num}: Status {resp.status_code} erhalten, stoppe.")
break
soup = BeautifulSoup(resp.text, "lxml")
rows = soup.select('tr[itemtype="http://schema.org/Book"]')
if not rows:
print(f"Seite {page_num}: keine Bücher gefunden, Ende erreicht.")
break
for row in rows:
title_tag = row.select_one("a.bookTitle")
author_tag = row.select_one("a.authorName")
title = title_tag.get_text(strip=True) if title_tag else ""
book_url = "https://www.goodreads.com" + title_tag["href"] if title_tag else ""
author = author_tag.get_text(strip=True) if author_tag else ""
all_books.append({"title": title, "author": author, "book_url": book_url})
print(f"Seite {page_num}: {len(rows)} Bücher gescrapt (insgesamt: {len(all_books)})")
time.sleep(5) # 5 Sekunden Pause zwischen Seiten
print(f"\nFertig. Insgesamt gesammelte Bücher: {len(all_books)}")
Die letzte Seite erkennst du daran, dass keine Ergebnisse mehr da sind (keine tr[itemtype="http://schema.org/Book"]-Elemente) oder 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 wieder stillschweigend den Inhalt von Seite 1 zurück. Kein Fehler, kein Redirect — nur dieselben Daten, immer und immer wieder.
Die Lösung: Exportier das _session_id2-Cookie aus deinem Browser (etwa über eine Cookie-Export-Erweiterung oder via Chrome DevTools > Application > Cookies) und häng es an deine Requests:
session = requests.Session()
session.headers.update(headers)
session.cookies.set("_session_id2", "DEIN_SESSION_COOKIE_WERT_HIER", domain=".goodreads.com")
# Danach session.get() statt requests.get() verwenden
resp = session.get(f"{base_url}?page=6", timeout=15)
Thunderbit beherrscht klickbasierte und Infinite-Scroll-Pagination von Haus aus — ohne Code, ohne Cookie-Gefummel. Wenn deine Pagination ständig auseinanderfliegt, lohnt sich ein Blick darauf.
Das vollständige Python-Skript zum Kopieren und Einfügen
Hier das komplette, zusammenhängende Skript. Es kümmert sich um Header, Pagination, Unterseiten-Scraping per JSON-LD, Rate-Limiting und CSV-Export. Getestet habe ich es gegen live Goodreads-Seiten, Stand Mitte 2025.
"""
goodreads_scraper.py — Scraped eine Goodreads-Liste mit Pagination und Anreicherung der Buchdetails.
Verwendung: python goodreads_scraper.py
Ausgabe: goodreads_books.csv
"""
import csv, json, time, requests
from bs4 import BeautifulSoup
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"Accept-Language": "en-US,en;q=0.9",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
}
BASE_URL = "https://www.goodreads.com/list/show/1.Best_Books_Ever"
MAX_PAGES = 3 # nach Bedarf anpassen
DELAY_LISTING = 5 # Sekunden zwischen Listen-Seiten
DELAY_DETAIL = 4 # Sekunden zwischen Detailseiten
OUTPUT_FILE = "goodreads_books.csv"
def scrape_listing_page(url):
"""Gibt eine Liste von Dicts mit Titel, Autor und book_url aus einer Listen-Seite zurück."""
resp = requests.get(url, headers=HEADERS, timeout=15)
if resp.status_code != 200:
return []
soup = BeautifulSoup(resp.text, "lxml")
rows = soup.select('tr[itemtype="http://schema.org/Book"]')
books = []
for row in rows:
t = row.select_one("a.bookTitle")
a = row.select_one("a.authorName")
if t:
books.append({
"title": t.get_text(strip=True),
"author": a.get_text(strip=True) if a else "",
"book_url": "https://www.goodreads.com" + t["href"],
})
return books
def scrape_book_detail(book_url):
"""Ruft eine Buchseite ab und extrahiert Metadaten via JSON-LD plus HTML-Fallback."""
resp = requests.get(book_url, headers=HEADERS, timeout=15)
if resp.status_code != 200:
return {}
soup = BeautifulSoup(resp.text, "lxml")
script = soup.find("script", {"type": "application/ld+json"})
if not script:
return {}
data = json.loads(script.string)
agg = data.get("aggregateRating", {})
genres = [g.get_text(strip=True)
for g in soup.select("span.BookPageMetadataSection__genreButton a span")]
return {
"isbn": data.get("isbn", ""),
"pages": data.get("numberOfPages", ""),
"avg_rating": agg.get("ratingValue", ""),
"rating_count": agg.get("ratingCount", ""),
"review_count": agg.get("reviewCount", ""),
"description": (data.get("description", "") or "")[:300],
"genres": ", ".join(genres[:5]),
"language": data.get("inLanguage", ""),
"format": data.get("bookFormat", ""),
"published": data.get("datePublished", ""),
}
def main():
all_books = []
# --- Durchlauf 1: Buch-URLs aus den Listen-Seiten sammeln ---
for page in range(1, MAX_PAGES + 1):
url = f"{BASE_URL}?page={page}"
page_books = scrape_listing_page(url)
if not page_books:
print(f"Seite {page}: leer — stoppe Pagination.")
break
all_books.extend(page_books)
print(f"Seite {page}: {len(page_books)} Bücher (insgesamt: {len(all_books)})")
time.sleep(DELAY_LISTING)
# --- Durchlauf 2: jedes Buch mit Detaildaten anreichern ---
for i, book in enumerate(all_books):
details = scrape_book_detail(book["book_url"])
book.update(details)
print(f"[{i+1}/{len(all_books)}] {book['title']} — ISBN: {book.get('isbn', 'N/A')}")
time.sleep(DELAY_DETAIL)
# --- Export nach CSV ---
if all_books:
fieldnames = list(all_books[0].keys())
with open(OUTPUT_FILE, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(all_books)
print(f"\n{len(all_books)} Bücher in {OUTPUT_FILE} gespeichert")
else:
print("Keine Bücher gescrapt.")
if __name__ == "__main__":
main()
Mit MAX_PAGES = 3 sammelt dieses Skript rund 300 Bücher aus der Liste „Best Books Ever“, besucht jede Detailseite und schreibt alles in eine CSV-Datei. Auf meinem Rechner dauert das etwa 25 Minuten (vor allem 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
Möchtest du die Daten lieber in Google Sheets statt zusätzlich als CSV, häng nach dem CSV-Export Folgendes an:
import gspread
from oauth2client.service_account import ServiceAccountCredentials
scope = ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/drive"]
creds = ServiceAccountCredentials.from_json_keyfile_name("credentials.json", scope)
client = gspread.authorize(creds)
sheet = client.open("Goodreads Scrape").sheet1
header = list(all_books[0].keys())
sheet.append_row(header)
for book in all_books:
sheet.append_row([str(book.get(k, "")) for k in header])
print("Daten an Google Sheets übertragen.")
Dafür brauchst du ein Google-Cloud-Service-Account mit aktivierten Sheets- und Drive-APIs. Die gspread-Dokumentation führt dich in etwa 5 Minuten durch die Einrichtung. Nutz Batch-Operationen (append_rows() mit einer Liste von Listen), sobald du mehr als ein paar hundert Zeilen schreibst — Googles Limit liegt bei 300 Requests pro 60 Sekunden pro Projekt.
Falls dir das ganze Setup zu viel ist: Thunderbit exportiert mit einem Klick direkt nach Google Sheets, Airtable, Notion, Excel, CSV und JSON — ohne Bibliotheksinstallation, Credentials-Datei oder API-Quoten.
Die No-Code-Alternative: Goodreads mit Thunderbit scrapen
Nicht jeder will ein Python-Skript pflegen. Vielleicht bist du ein Verlag mit einer einmaligen Marktanalyse oder ein Buchblogger, der bloß eine Tabelle der diesjährigen Bestseller braucht. Für genau diese Fälle haben wir Thunderbit gebaut.
So scrapest du Goodreads mit Thunderbit
- Installier die Thunderbit Chrome-Erweiterung aus dem Chrome Web Store und öffne eine Goodreads-Liste, ein Regal oder eine Suchergebnisseite.
- Klick in der Thunderbit-Seitenleiste auf „KI-Felder vorschlagen“. Die KI liest die Seite und schlägt Spalten vor — meist Titel, Autor, Bewertungswert, Cover-URL und Buchlink.
- Klick auf „Scrapen“ — die Daten landen in Sekunden in einer strukturierten Tabelle.
- Exportier nach Google Sheets, Excel, Airtable, Notion, CSV oder JSON.
Für detaillierte Buchdaten wie ISBN, Beschreibung, Genres und Seitenzahl klappert Thunderbits Subpage-Scraping automatisch jede Buch-Detailseite ab und ergänzt die Tabelle — ohne Schleifen, ohne Sleep-Timer, ohne Debugging.
Auch paginierte Listen beherrscht Thunderbit nativ. Du gibst an, dass auf „Next“ geklickt oder gescrollt werden soll, und das Tool sammelt über alle Seiten hinweg — ohne Code.
Der Unterschied ist simpel: Das Python-Skript gibt dir maximale Kontrolle und ist kostenlos (abgesehen von deiner Zeit), während Thunderbit ein bisschen Flexibilität gegen enorme Zeitersparnis und null Wartungsaufwand eintauscht. Bei einer Liste mit 300 Büchern läuft das Python-Skript rund 25 Minuten, plus die Zeit fürs Schreiben und Debuggen. Thunderbit liefert dieselben Daten in etwa 3 Minuten, mit zwei Klicks.
Thunderbit für Goodreads-Scraping testen
Goodreads verantwortungsvoll scrapen: robots.txt, Nutzungsbedingungen und Ethik
Dieses Thema verdient eine klare Antwort, keinen pauschalen Pflichtsatz.
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 einige weitere Pfade. GPTBot und CCBot (Common Crawl) werden vollständig mit Disallow: / ausgesperrt. Für bingbot gilt Crawl-delay: 5, ein globales Delay gibt es aber nicht.
Goodreads’ Nutzungsbedingungen in einfachen Worten
Die Nutzungsbedingungen (zuletzt überarbeitet am 28. April 2021) untersagen „jede Form von Data Mining, Robots oder ähnlichen Tools zur Datenerfassung und -extraktion“. Das ist sehr weit gefasst und sollte ernst genommen werden — gleichzeitig haben Gerichte wiederholt geurteilt, dass ein bloßer Verstoß gegen die Nutzungsbedingungen nicht automatisch eine strafbare „unbefugte Zugriffs“-Handlung darstellt. Das Urteil in Sandvig v. Barr 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 erst nach Login sichtbar werden
- 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: Öffentliche Daten für private Analysen oder akademische Forschung zu scrapen ist in der Regel unkritischer. Das größte rechtliche Risiko entsteht bei kommerzieller Weiterverbreitung.
Ein Tool wie Thunderbit, das über deine eigene Browser-Session läuft, sieht optisch wie normales Surfen aus — die ethischen Grundsätze gelten trotzdem unabhängig vom Tool. Wer tiefer in die rechtlichen Aspekte von Web Scraping einsteigen will, findet das bei uns separat aufbereitet.
Tipps und häufige Fehler
Tipp: Immer zuerst JSON-LD prüfen. Bevor du dich an komplexe CSS-Selektoren machst, schau nach, ob die gesuchten Daten schon im <script type="application/ld+json">-Tag liegen. Das ist stabiler, leichter zu parsen und übersteht Frontend-Änderungen deutlich besser.
Tipp: Setz auf die Zwei-Pass-Strategie. Erst alle Buch-URLs von den Listen-Seiten sammeln, dann jede Detailseite besuchen. So setzt du den Scraper bei einem Absturz mittendrin leichter fort und kannst die URL-Liste als Zwischenstand auf die Platte schreiben.
Fehler: Fehlende Felder nicht abfangen. Nicht jede Buchseite hat ISBN, Genre-Tags oder eine Beschreibung. Greif immer zu .get() mit Standardwert oder prüfe Selektoren per if. Ein einziger NoneType-Fehler kann einen dreistündigen Scrape-Lauf killen.
Fehler: Zu schnell laufen. Klar, time.sleep(0.5) lockt. Doch Goodreads schaltet nach etwa 20–30 schnellen Requests auf 403 um, und bist du erst markiert, heißt es womöglich stundenlang warten oder IP wechseln. 4–5 Sekunden Pause sind meist der Sweet Spot.
Fehler: Alten Tutorials blind vertrauen. Erwähnt eine Anleitung noch die Goodreads API oder benutzt Klassennamen wie .field.value oder #bookTitle, ist sie höchstwahrscheinlich veraltet. Prüf Selektoren immer direkt an der Live-Seite, bevor du den Scraper baust.
Mehr dazu, wie du die passenden Tools und Frameworks auswählst, findest du in unseren Leitfäden zu den besten Python-Web-Scraping-Tools und Web Scraping mit Python.
Fazit und wichtigste Erkenntnisse
Goodreads mit Python zu scrapen ist absolut machbar — du musst nur wissen, wo die Stolperfallen lauern. Die wichtigsten Punkte:
- Die Goodreads API ist Geschichte (seit Dezember 2020). Scraping ist der zentrale Weg, um strukturierte Buchdaten von der Plattform zu holen.
- Leere Ergebnisse gehen fast immer auf JS-gerenderte Inhalte, veraltete Selektoren, fehlende Header oder Authentifizierungsprobleme bei der Pagination zurück — nicht darauf, dass dein Code grundsätzlich falsch wäre.
- JSON-LD ist dein bester Freund für Buchmetadaten. Stabil, strukturiert, ändert sich selten.
- Pagination braucht bei vielen Regal- und Listen-Seiten ab Seite 5 Authentifizierung. Ergänze das
_session_id2-Cookie. - Rate-Limiting ist real. Setz auf 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 sich ihren Nachmittag erhalten wollen) erledigt Thunderbit das alles — JS-Rendering, Pagination, Anreicherung von Unterseiten und Export — in rund zwei Klicks.
Scrape verantwortungsvoll und respektiere die robots.txt. Mit den obigen Kniffen liefert dein Scraper am Ende echte Buchdaten statt [].
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 nach, die ein einfaches requests.get() nie 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.
Ist es legal, Goodreads zu scrapen?
Öffentlich verfügbare Daten für private oder Forschungszwecke zu scrapen, 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 niedrig, damit die Website nicht unnötig belastet wird.
Wie scrape ich mehrere Seiten auf Goodreads?
Häng ?page=N an die Shelf- oder Listen-URL und iteriere über die Seitennummern. Achte auf leere Ergebnisse oder das Fehlen eines „Next“-Links, um die letzte Seite zu erkennen. Wichtig: Manche Shelf-Seiten brauchen Authentifizierung (das _session_id2-Cookie), damit Seiten jenseits der 5 überhaupt Ergebnisse liefern — ohne dieses Cookie bekommst du sonst still und leise immer wieder Seite 1.
Kann ich Goodreads scrapen, ohne Code zu schreiben?
Ja. Thunderbit ist eine Chrome-Erweiterung, mit der du Goodreads in zwei Klicks scrapst — die KI schlägt Felder vor, du klickst auf „Scrapen“ 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


