Goodreads est l'un des plus grands catalogues publics de données sur les livres du web, et chaque page publique de livre porte le type de signal lecteur qui alimente l'analyse de tendances, les moteurs de recommandation et la recherche de marché : un titre, un auteur, une note moyenne, combien de personnes l'ont noté, et le texte des avis publics. Le problème est que Goodreads rend une grande partie de ce contenu côté client et charge les avis de façon asynchrone, donc une requête HTTP simple vous renvoie une coquille légère au lieu des notes et du texte d'avis que vous cherchez.
Ce guide vous montre comment scraper les notes et avis Goodreads avec Python de manière fiable. Vous construisez un petit scraper fonctionnel qui récupère une page de livre rendue via la Crawling API, analyse les champs voulus avec BeautifulSoup, et affiche un enregistrement structuré propre. L'ensemble du tutoriel est délibérément limité aux données publiques de livres et d'avis, et la section sur la légalité près de la fin n'est pas du remplissage, lisez-la avant de pointer ceci sur un volume réel.
Ce que vous allez construire
Un script Python qui prend une URL publique de livre Goodreads, récupère le HTML rendu via la Crawling API, et extrait un enregistrement structuré du livre et de ses avis visibles. Nous utilisons un titre public bien connu comme exemple fil rouge et extrayons ces champs :
- Book title le nom du livre, par exemple "The Great Gatsby".
- Author l'auteur crédité sur la page du livre.
- Average rating le score agrégé que Goodreads calcule à partir des notes des utilisateurs.
- Ratings count combien de personnes ont noté le livre.
- Reviews le texte d'avis public affiché sur la page, avec le nom d'affichage du rédacteur.
Pourquoi une requête simple échoue sur Goodreads
Si vous demandez une URL de livre Goodreads avec un client HTTP brut, vous obtenez une réponse avec le statut 200 et presque aucun contenu d'avis dans le corps. Deux éléments jouent contre vous. Premièrement, Goodreads rend une grande partie de la section de notes et d'avis dans le navigateur avec JavaScript, donc le HTML initial est une coquille qui ne se remplit qu'après l'exécution des scripts de la page. Deuxièmement, la liste d'avis charge de façon asynchrone et s'étend derrière une interaction, donc même un rendu partiel peut manquer les données que vous voulez à moins que vous ne laissiez la page se stabiliser.
Un scraper Goodreads fonctionnel a donc besoin de deux choses en une seule requête : un navigateur qui rend réellement la page, et une IP que la plateforme lit comme un vrai visiteur. Vous pouvez assembler cela vous-même avec un navigateur sans interface graphique et un pool de proxies résidentiels rotatifs, mais assembler ces éléments et les maintenir en bonne santé représente l'essentiel du travail. La Crawling API regroupe les deux en un seul appel : vous lui envoyez l'URL avec un token JavaScript, elle rend la page derrière une IP de confiance, et vous retourne le HTML final à analyser.
Crawlbase propose deux types de tokens. Le token normal récupère le HTML statique ; le token JavaScript (JS) rend d'abord la page dans un vrai navigateur. Goodreads charge ses notes et avis côté client, donc vous avez besoin du token JS ici. Utiliser le token normal renvoie la même coquille légère qu'une requête simple, et il y a peu de valeur à en extraire.
Prérequis
Quelques éléments doivent être en place avant d'écrire du code. Aucun ne prend longtemps.
Notions de base en Python. Vous devez être à l'aise pour écrire et exécuter un script Python et installer des paquets avec pip. Si vous débutez avec l'analyse HTML, notre guide sur comment utiliser BeautifulSoup en Python couvre les bases de sélecteurs sur lesquelles ce tutoriel s'appuie.
Python 3.8 ou version ultérieure. Confirmez votre version avec python --version. Si vous ne l'avez pas, installez-le depuis python.org ou via une distribution comme Anaconda.
Un compte Crawlbase et un token JS. Inscrivez-vous, ouvrez votre tableau de bord et copiez votre token JavaScript (JS) depuis la page de documentation du compte. Traitez le token comme un mot de passe : il authentifie vos requêtes, donc gardez-le hors du contrôle de version.
Configurer le projet
Créez un environnement virtuel pour isoler les dépendances du projet, puis installez les deux bibliothèques dont le scraper a besoin.
python --version python -m venv goodreads_env source goodreads_env/bin/activate pip install crawlbase beautifulsoup4
Sous Windows, activez l'environnement avec goodreads_env\Scripts\activate à la place de la ligne source. Deux dépendances font le travail : crawlbase est le client officiel pour la Crawling API, et beautifulsoup4 analyse le HTML renvoyé pour que vous puissiez extraire les champs individuels par sélecteur CSS.
Étape 1 : Récupérer la page de livre rendue
Commencez par obtenir la page finale. Importez la classe CrawlingAPI, initialisez-la avec votre token JS et demandez l'URL du livre. Vérifier le statut avant d'analyser permet de rendre les échecs visibles plutôt que silencieux.
from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_JS_TOKEN"}) def crawl(page_url): options = {"ajax_wait": "true", "page_wait": 5000} response = api.get(page_url, options) if response["status_code"] == 200: return response["body"].decode("utf-8") print(f"Request failed: {response['status_code']}") return None if __name__ == "__main__": page_url = "https://www.goodreads.com/book/show/4671.The_Great_Gatsby" html = crawl(page_url) print(html[:500] if html else "No HTML returned")
Les deux options d'attente sont importantes pour une cible rendue côté client comme celle-ci. ajax_wait demande à l'API d'attendre que le contenu asynchrone ait fini de charger, et page_wait maintient un délai fixe en millisecondes après le chargement pour que les éléments à rendu tardif apparaissent avant la capture de la page. Cinq secondes est un bon point de départ ; augmentez-les si les champs d'avis reviennent vides. Exécutez le script avec python scraper.py et vous devriez voir le vrai balisage de livre, pas la coquille légère qu'une requête simple renverrait. Cela confirme que le rendu fonctionne avant d'écrire un seul sélecteur.
Goodreads nécessite une page rendue derrière une IP de confiance, en un seul appel. La Crawling API prend un token JS, exécute la page dans un vrai navigateur, fait tourner les IP résidentielles côté serveur, et vous remet le HTML final, pour que vous n'ayez pas à gérer vous-même un parc de navigateurs sans interface ni un pool de proxies. Pointez-la sur une page publique de livre avec le niveau gratuit en premier.
Étape 2 : Analyser les champs du livre avec BeautifulSoup
Le HTML rendu en main, chargez-le dans BeautifulSoup et extrayez chaque champ par son sélecteur. Une page de livre Goodreads présente les détails essentiels dans une structure prévisible, donc vous pouvez associer le titre, l'auteur, la note moyenne et le nombre de notes à des sélecteurs individuels, puis parcourir les cartes d'avis pour collecter le texte des avis publics. Des fonctions auxiliaires qui retournent None sur un élément manquant empêchent qu'un seul champ absent ne fasse planter toute l'exécution.
from bs4 import BeautifulSoup def text_of(node, selector): el = node.select_one(selector) return el.get_text(strip=True) if el else None def scrape_book(html): soup = BeautifulSoup(html, "html.parser") rating_el = soup.select_one("div.RatingStatistics span.RatingStars") average_rating = rating_el["aria-label"] if rating_el else None reviews = [] for card in soup.select("div.ReviewsList article.ReviewCard"): reviews.append({ "user": text_of(card, 'div[data-testid="name"]'), "review": text_of(card, "section.ReviewText span.Formatted"), }) return { "title": text_of(soup, 'h1.H1Title a[data-testid="title"]'), "author": text_of(soup, "span.ContributorLink__name"), "average_rating": average_rating, "ratings_count": text_of(soup, 'span[data-testid="ratingsCount"]'), "reviews": reviews, }
L'auxiliaire text_of fait deux choses utiles à la fois : il interroge un seul élément dans un noeud donné et retourne None quand l'élément est absent, au lieu de planter sur un appel .get_text() sur rien. Passer le noeud explicitement est important ici, car chaque carte d'avis est sa propre portée et vous voulez lire le nom du rédacteur et le texte de l'avis depuis cette carte, pas depuis la première correspondance sur toute la page. La note moyenne est lue depuis l'attribut aria-label plutôt que depuis le texte visible, donc elle est gérée séparément.
Les noms de classes Goodreads (les marqueurs RatingStars et ReviewCard, les attributs data-testid et les wrappers de section) changent sans préavis. Traitez les sélecteurs ci-dessus comme un modèle de départ, non comme un contrat. Quand un champ revient None, ré-inspectez la page en direct dans les outils de développement de votre navigateur et mettez à jour le sélecteur. La maintenance périodique des sélecteurs est normale pour tout scraper en production.
Étape 3 : Charger plus d'avis
Le premier rendu montre seulement la tranche supérieure des avis. Goodreads révèle les autres derrière un bouton "Afficher plus d'avis" plutôt qu'un pagineur numéroté, donc pour accéder à un texte d'avis plus profond vous avez besoin que la page clique ce bouton avant d'être capturée. La Crawling API expose une option css_click_selector qui clique un élément correspondant pendant le rendu, ce qui vous permet d'obtenir un plus grand ensemble d'avis dans la même requête.
def crawl_with_more_reviews(page_url): options = { "ajax_wait": "true", "page_wait": 5000, "css_click_selector": 'button:has(span[data-testid="loadMore"])', } response = api.get(page_url, options) if response["status_code"] == 200: return response["body"].decode("utf-8") print(f"Request failed: {response['status_code']}") return None
Le sélecteur cible le bouton qui enveloppe le contrôle de chargement supplémentaire. Le cliquer une fois développe la liste d'avis visible avant la capture du HTML, donc le même analyseur scrape_book voit ensuite plus de cartes sans aucune modification. Si vous avez besoin d'encore plus d'avis qu'un seul clic ne produit, augmentez page_wait pour laisser la liste développée le temps de se rendre. Pour des informations de fond sur pourquoi le contenu piloté par interaction se comporte ainsi, voir notre guide sur comment crawler les sites web JavaScript.
Étape 4 : Assembler le tout
Reliez maintenant la récupération et l'analyse en un seul script exécutable. Récupérez le HTML rendu avec le clic de chargement supplémentaire, transmettez-le à l'analyseur, et écrivez l'enregistrement structuré dans un fichier JSON.
import json from crawlbase import CrawlingAPI from bs4 import BeautifulSoup api = CrawlingAPI({"token": "YOUR_CRAWLBASE_JS_TOKEN"}) def crawl(page_url): options = { "ajax_wait": "true", "page_wait": 5000, "css_click_selector": 'button:has(span[data-testid="loadMore"])', } response = api.get(page_url, options) if response["status_code"] == 200: return response["body"].decode("utf-8") print(f"Request failed: {response['status_code']}") return None def text_of(node, selector): el = node.select_one(selector) return el.get_text(strip=True) if el else None def scrape_book(html): soup = BeautifulSoup(html, "html.parser") rating_el = soup.select_one("div.RatingStatistics span.RatingStars") average_rating = rating_el["aria-label"] if rating_el else None reviews = [] for card in soup.select("div.ReviewsList article.ReviewCard"): reviews.append({ "user": text_of(card, 'div[data-testid="name"]'), "review": text_of(card, "section.ReviewText span.Formatted"), }) return { "title": text_of(soup, 'h1.H1Title a[data-testid="title"]'), "author": text_of(soup, "span.ContributorLink__name"), "average_rating": average_rating, "ratings_count": text_of(soup, 'span[data-testid="ratingsCount"]'), "reviews": reviews, } def main(): page_url = "https://www.goodreads.com/book/show/4671.The_Great_Gatsby" html = crawl(page_url) if not html: return data = scrape_book(html) with open("goodreads_book.json", "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) print(json.dumps(data, indent=2, ensure_ascii=False)[:600]) if __name__ == "__main__": main()
À quoi ressemble la sortie
Exécutez le script complet avec python scraper.py et vous obtenez un enregistrement structuré propre pour le livre, prêt à écrire en JSON, CSV ou dans une base de données.
{ "title": "The Great Gatsby", "author": "F. Scott Fitzgerald", "average_rating": "Rating 3.93 out of 5", "ratings_count": "5,432,109 ratings", "reviews": [ { "user": "Alex", "review": "Charms you with some of the most elegant English prose ever published." }, { "user": "Inge", "review": "There was one thing I really liked about The Great Gatsby. It was short." } ] }
Passer à l'échelle sur plusieurs livres
Un livre est une démonstration ; un vrai travail s'exécute sur une liste de titres. La structure reste la même : gardez une liste d'URLs de livres, récupérez chacune via la Crawling API, analysez-la avec la même fonction, et collectez les lignes. Comme chaque page de livre partage la même structure, l'analyseur que vous avez déjà écrit fonctionne sur tous sans modification.
import time books = [ "https://www.goodreads.com/book/show/4671.The_Great_Gatsby", "https://www.goodreads.com/book/show/5470.1984", ] results = [] for url in books: html = crawl(url) if html: results.append(scrape_book(html)) time.sleep(2) with open("goodreads_books.json", "w", encoding="utf-8") as f: json.dump(results, f, ensure_ascii=False, indent=2)
Le time.sleep(2) entre les requêtes pace la boucle pour que vous ne lanciez pas des pages de livres l'une après l'autre. Pour collecter des URLs de livres à grande échelle, vous pouvez scraper les pages de listes et d'étagères publiques Goodreads avec le même schéma récupération-puis-analyse, en collectant les liens de livres et en visitant ensuite chacun. Gardez simplement le volume raisonnable et respectez les limites de débit abordées ci-dessous.
Rester non bloqué
Même avec le rendu géré, Goodreads surveille le trafic ressemblant à un scraper. Quelques habitudes maintiennent une exécution saine, et elles s'appliquent à toute grande cible publique.
- Pacez vos requêtes. Bombarder des pages de livres dans une boucle serrée est le moyen le plus rapide d'être limité en débit. Répartissez les requêtes et variez vos cibles plutôt que de crawler un seul chemin à pleine vitesse.
- Misez sur la rotation. Un pool d'IP résidentielles répartit les requêtes sur de nombreuses adresses d'utilisateurs réels pour qu'aucune ne déclenche une limite de débit. La Crawling API gère cela pour vous ; si vous construisez votre propre infrastructure, c'est la partie à bien faire.
- Lisez les codes de statut. Une exécution qui commence à renvoyer des challenges ou des erreurs vous indique que le taux actuel ou le niveau d'IP n'est plus suffisant. Traitez-le comme un signal de reculer, pas comme du bruit à ignorer.
Pour le guide plus complet, voir comment scraper des sites web sans être bloqué et l'approfondissement sur comment contourner les CAPTCHA lors du scraping web. Si vous préférez router votre propre trafic via un pool rotatif plutôt qu'utiliser l'API managée, le Smart AI Proxy (également appelé AI Proxy) vous donne la même rotation d'IP résidentielles comme endpoint proxy plug-and-play.
Est-il légal de scraper Goodreads ?
La légalité du scraping de Goodreads dépend des conditions d'utilisation de Goodreads et d'Amazon, de votre juridiction et de ce que vous faites des données. Goodreads est une propriété d'Amazon, et ses conditions restreignent l'accès automatisé, donc le scraping peut aller à l'encontre de ces conditions quelle que soit la prudence de vos outils. Aucun code ici ne change cela ; il ne fait que rendre la partie technique fonctionnelle. Lisez les Conditions d'utilisation de Goodreads et son robots.txt, et traitez les deux comme la frontière de ce que vous collectez.
Quelques lignes à maintenir. Collectez uniquement les données publiques de livres et d'avis : titre, auteur, note moyenne, nombre de notes et le texte d'avis que tout le monde peut voir sans compte. Respectez les attentes de débit de Goodreads et gardez votre volume de requêtes assez bas pour ne pas peser sur ses serveurs. Évitez les données personnelles des rédacteurs au-delà de ce qui est publiquement affiché sur la page, et ne construisez pas de profils d'individus identifiables à partir de celles-ci. Si vous prévoyez de réutiliser les données commercialement, obtenez une autorisation ou une source sous licence plutôt que de supposer que le silence est un consentement.
Une note pratique spécifique à Goodreads : l'API officielle Goodreads est pratiquement dépréciée et fermée aux nouvelles clés, donc il n'y a pas de flux premier parti en direct sur lequel se rabattre comme certaines plateformes en proposent. Cela laisse deux options réalistes pour les données publiques, scraper les pages publiques de livres comme montré ici, ou s'approvisionner en données via un fournisseur sous licence. Ce guide est délibérément limité au contenu public des pages de livres parce que c'est la ligne qui rend le travail défendable. Il ne couvre pas ce qui est derrière une connexion, les étagères privées ou les données de compte d'un utilisateur, les données personnelles des rédacteurs au-delà de l'affichage public, ni aucune tentative de contourner l'authentification. Si votre projet nécessite plus que les données publiques de livres et d'avis, une source de données sous licence est le bon chemin, pas un scraper plus ingénieux.
Points clés
- Goodreads est rendu côté client. Une requête simple renvoie une coquille légère, donc vous devez rendre la page avant d'analyser les notes et les avis.
-
Vous avez besoin du rendu et d'une IP de confiance ensemble. La Crawling API avec un token JS fait les deux en un seul appel ;
ajax_waitetpage_waitcontrôlent combien de temps elle attend le contenu. -
Les avis se chargent derrière un bouton. Utilisez
css_click_selectorpour développer la liste d'avis pendant le rendu pour que le même analyseur voie plus de cartes. - BeautifulSoup fait l'extraction. Associez le titre, l'auteur, la note moyenne, le nombre de notes et le texte d'avis aux sélecteurs actuels, et attendez-vous à ce que ces sélecteurs dérivent.
- Restez sur des données publiques. Respectez les CGU Goodreads et Amazon et le robots.txt, préférez une source sous licence pour un usage en volume ou commercial, et ne touchez jamais les comptes, les étagères privées ou les données personnelles des rédacteurs.
Foire aux questions
Pourquoi une requête simple ne renvoie-t-elle aucun avis depuis Goodreads ?
Parce que Goodreads rend son contenu de notes et d'avis côté client avec JavaScript et charge la liste d'avis de façon asynchrone. Le HTML initial est une coquille qui ne se remplit qu'après l'exécution des scripts de la page dans un navigateur, donc une requête HTTP brute retourne le statut 200 avec les champs d'avis vides. Pour obtenir de vraies données, vous devez d'abord rendre la page, ce que le token JS de la Crawling API gère pour vous.
Ai-je besoin du token normal ou du token JS pour Goodreads ?
Du token JS. Le token normal récupère le HTML statique, qui sur Goodreads est la même coquille légère qu'une requête simple renvoie. Le token JS rend la page dans un vrai navigateur avant de remettre le HTML, donc les notes et le texte d'avis sont présents quand BeautifulSoup les analyse.
Comment charger plus que les premiers avis ?
Goodreads révèle les avis supplémentaires derrière un bouton "Afficher plus d'avis" plutôt qu'un pagineur numéroté. Passez une option css_click_selector à la Crawling API qui cible ce bouton, et il est cliqué pendant le rendu pour que le HTML capturé inclue la liste développée. Augmentez page_wait si vous avez besoin que les avis nouvellement révélés finissent de se rendre avant la capture.
Mes sélecteurs retournent None. Qu'est-ce qui a changé ?
Très probablement le balisage de Goodreads. Ses classes RatingStars et ReviewCard, ses attributs data-testid et ses wrappers de section changent sans préavis, donc les sélecteurs qui fonctionnaient le mois dernier peuvent se casser. Ré-inspectez une page de livre en direct dans les outils de développement de votre navigateur et mettez à jour les sélecteurs. La maintenance périodique des sélecteurs est normale pour tout scraper en production.
Puis-je utiliser l'API officielle Goodreads à la place ?
En pratique, non. L'API officielle Goodreads est pratiquement dépréciée et fermée aux nouvelles clés, donc il n'y a pas de flux premier parti en direct sur lequel se fier. Pour les données publiques, les options réalistes sont de scraper les pages publiques de livres avec l'approche de ce guide, ou de s'approvisionner en données via un fournisseur sous licence. Dans tous les cas, respectez les conditions d'utilisation, le robots.txt et les limites de débit.
Comment éviter d'être bloqué lors du scraping de Goodreads ?
Gardez votre taux de requêtes par IP bas, variez vos cibles plutôt que de boucler sur un seul chemin, et routez via des IP résidentielles rotatives pour qu'aucune adresse unique ne déclenche une limite de débit. La Crawling API gère la rotation et un pool d'IP de confiance pour vous ; si vous construisez votre propre infrastructure, c'est là qu'il faut investir. Surveillez les codes de statut et reculez quand vous commencez à voir des challenges.
Crawlez n'importe quel site à grande échelle, sans combattre l'infrastructure.
Crawlbase gère les proxies, les empreintes et les CAPTCHA afin que votre équipe livre des pipelines de données au lieu de maintenir la plomberie de crawl. 1 000 requêtes gratuites, sans carte requise.
