Comment extraire Goodreads avec Python (et en finir avec les résultats vides)

Dernière mise à jour le April 16, 2026

Il y a peu de choses plus décourageantes que d’écrire 30 lignes de Python, de lancer votre scraper Goodreads… et de voir revenir []. Une liste vide. Rien. Juste vous et votre curseur qui clignote.

Je vois ce scénario revenir encore et encore — dans nos propres tests internes chez , sur les forums de développeurs, et dans les issues GitHub qui s’accumulent sur des dépôts de scrapers abandonnés. Les plaintes sont presque toujours les mêmes : « la section des meilleurs avis est vide, ça m’affiche juste [] », « quel que soit le numéro de page, je scrape toujours la première », « mon code fonctionnait l’an dernier, maintenant il est cassé ». Et pour compliquer les choses, l’API Goodreads a été abandonnée en décembre 2020, donc le conseil “utilisez simplement l’API” que l’on trouve dans les anciens tutoriels ne mène nulle part.

Si vous voulez aujourd’hui des données structurées sur les livres depuis Goodreads — titres, auteurs, notes, avis, genres, ISBN — le scraping reste la meilleure solution. Ce guide vous montre une méthode complète et fonctionnelle pour extraire Goodreads avec Python, en couvrant le contenu rendu par JavaScript, la pagination, la protection contre les blocages et l’export. Et si Python n’est pas votre truc, je vous montrerai aussi une alternative sans code qui fait le travail en deux clics environ.

Qu’est-ce que le scraping Goodreads (et pourquoi le faire avec Python) ?

Le scraping Goodreads consiste à extraire automatiquement des données sur les livres — titres, auteurs, notes, nombre d’avis, genres, ISBN, nombre de pages, date de publication, etc. — depuis les pages Goodreads, à l’aide de code plutôt que par copier-coller manuel.

Goodreads est l’une des plus grandes bases de données sur les livres au monde, avec plus de et environ . Plus de 18 millions de livres sont ajoutés chaque mois aux étagères “Want to Read”. Ce type de données structurées et constamment mises à jour explique pourquoi éditeurs, data scientists, libraires et chercheurs y reviennent sans cesse.

Python est le langage de référence pour ce type de projet — il alimente environ de tous les projets de scraping. Les bibliothèques sont mûres (requests, BeautifulSoup, Selenium, Playwright, pandas), la syntaxe est accessible aux débutants, et la communauté est immense.

Si vous n’avez encore jamais scrapé un site web, Python est un excellent point de départ.

Pourquoi extraire Goodreads avec Python ? Cas d’usage concrets

Avant d’entrer dans le code, demandons-nous : qui a vraiment besoin de ces données, et qu’en fait-on ?

Cas d’usageQui en profiteCe que vous extrayez
Étude de marché pour l’éditionÉditeurs, agents littérairesGenres en vogue, titres les mieux notés, auteurs émergents, notes des concurrents
Systèmes de recommandation de livresData scientists, passionnés, créateurs d’appsNotes, genres, étagères utilisateurs, sentiment des avis
Suivi des prix et des stocksLibraires e-commerceTitres tendance, volume d’avis, nombre de “Want to Read”
Recherche académiqueChercheurs, étudiantsTexte des avis, répartition des notes, classification des genres
Analyse de lectureBook bloggers, projets personnelsDonnées d’étagères personnelles, historique de lecture, statistiques annuelles

Quelques exemples concrets : le UCSD Book Graph — l’un des jeux de données académiques les plus cités en recherche sur les recommandations — contient , tous collectés à partir d’étagères Goodreads accessibles publiquement. Plusieurs jeux de données Kaggle (goodbooks-10k, Best Books Ever, etc.) proviennent à l’origine de scraping Goodreads. Et une étude de 2025 dans Big Data and Society a constitué de manière computationnelle pour analyser l’impact des avis sponsorisés sur la plateforme.

Côté commercial, Bright Data vend des jeux de données Goodreads pré-extraits à partir de 0,50 $ pour 1 000 enregistrements — preuve que ces données ont une vraie valeur marchande.

L’API Goodreads n’existe plus — voici ce qui la remplace

Si vous avez cherché “Goodreads API” récemment, vous êtes probablement tombé sur un tutoriel dépassé. Le 8 décembre 2020, Goodreads a discrètement cessé d’attribuer de nouvelles clés API aux développeurs. Aucun billet de blog, aucun e-mail massif — juste une petite bannière sur la page de documentation et beaucoup de développeurs perplexes.

goodreads-data-access-tools.webp

Les conséquences ont été immédiates. Un développeur, Kyle K, avait créé un bot Discord pour partager des recommandations de lecture — « soudain, POUF, ça s’arrête de fonctionner ». Un autre, Matthew Jones, a perdu l’accès à l’API une semaine avant le vote des Reddit r/Fantasy Stabby Awards, ce qui l’a obligé à revenir à Google Forms. Une étudiante en master, Elena Neacsu, a vu son projet de mémoire dérailler en plein développement.

Alors, que reste-t-il ? Le paysage actuel ressemble à ceci :

ApprocheDonnées disponiblesFacilité d’utilisationLimites de débitStatut
API GoodreadsMétadonnées complètes, avisFacile (c’était le cas)1 requête/secAbandonnée (déc. 2020) — aucune nouvelle clé
Open Library APITitres, auteurs, ISBN, couvertures (~30 M de titres)Facile1 à 3 req/secActive, gratuite, sans authentification
Google Books APIMétadonnées, aperçusFacile1 000/jour gratuitActive (lacunes d’ISBN non anglophones)
Scraping Python (requests + BS4)Tout ce qui se trouve dans le HTML initialMoyenÀ gérer soi-mêmeFonctionne pour le contenu statique
Scraping Python (Selenium/Playwright)Aussi le contenu rendu par JSPlus difficileÀ gérer soi-mêmeRequis pour les avis, certaines listes
Thunderbit (extension Chrome sans code)Toute donnée visible de la pageTrès facile (2 clics)Basé sur des créditsActif — pas besoin de Python

Open Library est un excellent complément, en particulier pour les recherches d’ISBN et les métadonnées de base. Mais si vous avez besoin des notes, des avis, des tags de genre ou du nombre de “Want to Read”, alors vous devez extraire directement Goodreads — soit avec Python, soit avec un outil comme Thunderbit, capable de scraper les pages Goodreads (y compris les sous-pages de détails des livres) avec des champs suggérés par l’IA et un export direct vers Google Sheets, Notion ou Airtable.

Pourquoi votre scraper Goodreads en Python renvoie des résultats vides (et comment corriger ça)

C’est la section que j’aurais voulu lire quand j’ai commencé à travailler avec les données Goodreads. Le problème des “résultats vides” est de loin la plainte la plus fréquente sur les forums de développeurs, et il a plusieurs causes distinctes — chacune avec sa propre solution.

SymptômeCause racineSolution
Les avis/notes renvoient []Contenu rendu par JS (React / chargement différé)Utiliser Selenium ou Playwright au lieu de requests
Ne scrape toujours que la page 1Paramètres de pagination ignorés ou gérés par JSPasser correctement ?page=N ; utiliser l’automatisation du navigateur pour le scroll infini
Le code fonctionnait l’an dernier, maintenant il échoueGoodreads a changé les noms de classes HTMLUtiliser des sélecteurs plus robustes (JSON-LD, attributs data-testid)
Erreurs 403 / blocage après quelques requêtesEn-têtes manquants / requêtes trop rapidesAjouter User-Agent, time.sleep(), faire tourner les proxys
Mur de connexion sur les pages d’étagères / listesCookie / session requisUtiliser requests.Session() avec cookies ou scraper via navigateur

Contenu rendu par JavaScript : les avis et les notes apparaissent vides

Goodreads utilise une interface front-end basée sur React. Quand vous faites un requests.get() sur une page de livre, vous récupérez le HTML initial — mais les avis, les distributions de notes et de nombreuses sections “plus d’infos” se chargent ensuite via JavaScript. Votre scraper ne peut tout simplement pas les voir.

La solution : pour toute page où vous avez besoin de contenu rendu par JS, basculez vers Selenium ou Playwright. Pour de nouveaux projets, je recommande Playwright — il est grâce à son protocole basé sur WebSocket, et il offre une meilleure furtivité native ainsi qu’un bon support asynchrone.

troubleshooting-empty-array-causes.webp

Une pagination qui ne renvoie que la page 1

Celui-là est piégeux. Vous écrivez une boucle, incrémentez ?page=N, et vous obtenez quand même les mêmes résultats à chaque fois. Sur Goodreads, les pages d’étagères renvoient silencieusement le contenu de la page 1, quel que soit le paramètre ?page=, si vous n’êtes pas authentifié. Pas d’erreur, pas de redirection — juste la même première page encore et encore.

La solution : inclure un cookie de session authentifié (en particulier _session_id2). Nous y revenons dans la section pagination plus bas.

Un code qui fonctionnait l’an dernier et ne marche plus maintenant

Goodreads modifie périodiquement les noms des classes HTML et la structure des pages. Le dépôt populaire maria-antoniak/goodreads-scraper sur GitHub affiche désormais un avertissement permanent : « This project is unmaintained and no longer functioning. » La solution consiste à utiliser des sélecteurs plus robustes — des données structurées JSON-LD (conformes au standard schema.org et rarement modifiées) ou des attributs data-testid plutôt que des noms de classes fragiles.

Erreurs 403 ou blocages

La bibliothèque requests de Python a une empreinte TLS différente de celle de Chrome. Même avec une chaîne User-Agent de Chrome, des systèmes de détection de robots comme AWS WAF (utilisé par Goodreads, filiale d’Amazon) peuvent repérer la différence. La solution : ajouter des en-têtes de navigateur réalistes, insérer des pauses time.sleep() de 3 à 8 secondes entre les requêtes, et envisager curl_cffi pour faire correspondre l’empreinte TLS si vous scrapez à grande échelle.

Murs de connexion sur les pages d’étagères et de listes

Certaines pages d’étagères et de listes Goodreads exigent une authentification pour afficher tout le contenu, notamment au-delà de la page 5. Utilisez requests.Session() avec des cookies exportés depuis votre navigateur, ou passez par Selenium/Playwright avec un profil connecté. Thunderbit gère cela naturellement puisqu’il s’exécute dans votre propre navigateur Chrome déjà connecté.

Avant de commencer

  • Niveau requis : intermédiaire (bases de Python supposées acquises)
  • Temps nécessaire : environ 20 à 30 minutes pour le tutoriel complet
  • Ce qu’il vous faut :
    • Python 3.8+
    • Navigateur Chrome (pour l’inspection dans DevTools et Selenium/Playwright)
    • Bibliothèques : requests, beautifulsoup4, selenium ou playwright, pandas
    • (Optionnel) gspread pour l’export vers Google Sheets
    • (Optionnel) pour l’alternative sans code

goodreads-scraping-flow.webp

Étape 1 : configurer votre environnement Python

Installez les bibliothèques nécessaires. Ouvrez votre terminal et exécutez :

1pip install requests beautifulsoup4 selenium pandas lxml

Si vous préférez Playwright (recommandé pour les nouveaux projets) :

1pip install playwright
2playwright install chromium

Pour l’export vers Google Sheets (optionnel) :

1pip install gspread oauth2client

Assurez-vous d’utiliser Python 3.8 ou une version supérieure. Vous pouvez vérifier avec python --version.

Après l’installation, vous devriez pouvoir importer toutes les bibliothèques sans erreur. Essayez python -c "import requests, bs4, pandas; print('Ready')" pour confirmer.

Étape 2 : envoyer votre première requête avec les bons en-têtes

Ouvrez dans votre navigateur une page de liste ou d’étagère Goodreads — par exemple, https://www.goodreads.com/list/show/1.Best_Books_Ever. Maintenant, récupérons cette page avec Python.

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

Vous devriez voir Status: 200. Si vous obtenez 403, revérifiez vos en-têtes — AWS WAF de Goodreads vérifie un User-Agent réaliste et rejettera les requêtes trop minimalistes. Les en-têtes ci-dessus imitent une vraie session Chrome.

Étape 3 : inspecter la page et identifier les bons sélecteurs

Ouvrez Chrome DevTools (F12) sur la page de liste Goodreads. Faites un clic droit sur un titre de livre puis choisissez “Inspecter”. Vous verrez la structure DOM de chaque entrée.

Sur les pages de liste, chaque livre est généralement enveloppé dans un élément <tr> avec itemtype="http://schema.org/Book". À l’intérieur, vous trouverez :

  • Titre : a.bookTitle (le texte du lien est le titre, l’attribut href donne l’URL du livre)
  • Auteur : a.authorName
  • Note : span.minirating (contient la note moyenne et le nombre d’évaluations)
  • Image de couverture : img à l’intérieur de la ligne du livre

Pour les pages de détail d’un livre, laissez tomber les sélecteurs CSS et passez directement au JSON-LD. Goodreads intègre des données structurées dans une balise <script type="application/ld+json"> qui suit le format schema.org Book. C’est bien plus stable que les noms de classes, que Goodreads modifie à sa guise.

Étape 4 : extraire les données d’un livre à partir d’une seule page de liste

Parsons la page de liste et extrayons les infos de base pour chaque livre :

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

Vous devriez voir environ 100 livres par page de liste. Chaque entrée aura un titre, un auteur, une chaîne de note du type “4,28 avg rating — 9,031,257 ratings”, et une URL vers la page de détail du livre.

Étape 5 : scraper les sous-pages pour obtenir les informations détaillées d’un livre

La page de liste vous donne les bases, mais les vraies informations précieuses — ISBN, description complète, tags de genre, nombre de pages, date de publication — se trouvent sur la page individuelle de chaque livre. C’est là que JSON-LD devient très utile.

1import json
2import time
3def scrape_book_detail(book_url, headers):
4    """Visite une page de livre et extrait les métadonnées détaillées via JSON-LD."""
5    resp = requests.get(book_url, headers=headers, timeout=15)
6    if resp.status_code != 200:
7        return {}
8    soup = BeautifulSoup(resp.text, "lxml")
9    script = soup.find("script", {"type": "application/ld+json"})
10    if not script:
11        return {}
12    data = json.loads(script.string)
13    agg = data.get("aggregateRating", {})
14    # Les tags de genre ne sont pas dans JSON-LD ; on les récupère donc dans le HTML
15    genres = [g.get_text(strip=True) for g in soup.select('span.BookPageMetadataSection__genreButton a span')]
16    return {
17        "isbn": data.get("isbn", ""),
18        "pages": data.get("numberOfPages", ""),
19        "language": data.get("inLanguage", ""),
20        "format": data.get("bookFormat", ""),
21        "avg_rating": agg.get("ratingValue", ""),
22        "rating_count": agg.get("ratingCount", ""),
23        "review_count": agg.get("reviewCount", ""),
24        "description": data.get("description", "")[:200],  # tronqué pour l’aperçu
25        "genres": ", ".join(genres[:5]),
26    }
27# Exemple : enrichir les 3 premiers livres
28for book in books[:3]:
29    details = scrape_book_detail(book["book_url"], headers)
30    book.update(details)
31    print(f"Scrapé : {book['title']} — ISBN : {book.get('isbn', 'N/A')}")
32    time.sleep(4)  # respecter les limites de débit

Ajoutez un time.sleep() de 3 à 8 secondes entre les requêtes. Goodreads commence à limiter autour de 20 à 30 requêtes par minute depuis une même IP, et vous verrez apparaître des 403 ou des CAPTCHA si vous allez plus vite.

Cette approche en deux passes — d’abord collecter toutes les URLs de livres depuis les pages de liste, puis visiter chaque page de détail — est plus fiable et plus simple à reprendre si le scraping est interrompu. C’est la stratégie utilisée par la plupart des scrapers Goodreads qui fonctionnent bien.

À noter : peut faire cela automatiquement grâce au scraping des sous-pages. L’IA visite chaque page de détail d’un livre et enrichit votre tableau avec l’ISBN, la description, les genres, et plus encore — sans code, sans boucle, sans temporisation.

Étape 6 : gérer le contenu rendu en JavaScript avec Selenium

Pour les pages où le contenu nécessaire est chargé via JavaScript — avis, répartitions des notes, sections “plus de détails” — il faut un outil d’automatisation de navigateur. Voici un exemple avec 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# Attendre le chargement des avis
14try:
15    WebDriverWait(driver, 10).until(
16        EC.presence_of_element_located((By.CSS_SELECTOR, "article.ReviewCard"))
17    )
18except:
19    print("Les avis ne se sont pas chargés — la page peut exiger une connexion ou un délai JS")
20# Parser maintenant la page entièrement rendue
21page_source = driver.page_source
22soup = BeautifulSoup(page_source, "lxml")
23reviews = soup.select("article.ReviewCard")
24for rev in reviews[:3]:
25    text = rev.select_one("span.Formatted")
26    stars = rev.select_one("span.RatingStars")
27    print(f"Note : {stars.get_text(strip=True) if stars else 'N/A'}")
28    print(f"Avis : {text.get_text(strip=True)[:150] if text else 'N/A'}...")
29    print()
30driver.quit()

Quand utiliser Selenium plutôt que requests :

  • utilisez requests + BeautifulSoup pour les métadonnées des livres (JSON-LD), les pages de liste, les pages d’étagères (page 1), et les données des Choice Awards
  • utilisez Selenium ou Playwright pour les avis, les répartitions de notes et tout contenu absent du HTML brut

Playwright est généralement le meilleur choix pour les nouveaux projets — plus rapide, moins gourmand en mémoire, meilleures options de furtivité par défaut. Mais Selenium dispose d’une communauté plus large et de davantage d’exemples de code existants pour Goodreads en particulier.

Une pagination qui fonctionne vraiment : extraire les listes Goodreads complètes

La pagination est le point d’échec le plus fréquent des scrapers Goodreads, et je n’ai pas trouvé un seul tutoriel concurrent qui la couvre correctement. Voici comment faire comme il faut.

Comment fonctionnent les URLs de pagination Goodreads

Goodreads utilise un paramètre ?page=N très simple pour la plupart des pages paginées :

  • Listes : https://www.goodreads.com/list/show/1.Best_Books_Ever?page=2
  • Étagères : https://www.goodreads.com/shelf/show/thriller?page=2
  • Recherche : https://www.goodreads.com/search?q=fantasy&page=2

Chaque page de liste affiche généralement 100 livres. Les étagères en affichent 50 par page.

Écrire une boucle de pagination qui sait s’arrêter

1import time
2all_books = []
3base_url = "https://www.goodreads.com/list/show/1.Best_Books_Ever"
4for page_num in range(1, 50):  # limite de sécurité à 50 pages
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"Page {page_num} : statut {resp.status_code}, arrêt.")
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"Page {page_num} : aucun livre trouvé, fin atteinte.")
14        break
15    for row in rows:
16        title_tag = row.select_one("a.bookTitle")
17        author_tag = row.select_one("a.authorName")
18        title = title_tag.get_text(strip=True) if title_tag else ""
19        book_url = "https://www.goodreads.com" + title_tag["href"] if title_tag else ""
20        author = author_tag.get_text(strip=True) if author_tag else ""
21        all_books.append({"title": title, "author": author, "book_url": book_url})
22    print(f"Page {page_num} : {len(rows)} livres extraits (total : {len(all_books)})")
23    time.sleep(5)  # pause de 5 secondes entre les pages
24print(f"\nTerminé. Total de livres collectés : {len(all_books)}")

Vous détectez la dernière page en vérifiant que la liste de résultats est vide (aucun élément tr[itemtype="http://schema.org/Book"]) ou en observant l’absence d’un lien “suivant” (a.next_page).

Cas limite : authentification requise au-delà de la page 5

C’est le piège qui attrape presque tout le monde : certaines pages d’étagères et de listes Goodreads renvoient silencieusement le contenu de la page 1 lorsque vous demandez la page 6+ sans authentification. Pas d’erreur, pas de redirection — juste les mêmes données répétées.

Pour corriger cela, exportez le cookie _session_id2 depuis votre navigateur (via une extension d’export de cookies ou Chrome DevTools > Application > Cookies) et incluez-le dans vos requêtes :

1session = requests.Session()
2session.headers.update(headers)
3session.cookies.set("_session_id2", "YOUR_SESSION_COOKIE_VALUE_HERE", domain=".goodreads.com")
4# Utilisez maintenant session.get() au lieu de requests.get()
5resp = session.get(f"{base_url}?page=6", timeout=15)

Thunderbit gère nativement la pagination par clics et le scroll infini, sans code ni gestion de cookies. Si votre logique de pagination casse sans cesse, cela vaut vraiment la peine d’y penser.

Le script Python complet, prêt à copier-coller

Voici le script complet et consolidé. Il gère les en-têtes, la pagination, le scraping des sous-pages via JSON-LD, la limitation du débit et l’export CSV. Je l’ai testé sur des pages Goodreads en direct, à la mi-2025.

1"""
2goodreads_scraper.py — Scrape une liste Goodreads avec pagination et enrichissement des détails des livres.
3Utilisation : python goodreads_scraper.py
4Sortie : goodreads_books.csv
5"""
6import csv, json, time, requests
7from bs4 import BeautifulSoup
8HEADERS = {
9    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
10                  "(KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
11    "Accept-Language": "en-US,en;q=0.9",
12    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
13}
14BASE_URL = "https://www.goodreads.com/list/show/1.Best_Books_Ever"
15MAX_PAGES = 3          # à ajuster selon vos besoins
16DELAY_LISTING = 5      # secondes entre les pages de liste
17DELAY_DETAIL = 4       # secondes entre les pages de détail
18OUTPUT_FILE = "goodreads_books.csv"
19def scrape_listing_page(url):
20    """Retourne une liste de dicts avec title, author, book_url depuis une page de liste."""
21    resp = requests.get(url, headers=HEADERS, timeout=15)
22    if resp.status_code != 200:
23        return []
24    soup = BeautifulSoup(resp.text, "lxml")
25    rows = soup.select('tr[itemtype="http://schema.org/Book"]')
26    books = []
27    for row in rows:
28        t = row.select_one("a.bookTitle")
29        a = row.select_one("a.authorName")
30        if t:
31            books.append({
32                "title": t.get_text(strip=True),
33                "author": a.get_text(strip=True) if a else "",
34                "book_url": "https://www.goodreads.com" + t["href"],
35            })
36    return books
37def scrape_book_detail(book_url):
38    """Visite une page de livre et extrait les métadonnées via JSON-LD + fallback HTML."""
39    resp = requests.get(book_url, headers=HEADERS, timeout=15)
40    if resp.status_code != 200:
41        return {}
42    soup = BeautifulSoup(resp.text, "lxml")
43    script = soup.find("script", {"type": "application/ld+json"})
44    if not script:
45        return {}
46    data = json.loads(script.string)
47    agg = data.get("aggregateRating", {})
48    genres = [g.get_text(strip=True)
49              for g in soup.select("span.BookPageMetadataSection__genreButton a span")]
50    return {
51        "isbn": data.get("isbn", ""),
52        "pages": data.get("numberOfPages", ""),
53        "avg_rating": agg.get("ratingValue", ""),
54        "rating_count": agg.get("ratingCount", ""),
55        "review_count": agg.get("reviewCount", ""),
56        "description": (data.get("description", "") or "")[:300],
57        "genres": ", ".join(genres[:5]),
58        "language": data.get("inLanguage", ""),
59        "format": data.get("bookFormat", ""),
60        "published": data.get("datePublished", ""),
61    }
62def main():
63    all_books = []
64    # --- Passage 1 : collecter les URLs des livres depuis les pages de liste ---
65    for page in range(1, MAX_PAGES + 1):
66        url = f"{BASE_URL}?page={page}"
67        page_books = scrape_listing_page(url)
68        if not page_books:
69            print(f"Page {page} : vide — arrêt de la pagination.")
70            break
71        all_books.extend(page_books)
72        print(f"Page {page} : {len(page_books)} livres (total : {len(all_books)})")
73        time.sleep(DELAY_LISTING)
74    # --- Passage 2 : enrichir chaque livre avec les données de sa page ---
75    for i, book in enumerate(all_books):
76        details = scrape_book_detail(book["book_url"])
77        book.update(details)
78        print(f"[{i+1}/{len(all_books)}] {book['title']} — ISBN : {book.get('isbn', 'N/A')}")
79        time.sleep(DELAY_DETAIL)
80    # --- Export CSV ---
81    if all_books:
82        fieldnames = list(all_books[0].keys())
83        with open(OUTPUT_FILE, "w", newline="", encoding="utf-8") as f:
84            writer = csv.DictWriter(f, fieldnames=fieldnames)
85            writer.writeheader()
86            writer.writerows(all_books)
87        print(f"\n{len(all_books)} livres enregistrés dans {OUTPUT_FILE}")
88    else:
89        print("Aucun livre extrait.")
90if __name__ == "__main__":
91    main()

Avec MAX_PAGES = 3, ce script collecte environ 300 livres de la liste “Best Books Ever”, visite la page de détail de chaque livre et écrit le tout dans un CSV. Sur ma machine, cela prend environ 25 minutes (principalement à cause des pauses de 4 secondes entre les requêtes de détails). Votre fichier CSV de sortie contiendra des colonnes comme title, author, book_url, isbn, pages, avg_rating, rating_count, review_count, description, genres, language, format et published.

Exporter au-delà du CSV : Google Sheets avec gspread

Si vous voulez les données dans Google Sheets plutôt que dans un CSV, ou en plus, ajoutez ceci après l’export 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("Données envoyées vers Google Sheets.")

Vous aurez besoin d’un compte de service Google Cloud avec les API Sheets et Drive activées. La explique la configuration en environ 5 minutes. Utilisez des opérations par lots (append_rows() avec une liste de listes) si vous poussez plus de quelques centaines de lignes — la limite de Google est de 300 requêtes par 60 secondes et par projet.

Bien sûr, si toute cette configuration vous semble excessive, Thunderbit exporte vers Google Sheets, Airtable, Notion, Excel, CSV et JSON en — sans installation de bibliothèque, sans fichier d’identifiants, sans quota API.

L’alternative sans code : scraper Goodreads avec Thunderbit

Tout le monde n’a pas envie de maintenir un script Python. Peut-être êtes-vous un éditeur qui fait une étude de marché ponctuelle, ou un book blogger qui veut simplement un tableau des best-sellers de l’année. C’est exactement pour ce genre de cas que nous avons conçu Thunderbit.

Comment extraire Goodreads avec Thunderbit

  1. Installez l’extension Chrome Thunderbit depuis le et ouvrez une page de liste, d’étagère ou de résultats de recherche Goodreads.
  2. Cliquez sur “AI Suggest Fields” dans la barre latérale Thunderbit. L’IA lit la page et propose des colonnes — généralement le titre, l’auteur, l’URL de l’image de couverture et le lien du livre.
  3. Cliquez sur “Scrape” — les données sont extraites en quelques secondes dans un tableau structuré.
  4. Exportez vers Google Sheets, Excel, Airtable, Notion, CSV ou JSON.

Pour les données détaillées d’un livre (ISBN, description, genres, nombre de pages), la fonction de scraping des sous-pages de Thunderbit visite chaque page de détail et enrichit automatiquement le tableau — sans boucles, sans temporisation, sans débogage.

Thunderbit gère aussi nativement les listes paginées. Vous lui indiquez de cliquer sur “Next” ou de faire défiler, et il collecte les données sur toutes les pages sans écrire une seule ligne de code.

Le compromis est simple : le script Python vous donne un contrôle total et reste gratuit (hormis votre temps), tandis que Thunderbit échange un peu de flexibilité contre un gain de temps massif et aucune maintenance. Pour une liste de 300 livres, le script Python prend environ 25 minutes d’exécution, plus le temps passé à l’écrire et à le corriger. Thunderbit récupère les mêmes données en environ 3 minutes, avec deux clics.

Scraper Goodreads de manière responsable : robots.txt, conditions d’utilisation et éthique

Ici, il faut une réponse claire, pas un paragraphe d’avertissement générique.

Ce que dit vraiment le robots.txt de Goodreads

Le robots.txt de Goodreads en production est étonnamment précis. Les pages de détail des livres (/book/show/), les listes publiques (/list/show/), les étagères publiques (/shelf/show/) et les pages d’auteurs (/author/show/) ne sont pas bloquées. En revanche, sont bloqués : /api, /book/reviews/, /review/list, /review/show, /search, et plusieurs autres chemins. GPTBot et CCBot (Common Crawl) sont totalement bloqués avec Disallow: /. Il existe une directive Crawl-delay: 5 pour bingbot, mais pas de délai global.

Les conditions d’utilisation de Goodreads en langage clair

Les CGU (dernière révision le 28 avril 2021) interdisent “toute utilisation d’outils de data mining, robots ou similaires pour la collecte et l’extraction de données”. Le langage est large, et il faut le prendre au sérieux — mais les tribunaux ont régulièrement estimé qu’une violation des CGU ne constituait pas à elle seule un “accès non autorisé” au sens pénal. L’arrêt a indiqué que “criminaliser les violations des conditions d’utilisation risque de transformer chaque site web en sa propre juridiction pénale”.

Bonnes pratiques

  • Faites des pauses entre les requêtes : 3 à 8 secondes (le propre robots.txt de Goodreads suggère 5 secondes pour les robots)
  • Restez sous 5 000 requêtes par jour depuis une seule IP
  • Ne scrapez que des pages accessibles publiquement — évitez de collecter à grande échelle des données réservées aux comptes connectés
  • Ne redistribuez pas commercialement le texte brut des avis — les avis sont des œuvres créatives protégées par le droit d’auteur
  • Ne stockez que ce dont vous avez besoin et définissez une politique de conservation des données
  • Recherche personnelle vs usage commercial : le scraping de données publiques à des fins d’analyse personnelle ou de recherche académique est généralement accepté. Le risque juridique augmente surtout lors d’une redistribution commerciale.

Utiliser un outil comme Thunderbit (qui scrape via votre propre session navigateur) rend l’interaction visuellement identique à une navigation normale, mais les mêmes principes éthiques s’appliquent quel que soit l’outil. Si vous souhaitez aller plus loin sur les , nous avons traité le sujet séparément.

Conseils et pièges courants

Conseil : commencez toujours par JSON-LD. Avant d’écrire des sélecteurs CSS complexes, vérifiez si les données dont vous avez besoin se trouvent dans la balise <script type="application/ld+json">. C’est plus stable, plus simple à parser, et cela casse moins souvent lorsque Goodreads met à jour son front-end.

Conseil : utilisez la stratégie en deux passes. Commencez par collecter toutes les URLs de livres depuis les pages de liste, puis visitez chaque page de détail. Votre scraper sera plus facile à reprendre s’il plante au milieu, et vous pouvez sauvegarder la liste d’URLs sur disque comme point de reprise.

Piège : oublier les champs manquants. Toutes les pages de livres n’ont pas d’ISBN, de tags de genre ou de description. Utilisez toujours .get() avec une valeur par défaut, ou entourez vos sélecteurs de vérifications if. Une seule erreur NoneType peut faire planter une exécution de scraping de trois heures.

Piège : aller trop vite. Je sais qu’il est tentant de mettre time.sleep(0.5) et d’avancer à toute vitesse. Mais Goodreads commence à renvoyer des 403 après environ 20 à 30 requêtes rapides, et une fois que vous êtes signalé, vous devrez peut-être attendre des heures ou changer d’IP. Un délai de 4 à 5 secondes est le bon compromis.

Piège : faire confiance à d’anciens tutoriels. Si un guide mentionne l’API Goodreads, ou utilise des noms de classes comme .field.value ou #bookTitle, il est probablement obsolète. Vérifiez toujours les sélecteurs sur la page en direct avant de construire votre scraper.

Pour aller plus loin sur le choix des bons outils et frameworks de scraping, consultez nos guides sur les et .

Conclusion et points clés à retenir

Extraire Goodreads avec Python est tout à fait faisable — il faut simplement savoir où sont les pièges. En résumé :

  • L’API Goodreads a disparu (depuis décembre 2020). Le scraping est la principale façon d’obtenir des données structurées sur les livres depuis la plateforme.
  • Les résultats vides sont presque toujours dus à du contenu rendu par JS, à des sélecteurs obsolètes, à des en-têtes manquants ou à des problèmes d’authentification sur la pagination — pas au fait que votre code soit faux.
  • JSON-LD est votre meilleur allié pour les métadonnées des livres. C’est stable, structuré et rarement modifié.
  • La pagination nécessite une authentification pour de nombreuses pages d’étagères et de listes au-delà de la page 5. Ajoutez le cookie _session_id2.
  • La limitation de débit est bien réelle. Utilisez des pauses de 3 à 8 secondes et restez sous 5 000 requêtes par jour.
  • La stratégie en deux passes (collecter d’abord les URLs, puis scraper les pages de détail) est plus fiable et plus facile à reprendre.
  • Pour les non-codeurs (ou pour tous ceux qui tiennent à leur après-midi), gère tout cela — rendu JS, pagination, enrichissement des sous-pages et export — en deux clics environ.

Scrapez de façon responsable, respectez le robots.txt, et puisse votre extraction de livres ne jamais revenir avec seulement [].

FAQ

Peut-on encore utiliser l’API Goodreads ?

Non. Goodreads a abandonné son API publique en décembre 2020 et n’attribue plus de nouvelles clés développeur. Les clés existantes restées inactives pendant 30 jours ont été désactivées automatiquement. Le scraping web ou des API alternatives (comme Open Library ou Google Books) sont désormais les options disponibles pour accéder aux données de livres de manière programmatique.

Pourquoi mon scraper Goodreads renvoie-t-il des résultats vides ?

La cause la plus fréquente est le contenu rendu par JavaScript. Goodreads charge les avis, les distributions de notes et de nombreuses sections de détail via React/JavaScript, qu’un simple requests.get() ne peut pas voir. Passez à Selenium ou Playwright pour ces pages. Les autres causes incluent des sélecteurs CSS obsolètes (Goodreads a changé le HTML), des en-têtes User-Agent manquants (provoquant des blocages 403), ou des requêtes non authentifiées sur les pages d’étagères paginées.

Est-il légal de scraper Goodreads ?

Le scraping de données publiques à des fins personnelles ou de recherche est généralement accepté au regard des précédents juridiques actuels (hiQ v. LinkedIn, Meta v. Bright Data). Cependant, les conditions d’utilisation de Goodreads interdisent la collecte automatisée de données, et vous devez toujours consulter leur robots.txt. Évitez la redistribution commerciale du texte des avis protégés par le droit d’auteur, et limitez le volume de requêtes pour ne pas surcharger le site.

Comment scraper plusieurs pages sur Goodreads ?

Ajoutez ?page=N à l’URL de l’étagère ou de la liste, puis bouclez sur les numéros de page. Vérifiez les résultats vides ou l’absence d’un lien “suivant” pour détecter la dernière page. Point important : certaines pages d’étagères exigent une authentification (le cookie _session_id2) pour renvoyer des résultats au-delà de la page 5 — sans cela, vous récupérerez silencieusement les données de la page 1 en boucle.

Peut-on scraper Goodreads sans écrire de code ?

Oui. est une extension Chrome qui vous permet de scraper Goodreads en deux clics — l’IA suggère les champs de données, vous cliquez sur “Scrape”, puis vous exportez directement vers Google Sheets, Excel, Airtable ou Notion. Il gère automatiquement le contenu rendu par JavaScript, la pagination et l’enrichissement des sous-pages, sans Python ni code.

En savoir plus

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

Essaie Thunderbit

Extrayez des leads et d’autres données en seulement 2 clics. Propulsé par l’IA.

Obtenir Thunderbit C’est gratuit
Extraire des données grâce à l’IA
Transfère facilement les données vers Google Sheets, Airtable ou Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week