Cum să extragi date de pe Goodreads cu Python (fără rezultate goale)

Ultima actualizare pe April 16, 2026

Puține lucruri sunt mai frustrante decât să scrii 30 de linii de Python, să rulezi scraperul pentru Goodreads și să vezi că returnează []. O listă goală. Nimic. Doar tu și cursorul care clipește.

Am văzut scenariul acesta de zeci de ori — în experimentele noastre interne de la , pe forumurile de dezvoltatori și în issue-urile GitHub care se tot adună la proiectele de scraper abandonate. Plângerile sunt aproape mereu aceleași: „secțiunea cu recenziile de sus e goală, îmi arată doar []”, „oricâte numere de pagină încerc, îmi extrage mereu prima pagină”, „codul meu mergea anul trecut, acum nu mai funcționează”. Ca și cum nu era suficient, API-ul Goodreads a fost retras în decembrie 2020, așa că sfatul „folosește API-ul” din tutorialele mai vechi nu te mai ajută deloc.

Dacă vrei azi date structurate despre cărți din Goodreads — titluri, autori, evaluări, recenzii, genuri, ISBN-uri — scraping-ul este principala variantă. Ghidul de față îți arată o metodă completă și funcțională pentru a extrage Goodreads cu Python, inclusiv conținut randat cu JavaScript, paginare, anti-blocare și export. Iar dacă Python nu e pe gustul tău, îți arăt și o alternativă no-code care rezolvă totul în doar câteva clickuri.

Ce înseamnă să extragi date de pe Goodreads și de ce să folosești Python?

Scraping-ul Goodreads înseamnă să extragi automat date despre cărți — titluri, autori, evaluări, număr de recenzii, genuri, ISBN-uri, număr de pagini, data publicării și altele — direct din paginile web Goodreads, folosind cod, în loc să copiezi manual.

Goodreads este una dintre cele mai mari baze de date despre cărți din lume, cu peste și aproximativ . În fiecare lună, peste 18 milioane de cărți ajung pe rafturile „Want to Read”. Tocmai acest tip de date structurate, actualizate constant, îi readuce mereu pe editori, data scientist-ii, librarii și cercetătorii.

Python este limbajul preferat pentru acest tip de muncă — este folosit în aproximativ dintre proiectele de scraping. Librăriile sunt mature (requests, BeautifulSoup, Selenium, Playwright, pandas), sintaxa e prietenoasă pentru începători, iar comunitatea este uriașă.

Dacă nu ai mai extras niciodată date de pe un site, Python este locul ideal de unde să începi.

De ce să extragi Goodreads cu Python? Exemple reale de utilizare

Înainte să intrăm în cod, merită să ne întrebăm: cine are nevoie, de fapt, de aceste date și ce face cu ele?

Caz de utilizareCine beneficiazăCe extragi
Cercetare de piață pentru edituriEdituri, agenți literariGenuri în trend, titluri foarte bine cotate, autori emergenți, evaluările concurenței
Sisteme de recomandare de cărțiData scientist-i, pasionați, dezvoltatori de aplicațiiEvaluări, genuri, rafturi de utilizatori, sentimentul recenziilor
Monitorizarea prețurilor și a stocurilorLibrării onlineTitluri în trend, volum de recenzii, număr de „Want to Read”
Cercetare academicăCercetători, studențiTextul recenziilor, distribuția evaluărilor, clasificări pe genuri
Analiză de lecturăBloggeri de carte, proiecte personaleDate din raftul personal, istoric de lectură, statistici de final de an

Câteva exemple concrete: UCSD Book Graph — unul dintre cele mai citate seturi de date academice în cercetarea sistemelor de recomandare — conține , toate colectate din rafturi Goodreads accesibile public. Mai multe seturi de date de pe Kaggle (goodbooks-10k, Best Books Ever etc.) au pornit din scraping pe Goodreads. Iar un studiu din 2025 publicat în Big Data and Society a compilat computațional pentru a analiza cum influențează recenziile sponsorizate platforma.

Pe partea comercială, Bright Data vinde seturi de date Goodreads pre-extrase cu doar 0,50 USD per 1.000 de înregistrări — dovadă clară că aceste date au valoare reală pe piață.

API-ul Goodreads a dispărut — iată ce l-a înlocuit

Dacă ai căutat recent „Goodreads API”, probabil ai dat peste un tutorial depășit. Pe 8 decembrie 2020, Goodreads a încetat discret să mai emită chei noi de API pentru dezvoltatori. N-a existat niciun articol pe blog, niciun email de anunț — doar un mic banner pe pagina de documentație și mulți dezvoltatori luați prin surprindere.

goodreads-data-access-tools.webp

Efectele au fost imediate. Un dezvoltator, Kyle K, a construit un bot de Discord pentru recomandări de carte — „dintr-odată, PUF, nu mai funcționează”. Altul, Matthew Jones, și-a pierdut accesul la API cu o săptămână înainte de votul pentru Reddit r/Fantasy Stabby Awards, fiind nevoit să revină la Google Forms. O studentă la master, Elena Neacsu, a văzut proiectul de disertație blocat în plin proces de dezvoltare.

Așadar, ce mai rămâne? Peisajul actual arată cam așa:

AbordareDate disponibileUșurință de utilizareLimitări de ratăStare
Goodreads APIMetadate complete, recenziiUșor (era)1 cerere/secundăRetras (dec. 2020) — fără chei noi
Open Library APITitluri, autori, ISBN-uri, coperte (~30M titluri)Ușor1-3 cereri/secundăActiv, gratuit, fără autentificare
Google Books APIMetadate, previzualizăriUșor1.000/zi gratuitActiv (dar lipsesc unele ISBN-uri non-engleze)
Python scraping (requests + BS4)Orice apare în HTML-ul inițialMediuGestionat de tineFuncționează pentru conținut static
Python scraping (Selenium/Playwright)Și conținut randat cu JSMai dificilGestionat de tineNecesar pentru recenzii și unele liste
Thunderbit (extensie Chrome no-code)Orice date vizibile pe paginăFoarte ușor (2 clickuri)Pe bază de crediteActiv — fără Python

Open Library este un complement excelent, mai ales pentru căutări de ISBN și metadate de bază. Dar dacă ai nevoie de evaluări, recenzii, etichete de gen sau număr de „Want to Read”, atunci extragi direct din Goodreads — fie cu Python, fie cu un instrument precum Thunderbit, care poate extrage pagini Goodreads (inclusiv subpagini cu detalii despre carte) cu câmpuri sugerate de AI și export direct în Google Sheets, Notion sau Airtable.

De ce scraperul tău Python pentru Goodreads returnează rezultate goale (și cum repari)

Aceasta este secțiunea pe care mi-aș fi dorit să o am când am început să lucrez cu date Goodreads. Problema „rezultatelor goale” este cea mai frecventă plângere pe forumurile de dezvoltatori și are mai multe cauze distincte — fiecare cu propria soluție.

This paragraph contains content that cannot be parsed and has been skipped.

Conținut randat cu JS: recenziile și evaluările apar goale

Frontend-ul Goodreads este construit pe React. Când faci requests.get() pe o pagină de carte, primești HTML-ul inițial — dar recenziile, distribuția evaluărilor și multe secțiuni de tip „mai multe informații” se încarcă asincron prin JavaScript. Cu alte cuvinte, scraperul tău pur și simplu nu le poate vedea.

Soluția: pentru orice pagină unde ai nevoie de conținut randat cu JS, treci la Selenium sau Playwright. Eu recomand Playwright pentru proiecte noi — este datorită protocolului bazat pe WebSocket și are suport mai bun pentru stealth și async.

troubleshooting-empty-array-causes.webp

Paginare care returnează doar pagina 1

Asta e una înșelătoare. Scrii un loop, crești ?page=N, dar obții aceleași rezultate de fiecare dată. Pe Goodreads, paginile de raft returnează în tăcere conținutul din pagina 1 indiferent de parametrul ?page= dacă nu ești autentificat. Nicio eroare, nicio redirecționare — doar aceeași primă pagină iar și iar.

Soluția: include un cookie de sesiune autentificat (în special _session_id2). Mai multe detalii în secțiunea despre paginare de mai jos.

Cod care mergea anul trecut, dar acum eșuează

Goodreads schimbă periodic numele claselor HTML și structura paginilor. Popularul repo maria-antoniak/goodreads-scraper de pe GitHub afișează acum un mesaj permanent: „This project is unmaintained and no longer functioning.” Soluția este să folosești selectori mai rezilienți — date structurate JSON-LD (care respectă standardul schema.org și se schimbă rar) sau atribute data-testid, în locul unor clase fragile.

Erori 403 sau blocare

Librăria requests din Python are o amprentă TLS diferită față de Chrome. Chiar dacă folosești un User-Agent de Chrome, sistemele de detecție a roboților precum AWS WAF (pe care Goodreads îl folosește, fiind subsidiară Amazon) pot observa nepotrivirea. Soluția: adaugă headere realiste de browser, introduce întârzieri time.sleep() de 3-8 secunde între cereri și ia în calcul curl_cffi pentru potrivirea amprentei TLS dacă faci scraping la scară mare.

Pagini de raft și listă care cer autentificare

Unele pagini Goodreads de raft și listă cer autentificare pentru a afișa conținut complet, mai ales după pagina 5. Folosește requests.Session() cu cookie-uri exportate din browser sau Selenium/Playwright cu un profil conectat. Thunderbit gestionează asta natural, deoarece rulează în propriul tău browser Chrome deja autentificat.

Înainte să începi

  • Dificultate: Intermediară (presupune cunoștințe de bază de Python)
  • Timp necesar: ~20-30 de minute pentru tot tutorialul
  • Ce îți trebuie:
    • Python 3.8+
    • Browser Chrome (pentru inspectarea în DevTools și pentru Selenium/Playwright)
    • Librării: requests, beautifulsoup4, selenium sau playwright, pandas
    • (Opțional) gspread pentru export în Google Sheets
    • (Opțional) pentru alternativa no-code

goodreads-scraping-flow.webp

Pasul 1: Configurează mediul Python

Instalează librăriile necesare. Deschide terminalul și rulează:

1pip install requests beautifulsoup4 selenium pandas lxml

Dacă preferi Playwright (recomandat pentru proiecte noi):

1pip install playwright
2playwright install chromium

Pentru export în Google Sheets (opțional):

1pip install gspread oauth2client

Asigură-te că folosești Python 3.8 sau mai nou. Poți verifica cu python --version.

După instalare, ar trebui să poți importa toate librăriile fără erori. Încearcă python -c "import requests, bs4, pandas; print('Ready')" pentru confirmare.

Pasul 2: Trimite prima cerere cu headere corecte

Deschide în browser o pagină de listă sau de shelf Goodreads — de exemplu, https://www.goodreads.com/list/show/1.Best_Books_Ever. Acum să preluăm acea pagină cu Python.

1import requests
2from bs4 import BeautifulSoup
3> This paragraph contains content that cannot be parsed and has been skipped.
4url = "https://www.goodreads.com/list/show/1.Best_Books_Ever"
5response = requests.get(url, headers=headers, timeout=15)
6print(f"Status: {response.status_code}")

Ar trebui să vezi Status: 200. Dacă primești 403, verifică din nou headerele — AWS WAF de la Goodreads verifică dacă User-Agent-ul pare real și respinge cererile prea simple. Headerele de mai sus mimează o sesiune autentică de Chrome.

Pasul 3: Inspectează pagina și identifică selectori corecți

Deschide Chrome DevTools (F12) pe pagina de listă Goodreads. Dă click dreapta pe un titlu de carte și alege „Inspect.” Vei vedea structura DOM pentru fiecare element de carte.

Pentru paginile de listă, fiecare carte este de obicei încadrată într-un element <tr> cu itemtype="http://schema.org/Book". În interior vei găsi:

  • Titlu: a.bookTitle (textul linkului este titlul, iar href oferă URL-ul cărții)
  • Autor: a.authorName
  • Evaluare: span.minirating (conține media și numărul de evaluări)
  • Imagine de copertă: img din rândul cărții

Pentru paginile individuale de detaliu ale cărții, sari peste selectorii CSS și mergi direct la JSON-LD. Goodreads include date structurate într-un tag <script type="application/ld+json"> care respectă formatul schema.org Book. Este mult mai stabil decât clasele HTML, pe care Goodreads le schimbă după bunul plac.

Pasul 4: Extrage datele dintr-o singură pagină de listă

Să parsăm pagina de listă și să extragem informațiile de bază pentru fiecare carte:

1import requests
2from bs4 import BeautifulSoup
3> This paragraph contains content that cannot be parsed and has been skipped.
4url = "https://www.goodreads.com/list/show/1.Best_Books_Ever"
5response = requests.get(url, headers=headers, timeout=15)
6soup = BeautifulSoup(response.text, "lxml")
7books = []
8rows = soup.select('tr[itemtype="http://schema.org/Book"]')
9for row in rows:
10    title_tag = row.select_one("a.bookTitle")
11    author_tag = row.select_one("a.authorName")
12    rating_tag = row.select_one("span.minirating")
13    title = title_tag.get_text(strip=True) if title_tag else ""
14    book_url = "https://www.goodreads.com" + title_tag["href"] if title_tag else ""
15    author = author_tag.get_text(strip=True) if author_tag else ""
16    rating_text = rating_tag.get_text(strip=True) if rating_tag else ""
17> This paragraph contains content that cannot be parsed and has been skipped.
18print(f"Am găsit {len(books)} cărți pe pagina 1")
19for b in books[:3]:
20    print(b)

Ar trebui să vezi aproximativ 100 de cărți pe fiecare pagină de listă. Fiecare element va avea un titlu, un autor, un șir de evaluare de tipul „4.28 avg rating — 9,031,257 ratings” și un URL către pagina de detalii.

Pasul 5: Extrage subpagini pentru informații detaliate despre carte

Pagina de listă îți oferă baza, dar adevărata valoare — ISBN, descriere completă, etichete de gen, număr de pagini, data publicării — se află pe pagina individuală a fiecărei cărți. Aici strălucește JSON-LD.

1import json
2import time
3def scrape_book_detail(book_url, headers):
4    """Accesează o pagină de carte și extrage metadatele detaliate prin JSON-LD."""
5    resp = requests.get(book_url, headers=headers, timeout=15)
6    if resp.status_code != 200:
7        return {}
8> This paragraph contains content that cannot be parsed and has been skipped.
9    data = json.loads(script.string)
10    agg = data.get("aggregateRating", {})
11    # Etichetele de gen nu sunt în JSON-LD; folosim fallback din HTML
12    genres = [g.get_text(strip=True) for g in soup.select('span.BookPageMetadataSection__genreButton a span')]
13> This paragraph contains content that cannot be parsed and has been skipped.
14# Exemplu: îmbogățim primele 3 cărți
15for book in books[:3]:
16    details = scrape_book_detail(book["book_url"], headers)
17    book.update(details)
18    print(f"Extras: {book['title']} — ISBN: {book.get('isbn', 'N/A')}")
19    time.sleep(4)  # respectă limitele de rată

Adaugă un time.sleep() de 3-8 secunde între cereri. Goodreads începe să aplice limitări la aproximativ 20-30 de cereri pe minut dintr-un singur IP, iar dacă mergi prea repede vei vedea 403-uri sau CAPTCHA-uri.

Această abordare în doi pași — întâi colectezi toate URL-urile cărților din paginile de listă, apoi vizitezi fiecare pagină de detalii — este mai fiabilă și mai ușor de reluat dacă procesul se întrerupe. Este strategia folosită de majoritatea scraperelor de Goodreads care funcționează bine.

Notă: poate face asta automat cu funcția de scraping pe subpagini. AI-ul vizitează fiecare pagină de detalii și îți îmbogățește tabelul cu ISBN, descriere, genuri și multe altele — fără cod, fără loop-uri, fără timpi de așteptare.

Pasul 6: Gestionează conținutul randat cu JavaScript folosind Selenium

Pentru paginile în care conținutul necesar se încarcă prin JavaScript — recenzii, distribuția evaluărilor, secțiuni „more details” — ai nevoie de un instrument de automatizare a browserului. Iată un exemplu cu 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# Așteaptă încărcarea recenziilor
14try:
15    WebDriverWait(driver, 10).until(
16        EC.presence_of_element_located((By.CSS_SELECTOR, "article.ReviewCard"))
17    )
18except:
19    print("Recenziile nu s-au încărcat — pagina poate necesita autentificare sau time-out JS")
20# Acum parsează pagina randată complet
21page_source = driver.page_source
22soup = BeautifulSoup(page_source, "lxml")
23> This paragraph contains content that cannot be parsed and has been skipped.
24driver.quit()

Când folosești Selenium vs. requests:

  • Folosește requests + BeautifulSoup pentru metadate despre cărți (JSON-LD), pagini de listă, pagini de raft (pagina 1) și datele Choice Awards
  • Folosește Selenium sau Playwright pentru recenzii, distribuția evaluărilor și orice conținut care nu apare în HTML-ul brut

Playwright este, în general, alegerea mai bună pentru proiecte noi — mai rapid, consum mai mic de memorie, presetări mai bune pentru stealth. Dar Selenium are o comunitate mai mare și mai multe exemple de cod existente pentru Goodreads.

Paginare care chiar funcționează: cum extragi liste complete de pe Goodreads

Paginarea este punctul de eșec cel mai frecvent pentru scraper-ele Goodreads, iar eu nu am găsit niciun tutorial competitor care să o acopere corect. Așa trebuie făcut.

Cum funcționează URL-urile de paginare Goodreads

Goodreads folosește un parametru simplu ?page=N pentru majoritatea paginilor paginate:

  • Liste: https://www.goodreads.com/list/show/1.Best_Books_Ever?page=2
  • Rafturi: https://www.goodreads.com/shelf/show/thriller?page=2
  • Căutare: https://www.goodreads.com/search?q=fantasy&page=2

Fiecare pagină de listă afișează de obicei 100 de cărți. Rafturile afișează 50 per pagină.

Cum scrii un loop de paginare care știe când să se oprească

1import time
2all_books = []
3base_url = "https://www.goodreads.com/list/show/1.Best_Books_Ever"
4for page_num in range(1, 50):  # limită de siguranță la 50 de pagini
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"Pagina {page_num}: status {resp.status_code}, opresc.")
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"Pagina {page_num}: nu s-au găsit cărți, am ajuns la final.")
14        break
15> This paragraph contains content that cannot be parsed and has been skipped.
16    print(f"Pagina {page_num}: am extras {len(rows)} cărți (total: {len(all_books)})")
17    time.sleep(5)  # întârziere de 5 secunde între pagini
18print(f"\nGata. Total cărți colectate: {len(all_books)}")

Poți detecta ultima pagină verificând dacă lista de rezultate este goală (nu există elemente tr[itemtype="http://schema.org/Book"]) sau dacă lipsește linkul „next” (a.next_page).

Caz special: autentificare necesară după pagina 5

Acesta este capcana în care cad aproape toți: unele pagini Goodreads de raft și listă returnează în tăcere conținutul din pagina 1 dacă ceri pagina 6+ fără autentificare. Nicio eroare, nicio redirecționare — doar aceleași date repetate.

Ca să rezolvi, exportă cookie-ul _session_id2 din browser (cu o extensie de export cookie-uri sau din Chrome DevTools > Application > Cookies) și include-l în cereri:

1session = requests.Session()
2session.headers.update(headers)
3session.cookies.set("_session_id2", "VALOAREA_COOKIE-ULUI_TĂU_DE_SESIUNE", domain=".goodreads.com")
4# Folosește session.get() în loc de requests.get()
5resp = session.get(f"{base_url}?page=6", timeout=15)

Thunderbit gestionează nativ atât paginarea pe click, cât și infinite scroll, fără cod și fără administrare de cookie-uri. Dacă logica de paginare îți tot cedează, merită luat în calcul.

Scriptul complet, gata de copiat și rulat

Mai jos ai scriptul complet, consolidat. Gestionează headerele, paginarea, scraping-ul subpaginilor prin JSON-LD, limitarea ratei și exportul CSV. L-am testat pe pagini Goodreads active, la mijlocul lui 2025.

1"""
2goodreads_scraper.py — Extrage o listă Goodreads cu paginare și îmbogățirea detaliilor despre carte.
3Utilizare: python goodreads_scraper.py
4Ieșire: goodreads_books.csv
5"""
6> This paragraph contains content that cannot be parsed and has been skipped.
7> This paragraph contains content that cannot be parsed and has been skipped.
8> This paragraph contains content that cannot be parsed and has been skipped.
9> This paragraph contains content that cannot be parsed and has been skipped.
10def main():
11    all_books = []
12    # --- Pasul 1: colectează URL-urile cărților din paginile de listă ---
13    for page in range(1, MAX_PAGES + 1):
14        url = f"{BASE_URL}?page={page}"
15        page_books = scrape_listing_page(url)
16        if not page_books:
17            print(f"Pagina {page}: goală — opresc paginarea.")
18            break
19        all_books.extend(page_books)
20        print(f"Pagina {page}: {len(page_books)} cărți (total: {len(all_books)})")
21        time.sleep(DELAY_LISTING)
22    # --- Pasul 2: îmbogățește fiecare carte cu datele din pagina de detaliu ---
23    for i, book in enumerate(all_books):
24        details = scrape_book_detail(book["book_url"])
25        book.update(details)
26        print(f"[{i+1}/{len(all_books)}] {book['title']} — ISBN: {book.get('isbn', 'N/A')}")
27        time.sleep(DELAY_DETAIL)
28    # --- Export în CSV ---
29    if all_books:
30        fieldnames = list(all_books[0].keys())
31        with open(OUTPUT_FILE, "w", newline="", encoding="utf-8") as f:
32            writer = csv.DictWriter(f, fieldnames=fieldnames)
33            writer.writeheader()
34            writer.writerows(all_books)
35        print(f"\nAm salvat {len(all_books)} cărți în {OUTPUT_FILE}")
36    else:
37        print("Nu s-au extras cărți.")
38if __name__ == "__main__":
39    main()

Cu MAX_PAGES = 3, scriptul colectează aproximativ 300 de cărți din lista „Best Books Ever”, vizitează pagina de detalii a fiecărei cărți și scrie totul într-un CSV. Pe calculatorul meu, durează aproximativ 25 de minute (mai ales din cauza întârzierii de 4 secunde între cererile către paginile de detaliu). CSV-ul rezultat va avea coloane precum title, author, book_url, isbn, pages, avg_rating, rating_count, review_count, description, genres, language, format și published.

Export dincolo de CSV: Google Sheets cu gspread

Dacă vrei datele în Google Sheets, în loc de CSV sau pe lângă CSV, adaugă asta după exportul CSV:

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("Datele au fost trimise în Google Sheets.")

Ai nevoie de un cont de service Google Cloud cu API-urile Sheets și Drive activate. explică setup-ul în aproximativ 5 minute. Folosește operații batch (append_rows() cu o listă de liste) dacă trimiți mai mult de câteva sute de rânduri — limita Google este de 300 de cereri la 60 de secunde per proiect.

Desigur, dacă tot acest setup ți se pare prea mult, Thunderbit exportă în Google Sheets, Airtable, Notion, Excel, CSV și JSON cu — fără instalări de librării, fără fișier de credențiale, fără cote API.

Alternativa no-code: extrage Goodreads cu Thunderbit

Nu toată lumea vrea să întrețină un script Python. Poate ești editor și faci o analiză punctuală de piață, sau un blogger de carte care vrea doar un tabel cu bestseller-ele anului. Exact pentru astfel de situații am construit Thunderbit.

Cum să extragi Goodreads cu Thunderbit

  1. Instalează extensia Chrome Thunderbit din și deschide o pagină Goodreads de listă, shelf sau rezultate de căutare.
  2. Apasă „AI Suggest Fields” în sidebar-ul Thunderbit. AI-ul citește pagina și propune coloane — de obicei titlu, autor, URL-ul imaginii de copertă și linkul cărții.
  3. Apasă „Scrape” — datele sunt extrase într-un tabel structurat în câteva secunde.
  4. Exportă în Google Sheets, Excel, Airtable, Notion, CSV sau JSON.

Pentru date detaliate despre carte (ISBN, descriere, genuri, număr de pagini), funcția de scraping pe subpagini a Thunderbit vizitează automat fiecare pagină de detalii și îmbogățește tabelul — fără loop-uri, fără timpi de așteptare, fără debugging.

Thunderbit gestionează și listele paginate nativ. Îi spui să apese „Next” sau să deruleze, iar el colectează datele de pe toate paginile fără niciun cod.

Compromisul este simplu: scriptul Python îți oferă control total și este gratuit, în afară de timpul tău, în timp ce Thunderbit schimbă puțină flexibilitate pe economie masivă de timp și zero mentenanță. Pentru o listă de 300 de cărți, scriptul Python durează ~25 de minute de execuție, plus timpul petrecut să-l scrii și depanezi. Thunderbit obține aceleași date în aproximativ 3 minute, cu două clickuri.

Extrage Goodreads în mod responsabil: robots.txt, Termeni de utilizare și etică

Aici merită un răspuns direct, nu un paragraf de avertisment pus de formă.

Ce spune de fapt robots.txt al Goodreads

Fișierul robots.txt live al Goodreads este surprinzător de specific. Paginile de detaliu ale cărților (/book/show/), listele publice (/list/show/), rafturile publice (/shelf/show/) și paginile autorilor (/author/show/) nu sunt blocate. Ce este blocat: /api, /book/reviews/, /review/list, /review/show, /search și câteva alte căi. GPTBot și CCBot (Common Crawl) sunt blocate complet cu Disallow: /. Există o directivă Crawl-delay: 5 pentru bingbot, dar nu există o întârziere globală.

Termenii de utilizare Goodreads, pe scurt

Termenii de utilizare (ultima revizuire: 28 aprilie 2021) interzic „orice utilizare a data mining-ului, roboților sau a unor instrumente similare de colectare și extragere de date”. Este o formulare amplă și merită tratată cu seriozitate — dar instanțele au decis constant că simpla încălcare a ToS nu echivalează cu „acces neautorizat” de natură penală. Hotărârea a spus că „criminalizarea încălcărilor termenilor de utilizare riscă să transforme fiecare site web într-o jurisdicție penală proprie”.

Cele mai bune practici

  • Introdu întârzieri între cereri: 3-8 secunde între request-uri (robots.txt-ul Goodreads sugerează chiar 5 secunde pentru boți)
  • Rămâi sub 5.000 de cereri pe zi de la un singur IP
  • Extrage doar pagini public accesibile — evită scraping-ul masiv al datelor disponibile doar după autentificare
  • Nu redistribui comercial textul brut al recenziilor — recenziile sunt opere creative protejate prin drepturi de autor
  • Stochează doar ce ai nevoie și stabilește o politică de retenție a datelor
  • Cercetare personală vs. utilizare comercială: scraping-ul datelor publice pentru analiză personală sau cercetare academică este, în general, acceptat. Riscul juridic crește atunci când faci redistribuire comercială.

Folosirea unui instrument precum Thunderbit (care extrage prin propria ta sesiune de browser) face interacțiunea să arate exact ca browsing-ul normal, dar aceleași principii etice rămân valabile indiferent de instrumentul ales. Dacă vrei să mergi mai departe cu , am tratat separat subiectul.

Sfaturi și capcane frecvente

Sfat: începe mereu cu JSON-LD. Înainte să scrii selectori CSS complicați, verifică dacă datele de care ai nevoie sunt în tagul <script type="application/ld+json">. Este mai stabil, mai ușor de parsare și are șanse mai mici să se strice când Goodreads își actualizează frontend-ul.

Sfat: folosește strategia în doi pași. Mai întâi colectează toate URL-urile cărților din paginile de listă, apoi vizitează fiecare pagină de detalii. Asta îți face scraperul mai ușor de reluat dacă se oprește la jumătate și poți salva lista de URL-uri pe disc ca punct de control.

Capcană: ignori câmpurile lipsă. Nu toate paginile de carte au ISBN, etichete de gen sau descriere. Folosește mereu .get() cu o valoare implicită sau verifică selectorii cu if. O singură eroare de tip NoneType îți poate opri un run de 3 ore.

Capcană: rulezi prea repede. Știu, e tentant să pui time.sleep(0.5) și să mergi în viteză. Dar Goodreads începe să returneze 403 după aproximativ 20-30 de cereri rapide, iar odată ce ești semnalat, s-ar putea să trebuiască să aștepți ore sau să schimbi IP-ul. O întârziere de 4-5 secunde este punctul optim.

Capcană: ai încredere în tutoriale vechi. Dacă un ghid face referire la API-ul Goodreads sau folosește nume de clase precum .field.value ori #bookTitle, probabil este depășit. Verifică întotdeauna selectorii pe pagina live înainte să îți construiești scraperul.

Pentru mai multe informații despre alegerea instrumentelor și framework-urilor potrivite de scraping, vezi ghidurile noastre despre și .

Concluzie și idei-cheie

Scraping-ul Goodreads cu Python este perfect realizabil — trebuie doar să știi unde sunt capcanele. Pe scurt:

  • API-ul Goodreads a dispărut (din decembrie 2020). Scraping-ul este principala metodă de a obține date structurate despre cărți de pe platformă.
  • Rezultatele goale sunt aproape întotdeauna cauzate de conținut randat cu JS, selectori învechiți, headere lipsă sau probleme de autentificare la paginare — nu de faptul că „codul e greșit”.
  • JSON-LD este cel mai bun prieten al tău pentru metadatele cărților. Este stabil, structurat și se schimbă rar.
  • Paginarea cere autentificare pentru multe pagini de raft și listă după pagina 5. Include cookie-ul _session_id2.
  • Limitarea ratei este reală. Folosește întârzieri de 3-8 secunde și rămâi sub 5.000 de cereri pe zi.
  • Strategia în doi pași (colectezi URL-urile, apoi extragi paginile de detalii) este mai fiabilă și mai ușor de reluat.
  • Pentru cei care nu codează (sau pentru oricine ține la după-amiaza lui), face totul — randare JS, paginare, îmbogățire pe subpagini și export — în aproximativ două clickuri.

Extrage responsabil, respectă robots.txt și speră ca datele tale despre cărți să vină mereu cu mai mult decât [].

Întrebări frecvente

Mai poți folosi API-ul Goodreads?

Nu. Goodreads a retras API-ul public în decembrie 2020 și nu mai emite chei noi pentru dezvoltatori. Cheile existente care au fost inactive 30 de zile au fost dezactivate automat. Web scraping-ul sau API-uri alternative (precum Open Library sau Google Books) sunt opțiunile actuale pentru acces programatic la date despre cărți.

De ce scraperul meu Goodreads returnează rezultate goale?

Cea mai comună cauză este conținutul randat cu JavaScript. Goodreads încarcă recenziile, distribuția evaluărilor și multe secțiuni de detaliu prin React/JavaScript, pe care un simplu requests.get() nu le vede. Folosește Selenium sau Playwright pentru acele pagini. Alte cauze includ selectori CSS învechiți (Goodreads a schimbat HTML-ul), headere User-Agent lipsă (care declanșează blocarea 403) sau cereri neautentificate pe paginile de raft cu paginare.

Extragerea datelor public disponibile pentru uz personal sau de cercetare este, în general, acceptată conform precedentelor juridice actuale (hiQ v. LinkedIn, Meta v. Bright Data). Totuși, termenii de utilizare Goodreads interzic colectarea automată de date, iar robots.txt trebuie verificat întotdeauna. Evită redistribuirea comercială a textului recenziilor protejate prin drepturi de autor și limitează volumul cererilor pentru a nu încărca inutil resursele site-ului.

Cum extrag mai multe pagini pe Goodreads?

Adaugă ?page=N la URL-ul de raft sau listă și rulează un loop peste numerele de pagină. Verifică dacă rezultatele sunt goale sau dacă lipsește linkul „next” pentru a detecta ultima pagină. Important: unele pagini de raft cer autentificare (cookie-ul _session_id2) pentru a returna rezultate după pagina 5 — fără el, vei primi în tăcere datele din pagina 1 repetate.

Pot extrage Goodreads fără să scriu cod?

Da. este o extensie Chrome care îți permite să extragi Goodreads în două clickuri — AI-ul sugerează câmpurile de date, tu apeși „Scrape” și exporți direct în Google Sheets, Excel, Airtable sau Notion. Gestionează automat conținutul randat cu JavaScript, paginarea și îmbogățirea subpaginilor, fără Python sau cod.

Află mai multe

Cuprins

Încearcă Thunderbit

Extrage leaduri și alte date în doar 2 clicuri. Alimentat de AI.

Obține Thunderbit Este gratuit
Extrage date folosind AI
Transferă ușor datele în Google Sheets, Airtable sau Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week