Jak scrapovat Goodreads v Pythonu (už žádné prázdné výsledky)

Poslední aktualizace April 16, 2026
Jak scrapovat Goodreads v Pythonu (už žádné prázdné výsledky)

Pár věcí dokáže člověka zklamat víc než napsat 30 řádků v Pythonu, spustit Goodreads scraper a místo dat dostat []. Prázdný seznam. Nic. Jen ty a blikající kurzor.

Tohle jsem viděl už desítkykrát — v našich interních experimentech v Thunderbit, ve vývojářských fórech i v issuech na GitHubu u dávno opuštěných scraper repozitářů. Stížnosti jsou skoro vždycky stejné: "sekce s top recenzemi je prázdná, vrací jen []", "ať spustím jakékoliv číslo stránky, vždycky mi to stáhne jen první stránku", "kód fungoval loni, teď je rozbitý." A aby toho nebylo málo, Goodreads API bylo v prosinci 2020 zrušeno, takže rada typu „prostě použij API“, kterou najdeš ve starších návodech, je dnes slepá ulička.

Jestli dnes chceš z Goodreads získat strukturovaná data o knihách — názvy, autory, hodnocení, recenze, žánry, ISBN — scraping je hlavní cesta. V tomhle průvodci ti ukážu funkční a kompletní postup, jak scrapovat Goodreads v Pythonu, včetně obsahu renderovaného JavaScriptem, stránkování, ochrany proti blokování a exportu. A pokud Python není tvoje parketa, ukážu i no-code alternativu, která to zvládne zhruba na dvě kliknutí.

Co je scraping Goodreads a proč ho dělat v Pythonu?

Scraping Goodreads znamená automatizované získávání dat o knihách — názvy, autoři, hodnocení, počet recenzí, žánry, ISBN, počet stran, datum vydání a další — přímo z webových stránek Goodreads pomocí kódu místo ručního kopírování.

Goodreads je jedna z největších knižních databází na světě, s více než 150 miliony registrovaných uživatelů a přibližně 90 miliony uživatelských recenzí. Každý měsíc se na „Want to Read“ police dostane přes 18 milionů knih. Právě taková průběžně aktualizovaná a strukturovaná data jsou důvodem, proč se k ní pořád vracejí vydavatelé, datoví analytici, knihkupci i výzkumníci.

Python je pro tenhle typ práce volba číslo jedna — pohání zhruba 69,6 % všech scrapingových projektů. Knihovny jsou vyspělé (requests, BeautifulSoup, Selenium, Playwright, pandas), syntaxe je přívětivá pro začátečníky a komunita obrovská.

Pokud jsi nikdy předtím web nescrapoval, Python je skvělé místo, kde začít.

Proč scrapovat Goodreads v Pythonu? Praktické případy použití

Než se pustíme do kódu, stojí za to položit si otázku: kdo vlastně ta data potřebuje a co s nimi dělá?

Případ použitíKdo z toho těžíCo scrapujete
Průzkum trhu pro vydavateleVydavatelé, literární agentiTrendující žánry, nejlépe hodnocené tituly, nové autory, hodnocení konkurence
Doporučovací systémy pro knihyDatoví analytici, nadšenci, vývojáři aplikacíHodnocení, žánry, uživatelské police, sentiment recenzí
Sledování cen a zásobOnline knihkupciTrendující tituly, objem recenzí, počet „Want to Read“
Akademický výzkumVýzkumníci, studentiTexty recenzí, rozložení hodnocení, kategorizace žánrů
Analýza čteníBook blogeři, osobní projektyData z osobních polic, historie čtení, statistiky za rok

Pár konkrétních příkladů: UCSD Book Graph — jeden z nejčastěji citovaných akademických datasetů v oblasti doporučovacích systémů — obsahuje 2,36 milionu unikátních knih a přes 228 milionů interakcí uživatel–kniha, vše sesbírané z veřejně dostupných Goodreads polic. Několik Kaggle datasetů (goodbooks-10k, Best Books Ever atd.) původně vzniklo právě scrapingem Goodreads. A studie z roku 2025 v Big Data and Society strojově zpracovala 331 211 incentivizovaných recenzí knih, aby analyzovala, jak sponzorované recenze ovlivňují platformu.

Z obchodního hlediska Bright Data prodává předem nascrapované Goodreads datasety už od 0,50 USD za 1 000 záznamů — jasný důkaz, že tahle data mají reálnou hodnotu.

Goodreads API zmizelo — co ho nahradilo?

Pokud jsi v poslední době hledal „Goodreads API“, nejspíš jsi narazil na návod, který je dávno zastaralý. Dne 8. prosince 2020 Goodreads potichu přestal vydávat nové vývojářské API klíče. Žádný blogový post, žádný e-mail — jen malý banner v dokumentaci a spousta zmatených vývojářů.

goodreads-data-access-tools.webp

Dopady byly okamžité. Jeden vývojář, Kyle K, vytvořil Discord bota na sdílení knižních doporučení — „najednou to PUF prostě přestalo fungovat.“ Jiný, Matthew Jones, přišel o přístup k API týden před hlasováním v Redditu pro r/Fantasy Stabby Awards, takže se musel vrátit ke Google Forms. Studentka magistrského studia Elena Neacsu měla rozpracovaný projekt diplomky a vše se jí rozpadlo uprostřed vývoje.

Co tedy zbývá? Dnešní možnosti vypadají takto:

PřístupDostupná dataJednoduchost použitíRate limitStav
Goodreads APIKompletní metadata, recenzeSnadné (dříve)1 požadavek/sZrušeno (prosinec 2020) — nové klíče nejsou
Open Library APINázvy, autoři, ISBN, obaly (~30 mil. titulů)Snadné1–3 požadavky/sAktivní, zdarma, bez autentizace
Google Books APIMetadata, náhledySnadné1 000/den zdarmaAktivní (chybí některá ISBN mimo angličtinu)
Python scraping (requests + BS4)Cokoliv v počátečním HTMLStředně náročnéSpravujete samiFunguje pro statický obsah
Python scraping (Selenium/Playwright)I obsah renderovaný JSNáročnějšíSpravujete samiNutné pro recenze a některé seznamy
Thunderbit (no-code Chrome rozšíření)Jakákoli viditelná data na stránceVelmi snadné (2 kliknutí)Na kredityAktivní — Python není potřeba

Open Library je výborný doplněk, hlavně pro dohledávání ISBN a základních metadat. Pokud ale potřebuješ hodnocení, recenze, štítky žánrů nebo počty „Want to Read“, scrapuješ Goodreads přímo — buď v Pythonu, nebo pomocí nástroje jako Thunderbit, který umí Goodreads stránky (včetně podstránek s detaily knih) scrapovat s AI navrženými poli a rovnou exportovat do Google Sheets, Notion nebo Airtable.

Proč váš Goodreads Python scraper vrací prázdné výsledky (a jak to opravit)

Tohle je sekce, kterou bych si přál mít k dispozici, když jsem s Goodreads daty začínal. Problém „prázdných výsledků“ je ve vývojářských fórech úplně nejčastější stížnost a může mít několik různých příčin — každá má jiné řešení.

PříznakPříčinaŘešení
Recenze/hodnocení vrací []Obsah renderovaný JS (React / lazy-load)Použijte Selenium nebo Playwright místo requests
Vždy se scrapuje jen první stránkaParametr stránkování je ignorovaný nebo řízený JSPředávejte správně ?page=N; pro infinite scroll použijte automatizaci prohlížeče
Kód fungoval loni, teď selháváGoodreads změnil názvy HTML třídPoužijte odolnější selektory (JSON-LD, atributy data-testid)
403 / blokace po pár požadavcíchChybějící hlavičky / příliš rychlé požadavkyPřidejte User-Agent, time.sleep(), střídejte proxy
Login wall na stránkách polic / seznamůNutná cookie / sessionPoužijte requests.Session() s cookies nebo scraping přes prohlížeč

Obsah renderovaný JavaScriptem: recenze a hodnocení jsou prázdné

Goodreads používá frontend postavený na Reactu. Když zavoláš requests.get() na stránku knihy, dostaneš počáteční HTML — ale recenze, rozložení hodnocení a mnoho sekcí „více informací“ se načítá asynchronně přes JavaScript. Tvůj scraper je jednoduše nevidí.

Řešení: u každé stránky, kde potřebuješ obsah renderovaný JavaScriptem, přejdi na Selenium nebo Playwright. Pro nové projekty doporučuji Playwright — díky protokolu založenému na WebSocketu je o 35–45 % rychlejší než Selenium a má lepší vestavěné možnosti stealth a podporu async.

troubleshooting-empty-array-causes.webp

Stránkování vrací pořád jen první stránku

Tohle je záludné. Napíšeš smyčku, zvyšuješ ?page=N, ale výsledky jsou pokaždé stejné. Na Goodreads se u stránek polic potichu vrací obsah první stránky bez ohledu na parametr ?page=, pokud nejsi přihlášený. Žádná chyba, žádné přesměrování — jen pořád dokola ta samá první stránka.

Řešení: přidej autentizační cookie session, konkrétně _session_id2. Více o tom v sekci o stránkování níže.

Kód, který fungoval loni, už nefunguje

Goodreads občas mění názvy HTML tříd i strukturu stránky. Populární repozitář maria-antoniak/goodreads-scraper na GitHubu dnes nese trvalé upozornění: "This project is unmaintained and no longer functioning." Řešení je používat odolnější selektory — strukturovaná data JSON-LD (podle standardu schema.org, která se mění jen zřídka) nebo atributy data-testid místo křehkých názvů tříd.

403 chyby nebo blokace

Knihovna requests v Pythonu má jiný TLS fingerprint než Chrome. I když použiješ User-Agent string pro Chrome, systémy na detekci botů jako AWS WAF (který Goodreads používá, protože patří Amazonu) mohou nesoulad odhalit. Řešení: přidej realistické browser hlavičky, mezi požadavky vlož zpoždění time.sleep() 3–8 sekund a při větším scrapingovém objemu zvaž curl_cffi pro shodu TLS fingerprintu.

Login wall na stránkách polic a seznamů

Některé stránky polic a seznamů na Goodreads vyžadují přihlášení, aby se zobrazil plný obsah, zejména po páté stránce. Použij requests.Session() s cookies exportovanými z prohlížeče, nebo Selenium/Playwright s přihlášeným profilem. Thunderbit to řeší přirozeně, protože běží ve tvém vlastním přihlášeném Chromu.

Než začnete

  • Obtížnost: střední (předpokládá se základní znalost Pythonu)
  • Časová náročnost: asi 20–30 minut pro celý postup
  • Co budete potřebovat:
    • Python 3.8+
    • Prohlížeč Chrome (pro kontrolu přes DevTools a Selenium/Playwright)
    • Knihovny: requests, beautifulsoup4, selenium nebo playwright, pandas
    • (Volitelné) gspread pro export do Google Sheets
    • (Volitelné) Thunderbit Chrome Extension jako no-code alternativa

goodreads-scraping-flow.webp

Krok 1: Nastavení Python prostředí

Nainstalujte potřebné knihovny. Otevřete terminál a spusťte:

pip install requests beautifulsoup4 selenium pandas lxml

Pokud dáváte přednost Playwrightu (doporučeno pro nové projekty):

pip install playwright
playwright install chromium

Pro export do Google Sheets (volitelné):

pip install gspread oauth2client

Ujistěte se, že používáte Python 3.8 nebo vyšší. Verzi zkontrolujete přes python --version.

Po instalaci by mělo jít importovat všechny knihovny bez chyb. Zkuste python -c "import requests, bs4, pandas; print('Ready')", abyste si to ověřili.

Krok 2: Pošlete první požadavek se správnými hlavičkami

V prohlížeči si otevřete nějakou Goodreads stránku žánrové police nebo seznamu — například https://www.goodreads.com/list/show/1.Best_Books_Ever. Teď ji načteme v Pythonu.

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}")

Měli byste vidět Status: 200. Pokud dostanete 403, zkontrolujte hlavičky — AWS WAF Goodreads kontroluje realistický User-Agent a jednoduché požadavky blokuje. Hlavičky výše napodobují běžnou relaci v Chrome.

Krok 3: Prozkoumejte stránku a najděte správné selektory

Otevřete v Chromu DevTools (F12) na stránce seznamu Goodreads. Klikněte pravým tlačítkem na název knihy a zvolte „Inspect“. Uvidíte DOM strukturu jednotlivých záznamů.

U seznamových stránek je každá kniha obvykle zabalená v elementu <tr> s itemtype="http://schema.org/Book". Uvnitř najdete:

  • Název: a.bookTitle (text odkazu je název, href je URL knihy)
  • Autor: a.authorName
  • Hodnocení: span.minirating (obsahuje průměrné hodnocení a počet hodnocení)
  • Obrázek obálky: img uvnitř řádku knihy

U detailních stránek knih raději přeskoč CSS selektory a jdi rovnou na JSON-LD. Goodreads vkládá strukturovaná data do tagu <script type="application/ld+json">, který odpovídá formátu schema.org Book. Je mnohem stabilnější než názvy tříd, které Goodreads mění podle potřeby.

Krok 4: Vytáhněte data o knihách z jedné stránkové výpisové stránky

Pojďme rozparsovat list stránku a vytáhnout základní informace o každé knize:

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"Na stránce 1 jsem našel {len(books)} knih")
for b in books[:3]:
    print(b)

Měli byste vidět zhruba 100 knih na jednu list stránku. Každý záznam bude obsahovat název, autora, řetězec s hodnocením jako „4,28 avg rating — 9 031 257 ratings“ a URL na detail knihy.

Krok 5: Scrape podstránek pro detailní informace o knize

Výpisová stránka ti dá základ, ale to nejzajímavější — ISBN, plný popis, žánrové štítky, počet stran, datum vydání — je až na detailní stránce knihy. Tady se ukáže síla JSON-LD.

import json
import time

def scrape_book_detail(book_url, headers):
    """Navštíví jednu stránku knihy a vytáhne detailní metadata přes 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", {})

    # Žánrové štítky nejsou v JSON-LD; použijeme HTML jako fallback
    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],  # zkráceno pro náhled
        "genres": ", ".join(genres[:5]),
    }

# Příklad: obohaťte první 3 knihy
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)  # respektujte rate limit

Mezi požadavky přidej time.sleep() 3–8 sekund. Goodreads obvykle začne rate limiting kolem 20–30 požadavků za minutu z jedné IP a při rychlejším tempu se začnou objevovat 403 nebo CAPTCHA.

Tenhle dvoufázový postup — nejdřív nasbírat URL knih z list stránek a pak navštívit detailní stránky — je spolehlivější a snadno se obnovuje po přerušení. Používá ho většina úspěšných Goodreads scraperů.

Poznámka: Thunderbit to zvládne automaticky i se scrapingem podstránek. AI navštíví detail každé knihy a obohatí tabulku o ISBN, popis, žánry a další — bez kódu, smyček a čekacích timerů.

Krok 6: Obsah renderovaný JavaScriptem vyřešte pomocí Selenium

U stránek, kde se potřebný obsah načítá přes JavaScript — recenze, rozpad hodnocení, sekce „more details“ — budeš potřebovat nástroj pro automatizaci prohlížeče. Tady je příklad se 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")

# Počkejte, až se načtou recenze
try:
    WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.CSS_SELECTOR, "article.ReviewCard"))
    )
except:
    print("Recenze se nenačetly — stránka možná vyžaduje přihlášení nebo vypršel čas pro JS")

# Teď rozparsujte plně vyrenderovanou stránku
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"Hodnocení: {stars.get_text(strip=True) if stars else 'N/A'}")
    print(f"Recenze: {text.get_text(strip=True)[:150] if text else 'N/A'}...")
    print()

driver.quit()

Kdy použít Selenium vs. requests:

  • requests + BeautifulSoup použij pro metadata knih (JSON-LD), list stránku, shelf stránky (strana 1) a data z Choice Awards
  • Selenium nebo Playwright použij pro recenze, rozpad hodnocení a jakýkoli obsah, který se v raw HTML vůbec neobjeví

Playwright je obecně lepší volba pro nové projekty — je rychlejší, méně paměťově náročný a má lepší stealth defaulty. Selenium má ale větší komunitu a víc existujících příkladů pro Goodreads.

Stránkování, které opravdu funguje: jak scrapovat celé seznamy Goodreads

Stránkování je nejčastější místo, kde Goodreads scrapery selhávají, a nenašel jsem jediný konkurenční návod, který by to vysvětloval pořádně. Takhle to uděláš správně.

Jak fungují URL stránkování na Goodreads

Goodreads používá u většiny stránkovaných stránek jednoduchý parametr ?page=N:

  • Seznamy: https://www.goodreads.com/list/show/1.Best_Books_Ever?page=2
  • Police: https://www.goodreads.com/shelf/show/thriller?page=2
  • Vyhledávání: https://www.goodreads.com/search?q=fantasy&page=2

Každá list stránka obvykle zobrazuje 100 knih. Police zobrazují 50 na stránku.

Jak napsat smyčku pro stránkování, která pozná, kdy skončit

import time

all_books = []
base_url = "https://www.goodreads.com/list/show/1.Best_Books_Ever"

for page_num in range(1, 50):  # bezpečnostní limit na 50 stránek
    url = f"{base_url}?page={page_num}"
    resp = requests.get(url, headers=headers, timeout=15)

    if resp.status_code != 200:
        print(f"Stránka {page_num}: status {resp.status_code}, končím.")
        break

    soup = BeautifulSoup(resp.text, "lxml")
    rows = soup.select('tr[itemtype="http://schema.org/Book"]')

    if not rows:
        print(f"Stránka {page_num}: žádné knihy nenalezeny, konec seznamu.")
        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"Stránka {page_num}: nascrapováno {len(rows)} knih (celkem: {len(all_books)})")
    time.sleep(5)  # pětisekundová pauza mezi stránkami

print(f"\nHotovo. Celkem získáno knih: {len(all_books)}")

Poslední stránku poznáš podle toho, že je výsledkový seznam prázdný (nenajdou se žádné elementy tr[itemtype="http://schema.org/Book"]) nebo že chybí odkaz na další stránku (a.next_page).

Záludný případ: po páté stránce je potřeba přihlášení

Tohle je past, do které spadne skoro každý: některé stránky polic a seznamů na Goodreads po zadání stránky 6+ bez přihlášení potichu vrátí obsah první stránky. Žádná chyba, žádné přesměrování — jen opakovaná stejná data.

Řešení: exportuj cookie _session_id2 z prohlížeče (pomocí rozšíření na export cookies nebo přes Chrome DevTools > Application > Cookies) a přidej ji do požadavků:

session = requests.Session()
session.headers.update(headers)
session.cookies.set("_session_id2", "HODNOTA_VAŠÍ_SESSION_COOKIE", domain=".goodreads.com")

# Místo requests.get() používejte session.get()
resp = session.get(f"{base_url}?page=6", timeout=15)

Thunderbit zvládá stránkování přes kliknutí i nekonečný scroll nativně, bez kódu a bez správy cookies. Pokud se ti logika stránkování pořád rozpadá, stojí za to ho zvážit.

Kompletní Python skript připravený ke zkopírování

Tady je kompletní sjednocený skript. Ošetřuje hlavičky, stránkování, scraping podstránek přes JSON-LD, rate limiting i export do CSV. Testoval jsem ho proti živým Goodreads stránkám k polovině roku 2025.

"""
goodreads_scraper.py — Scrape Goodreads list s pagingem a obohacením detailů knih.
Použití: python goodreads_scraper.py
Výstup: 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          # upravte podle potřeby
DELAY_LISTING = 5      # sekundy mezi stránkami seznamu
DELAY_DETAIL = 4       # sekundy mezi detailními stránkami
OUTPUT_FILE = "goodreads_books.csv"


def scrape_listing_page(url):
    """Vrátí seznam dictů s title, author, book_url z jedné list stránky."""
    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):
    """Navštíví stránku knihy a vytáhne metadata přes JSON-LD + 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 = []

    # --- 1. průchod: seberte URL knih z list stránek ---
    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"Stránka {page}: prázdná — končím stránkování.")
            break
        all_books.extend(page_books)
        print(f"Stránka {page}: {len(page_books)} knih (celkem: {len(all_books)})")
        time.sleep(DELAY_LISTING)

    # --- 2. průchod: obohaťte každou knihu o detailní data ---
    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 do 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"\nUloženo {len(all_books)} knih do {OUTPUT_FILE}")
    else:
        print("Nepodařilo se nascrapovat žádné knihy.")


if __name__ == "__main__":
    main()

S MAX_PAGES = 3 tenhle skript nasbírá z listu „Best Books Ever“ asi 300 knih, navštíví detail každé z nich a všechno zapíše do CSV. Na mém počítači to trvá zhruba 25 minut (hlavně kvůli 4sekundovým pauzám mezi detailními požadavky). Výsledné CSV bude mít sloupce jako title, author, book_url, isbn, pages, avg_rating, rating_count, review_count, description, genres, language, format a published.

Export i jinam než do CSV: Google Sheets přes gspread

Pokud chceš data do Google Sheets místo CSV, nebo navíc k němu, přidej po exportu do CSV tohle:

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("Data odeslána do Google Sheets.")

Budeš potřebovat služební účet Google Cloud s aktivovanými Sheets a Drive API. Dokumentace gspread tě provede nastavením asi za 5 minut. Pokud posíláš víc než několik stovek řádků, používej dávkové operace (append_rows() se seznamem seznamů) — limit Googlu je 300 požadavků za 60 sekund na projekt.

Samozřejmě, pokud ti celé tohle nastavování připadá zbytečně komplikované, Thunderbit exportuje do Google Sheets, Airtable, Notion, Excelu, CSV i JSON na jedno kliknutí — bez instalace knihoven, bez souboru s credentials a bez API kvót.

No-code alternativa: scrapování Goodreads s Thunderbit

Ne každý chce udržovat Python skript. Možná jsi vydavatel a děláš jednorázovou analýzu trhu, nebo book blogger, který chce jen tabulku nejprodávanějších titulů. Přesně pro takové případy jsme Thunderbit vytvořili.

Jak scrapovat Goodreads s Thunderbit

  1. Nainstalujte rozšíření Thunderbit pro Chrome z Chrome Web Store a otevři Goodreads list, shelf nebo výsledky vyhledávání.
  2. Klikni na „AI Suggest Fields“ v postranním panelu Thunderbit. AI si stránku přečte a navrhne sloupce — obvykle název, autora, URL obrázku obálky a odkaz na knihu.
  3. Klikni na „Scrape“ — data se během pár sekund vytáhnou do strukturované tabulky.
  4. Exportuj do Google Sheets, Excelu, Airtable, Notion, CSV nebo JSON.

Pro detailní data o knihách (ISBN, popis, žánry, počet stran) Thunderbit použije scraping podstránek: navštíví detail každé knihy a tabulku automaticky obohatí — bez smyček, bez čekání, bez debugování.

Thunderbit zvládá i stránkované seznamy nativně. Řekneš mu, aby klikal na „Next“ nebo scrolloval, a on nasbírá data přes všechny stránky bez jediného řádku kódu.

Výhoda i kompromis jsou jednoduché: Python skript ti dává plnou kontrolu a je zdarma, pokud nepočítáš svůj čas. Thunderbit obětuje trochu flexibility výměnou za obrovskou úsporu času a nulovou údržbu. U seznamu o 300 knihách trvá Python skript asi 25 minut běhu plus čas, který jsi strávil psaním a laděním. Thunderbit stejná data získá zhruba za 3 minuty a ve dvou kliknutích.

Vyzkoušet Thunderbit pro scraping Goodreads

Scraping Goodreads zodpovědně: robots.txt, podmínky použití a etika

Tohle si zaslouží přímou odpověď, ne laciný odstavcový disclaimer.

Co vlastně říká Goodreads robots.txt

Aktuální robots.txt Goodreads je překvapivě konkrétní. Stránky s detaily knih (/book/show/), veřejné seznamy (/list/show/), veřejné police (/shelf/show/) i stránky autorů (/author/show/) nejsou blokované. Co blokované je: /api, /book/reviews/, /review/list, /review/show, /search a několik dalších cest. GPTBot a CCBot (Common Crawl) jsou zcela zablokované přes Disallow: /. U bingbotu je nastavené Crawl-delay: 5, ale žádné globální zdržení neplatí.

Podmínky použití Goodreads lidsky

Podmínky použití (naposledy upravené 28. dubna 2021) zakazují „jakékoli použití dataminingu, robotů nebo podobných nástrojů pro sběr a extrakci dat“. To je široká formulace a stojí za to ji brát vážně — zároveň ale soudy opakovaně rozhodly, že samotné porušení ToS ještě neznamená trestněprávní „neoprávněný přístup“. Rozhodnutí Sandvig v. Barr uvedlo, že „kriminalizace porušení podmínek použití riskuje, že se každý web stane vlastním trestním jurisdikčním územím“.

Doporučené postupy

  • Zpomalte požadavky: 3–8 sekund mezi requesty (samotné Goodreads v robots.txt doporučuje pro boty 5 sekund)
  • Nepřekračujte 5 000 požadavků denně z jedné IP
  • Scrapujte jen veřejně dostupné stránky — vyhněte se hromadnému sběru dat dostupných jen po přihlášení
  • Nešířte komerčně raw text recenzí — recenze jsou chráněné autorské dílo
  • Ukládejte jen to, co opravdu potřebujete a nastavte si retenční politiku dat
  • Osobní výzkum vs. komerční použití: Scrapování veřejných dat pro osobní analýzu nebo akademický výzkum je obecně přijímané. Komerční redistribuce s sebou nese vyšší právní riziko.

Použití nástroje jako Thunderbit (který scrapuje přes tvoji vlastní browser session) vizuálně odpovídá běžnému prohlížení, ale stejné etické zásady platí bez ohledu na nástroj. Pokud chceš jít víc do hloubky a podívat se na právní dopady web scrapingu, máme k tomu samostatný článek.

Tipy a časté chyby

Tip: Začněte vždy s JSON-LD. Než budeš psát složité CSS selektory, zkontroluj, jestli nejsou potřebná data v tagu <script type="application/ld+json">. Je stabilnější, snadněji se parsuje a méně často se rozbije, když Goodreads upraví frontend.

Tip: Používejte dvoufázový postup. Nejprve nasbírej všechny URL knih z list stránek a pak navštiv každou detailní stránku. Scraper se pak snáz obnoví, když spadne uprostřed, a seznam URL si můžeš uložit na disk jako checkpoint.

Chyba: Neošetření chybějících polí. Ne každá stránka knihy má ISBN, žánrové štítky nebo popis. Vždy používej .get() s výchozí hodnotou nebo podmínky if kolem selektorů. Jedna chyba NoneType ti může zničit tříhodinový scraping.

Chyba: Příliš rychlý běh. Vím, že je lákavé nastavit time.sleep(0.5) a projet to co nejrychleji. Goodreads ale začne po zhruba 20–30 rychlých požadavcích vracet 403 a jakmile tě označí, můžeš čekat hodiny nebo měnit IP. Zpoždění 4–5 sekund je ideální kompromis.

Chyba: Věřit starým návodům. Pokud průvodce odkazuje na Goodreads API nebo používá názvy tříd jako .field.value nebo #bookTitle, je nejspíš zastaralý. Před stavbou scraperu si vždy ověř selektory na živé stránce.

Další tipy k výběru správných scrapingových nástrojů a frameworků najdeš v našich průvodcích na nejlepší Python web scraping nástroje a jak scrapovat web v Pythonu.

Závěr a hlavní poznatky

Scraping Goodreads v Pythonu je úplně reálný — jen musíš vědět, kde číhají nástrahy. Stručně:

  • Goodreads API je pryč (od prosince 2020). Scraping je dnes hlavní cesta, jak z platformy získat strukturovaná data o knihách.
  • Prázdné výsledky jsou skoro vždy způsobené obsahem renderovaným JS, zastaralými selektory, chybějícími hlavičkami nebo problémy s autentizací u stránkování — ne tím, že by byl tvůj kód špatně.
  • JSON-LD je tvůj nejlepší přítel pro metadata knih. Je stabilní, strukturované a mění se zřídka.
  • Stránkování u mnoha stránek polic a seznamů po páté stránce vyžaduje autentizaci. Přidej cookie _session_id2.
  • Rate limiting je skutečný. Používej pauzy 3–8 sekund a drž se pod 5 000 požadavků denně.
  • Dvoufázový postup (nejprve URL, potom detailní stránky) je spolehlivější a lze ho snadno obnovit.
  • Pro neprogramátory (nebo každého, kdo si váží odpoledne) Thunderbit zvládne všechno — JS rendering, stránkování, obohacení podstránek i export — zhruba na dvě kliknutí.

Scrapuj zodpovědně, respektuj robots.txt a ať ti knižní data nikdy nevrací jen [].

Často kladené otázky

Dá se ještě použít Goodreads API?

Ne. Goodreads v prosinci 2020 veřejné API zrušilo a nové vývojářské klíče už nevydává. Existující klíče, které byly 30 dní neaktivní, byly automaticky deaktivovány. Pro programový přístup k datům o knihách dnes zbývá web scraping nebo alternativní API (např. Open Library nebo Google Books).

Proč můj Goodreads scraper vrací prázdné výsledky?

Nejčastější příčinou je obsah renderovaný JavaScriptem. Goodreads načítá recenze, rozdělení hodnocení a mnoho detailů přes React/JavaScript, které jednoduchý requests.get() nevidí. U těchto stránek přejdi na Selenium nebo Playwright. Další příčiny jsou zastaralé CSS selektory (Goodreads změnil HTML), chybějící User-Agent hlavičky (spustí 403 blokaci) nebo neautentizované požadavky na stránkované shelf stránky.

Je scrapování Goodreads legální?

Scrapování veřejně dostupných dat pro osobní nebo výzkumné účely je podle současné právní praxe obecně přijímané (hiQ v. LinkedIn, Meta v. Bright Data). Goodreads ale ve svých podmínkách použití automatizovaný sběr dat zakazuje a vždy by sis měl zkontrolovat i robots.txt. Vyhni se komerční redistribuci chráněných textů recenzí a omezuj objem požadavků, abys web zbytečně nezatěžoval.

Jak scrapovat více stránek na Goodreads?

K URL police nebo seznamu přidej ?page=N a projdi čísla stránek ve smyčce. Poslední stránku poznáš podle prázdných výsledků nebo chybějícího odkazu „next“. Důležité: některé shelf stránky vyžadují autentizaci (cookie _session_id2), aby vrátily data i po páté stránce — bez ní se ti tiše budou opakovat data z první stránky.

Lze Goodreads scrapovat i bez programování?

Ano. Thunderbit je Chrome rozšíření, které ti umožní scrapovat Goodreads na dvě kliknutí — AI navrhne datová pole, klikneš na „Scrape“ a výsledek exportuješ přímo do Google Sheets, Excelu, Airtable nebo Notion. Automaticky zvládá obsah renderovaný JavaScriptem, stránkování i obohacení podstránek, bez nutnosti Pythonu nebo programování.

Zjisti více

Shuai Guan
Shuai Guan
CEO ve společnosti Thunderbit | Expert na automatizaci dat s využitím AI Shuai Guan je CEO společnosti Thunderbit a absolvent inženýrského oboru na University of Michigan. Na základě téměř deseti let zkušeností v oblasti technologií a architektury SaaS se zaměřuje na převádění složitých modelů AI do praktických nástrojů pro extrakci dat bez nutnosti programování. Na tomto blogu sdílí nezkreslené a v praxi ověřené poznatky o web scrapingu a automatizačních strategiích, které vám pomohou vytvářet chytřejší datově řízené pracovní postupy. Když zrovna neoptimalizuje datové workflow, věnuje stejný důraz na detail také své vášni pro fotografii.

Vyzkoušej Thunderbit

Získej leady i další data jen na 2 kliknutí. Pohání AI.

Získat Thunderbit Je to zdarma
Vytěž data pomocí AI
Snadno přenes data do Google Sheets, Airtable nebo Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week