Les avis clients sont parmi les données publiques les plus utiles sur le web. Les notes, les retours écrits et les indicateurs d'achat vérifié vous disent ce que les gens pensent réellement d'un produit, et ils se mettent à jour en permanence. Le problème est que la plupart des pages d'avis sont rendues côté client et paginées sur des dizaines ou des centaines d'écrans, de sorte qu'une simple requête HTTP vous remet une coquille vide. Ce guide vous montre comment scraper les avis clients avec un petit pipeline Python exécutable : rendre la page à fort contenu JavaScript, analyser les champs structurés, paginer sur l'ensemble complet, stocker les résultats et éventuellement exécuter une analyse de sentiment par-dessus.
Pour rester honnête et défendable, l'ensemble du tutoriel est limité aux avis publics : la note, le titre, le corps, la date et l'indicateur vérifié que n'importe qui peut voir sans se connecter. Il ne touche pas aux comptes d'utilisateurs, au contenu verrouillé par une connexion, ni aux données personnelles au-delà de ce qu'une plateforme affiche déjà publiquement. La section éthique et CGU proche de la fin n'est pas un boilerplate, alors lisez-la avant de pointer ceci vers un volume de production.
Pourquoi scraper les avis clients
Un seul avis vous donne l'opinion d'une personne. Quelques milliers d'entre eux, structurés en lignes que vous pouvez interroger, vous disent où un produit gagne et où il perd discrètement des clients. C'est la valeur : transformer une page d'avis rendue en données propres et comparables que vous pouvez visualiser, suivre dans le temps ou alimenter dans un modèle. Les équipes l'utilisent pour le benchmarking concurrentiel, l'analyse des lacunes produit, la surveillance de marque et le suivi de l'évolution du sentiment après un lancement ou une correction.
C'est la même forme de problème que tout travail de web scraping e-commerce. La différence avec les avis est le volume et la pagination : les données sont réparties sur de nombreuses pages, chargées paresseusement et protégées par des défenses anti-bot qui s'intensifient au fur et à mesure. L'approche doit donc gérer le rendu, la pagination et le blocage dès la première requête.
Pourquoi une simple récupération échoue sur les pages d'avis
Demandez une URL d'avis moderne avec un client HTTP brut et vous obtenez généralement le statut 200 et presque aucun contenu d'avis dans le corps. Deux choses jouent contre vous. Premièrement, la plupart des plateformes rendent les avis dans le navigateur avec JavaScript, donc le HTML initial est un squelette qui ne se remplit qu'après l'exécution des scripts de la page. Deuxièmement, les sites d'avis signalent rapidement le trafic automatisé : les adresses IP de centres de données et les schémas de requêtes qui ne ressemblent pas à un vrai navigateur sont contestés ou bloqués avant même de voir le contenu rendu.
Un scraper d'avis 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 visiteur réel. Vous pouvez assembler cela vous-même avec un navigateur headless plus un pool de proxies résidentiels rotatifs, mais assembler ces éléments et les maintenir en bonne santé représente la majeure partie du travail. La Crawling API Crawlbase 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 renvoie le HTML terminé à analyser. Si vous préférez ignorer les sélecteurs sur des cibles courantes, la Crawling API renvoie les champs analysés en JSON, et pour l'accès proxy brut il y a le Smart AI Proxy.
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. Les pages d'avis sont rendues côté client, donc vous avez besoin du token JS ici. Utiliser le token normal renvoie la même coquille vide qu'une simple récupération, avec les fiches d'avis manquantes.
Ce que vous extrayez d'un avis
Les champs à capturer sont cohérents sur la plupart des plateformes, même quand le balisage ne l'est pas. Visez ces cinq sur chaque fiche d'avis :
- rating la note en étoiles, normalisée en nombre. La plupart des sites utilisent une échelle de 1 à 5 ; certains utilisent 1 à 10, que vous convertissez ensuite.
- title le court titre qu'un évaluateur donne, quand la plateforme en a un.
- body le texte de l'avis lui-même, la partie qualitative qui porte le vrai signal.
-
date quand l'avis a été publié, idéalement depuis un attribut
datetimelisible par machine plutôt que du texte d'affichage. - verified si la plateforme le marque comme un achat vérifié, ce qui vous permet de filtrer les avis moins fiables plus tard.
L'objectif est un schéma stable afin que les avis de différentes sources s'alignent sans nettoyage spécifique à chaque source. Une seule forme unifiée fonctionne bien :
{ "rating": 4.5, "title": "Exactly what I needed", "body": "Arrived early and works as described.", "date": "2026-01-10", "verified": true, "url": "https://www.example.com/product/123/reviews?page=2" }
Une fois que chaque avis correspond à cette structure, l'analyse croisée entre produits et plateformes n'est que du filtrage et du regroupement, pas du reformatage.
Configurer le projet
Vous avez besoin de Python 3 et d'un compte Crawlbase avec un token JS, que vous obtenez depuis le tableau de bord après inscription. Créez un dossier de projet et installez les bibliothèques.
python --version mkdir review-scraper && cd review-scraper python -m venv venv && source venv/bin/activate pip install crawlbase beautifulsoup4
Deux dépendances font le travail : crawlbase est le client officiel pour la Crawling API, et beautifulsoup4 analyse le HTML renvoyé. Les modules de la bibliothèque standard re, csv et json couvrent la normalisation et le stockage, il n'y a donc rien d'autre à installer. Gardez votre token hors du code source : exportez-le comme variable d'environnement et lisez-le à l'exécution.
Récupérer le HTML rendu
Commencez par obtenir la page terminée. Le client Python encapsule l'API en un seul appel get. Vous passez deux options qui comptent pour un site d'avis : ajax_wait indique à l'API d'attendre le chargement du contenu asynchrone, et page_wait attend un nombre fixe de millisecondes après le chargement pour que les fiches d'avis à rendu tardif aient le temps d'apparaître. Cinq secondes est un bon point de départ ; augmentez-les si les résultats reviennent maigres.
import os from crawlbase import CrawlingAPI # JS token renders the page in a real browser before returning HTML api = CrawlingAPI({"token": os.environ["CRAWLBASE_JS_TOKEN"]}) options = { "ajax_wait": "true", "page_wait": 5000, } def fetch_html(url): response = api.get(url, options) if response["status_code"] != 200: raise RuntimeError(f"fetch failed: {response['status_code']}") return response["body"].decode("utf-8") if __name__ == "__main__": url = "https://www.example.com/product/123/reviews" print(fetch_html(url)[:2000])
Exécutez-le et vous devriez voir un vrai balisage avec des fiches d'avis, pas la coquille vide qu'une simple récupération renvoie. Cela confirme que le rendu fonctionne avant d'écrire un seul sélecteur. Le response["body"] revient en octets depuis le client, alors décodez-le une fois et passez la chaîne au parseur.
Les pages d'avis ont besoin d'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 des IPs résidentielles côté serveur et vous remet du HTML terminé, vous évitant de gérer vous-même une flotte headless et un pool de proxies. Pointez-la vers une page d'avis publique sur le niveau gratuit d'abord.
Analyser les avis avec BeautifulSoup
Avec le HTML en main, chargez-le dans BeautifulSoup et parcourez les fiches d'avis. Chaque fiche porte les champs que vous voulez, mais les noms de classes diffèrent selon la plateforme, donc le parseur utilise une liste de sélecteurs candidats et prend le premier qui correspond. Inspectez la page en direct dans les outils de développement de votre navigateur pour trouver les sélecteurs actuels de votre cible, puis mappez chaque champ sur l'un d'eux.
import re from bs4 import BeautifulSoup def first_text(card, selectors): for sel in selectors: el = card.select_one(sel) if el: return el.get_text(separator=" ", strip=True) return "" def parse_reviews(html, source_url=""): soup = BeautifulSoup(html, "html.parser") cards = soup.select("[data-review-id], article[class*='review'], .review-card") reviews = [] for card in cards: rating_raw = first_text(card, ["[data-rating]", ".star-rating", "[class*='star']"]) match = re.search(r"(\d+(?:\.\d+)?)", rating_raw) date_el = card.select_one("time[datetime], [data-review-date]") reviews.append({ "rating": float(match.group(1)) if match else None, "title": first_text(card, [".review-title", "h3", "[class*='title']"]), "body": first_text(card, [".review-body", "[data-review-text]", "p"]), "date": (date_el.get("datetime") if date_el else ""), "verified": card.select_one("[class*='verified']") is not None, "url": source_url, }) return [r for r in reviews if r["body"]]
Les plateformes d'avis changent leurs noms de classes et leurs attributs de données sans préavis. Traitez les sélecteurs ci-dessus comme un modèle de départ, pas comme un contrat. Quand l'extraction renvoie des champs vides, réinspectez la page en direct et mettez à jour les listes de candidats. C'est une maintenance normale pour tout scraper en production, ce n'est pas le signe que quelque chose est cassé.
L'assistant first_text est ce qui rend le parseur portable : donnez-lui une courte liste de sélecteurs probables par champ et il renvoie le premier résultat, donc s'adapter à une nouvelle plateforme revient principalement à modifier ces listes plutôt qu'à réécrire la logique. Supprimer les avis avec un body vide filtre les fiches de mise en page et les emplacements publicitaires qui partagent la classe du conteneur d'avis.
Paginer sur tous les avis
Récupérer une seule page est rarement suffisant. La plupart des plateformes répartissent les avis sur des dizaines ou des centaines de pages, généralement avec un paramètre de requête de style ?page=2. Si vous ne demandez que la première page, vous manquez la majorité des données. Le schéma consiste à construire des URLs de pages dans une boucle, récupérer et analyser chacune, et s'arrêter quand une page ne renvoie aucun avis ou que vous atteignez une limite que vous fixez.
import time def scrape_all_reviews(base_url, max_pages=25): all_reviews = [] for page in range(1, max_pages + 1): sep = "&" if "?" in base_url else "?" page_url = f"{base_url}{sep}page={page}" html = fetch_html(page_url) reviews = parse_reviews(html, page_url) if not reviews: break # empty page means we ran past the last one all_reviews.extend(reviews) print(f"page {page}: {len(reviews)} reviews") time.sleep(1) # pace requests so you stay under rate limits return all_reviews
Quelques notes pratiques. Fixez un max_pages raisonnable pour qu'un changement de mise en page ne vous envoie pas dans une boucle infinie. Arrêtez dès qu'une page ne donne aucun avis. Et si votre cible utilise le défilement infini plutôt que des pages numérotées, ajoutez l'option scroll dans le dict options de la Crawling API plutôt que de construire des URLs de pages ; le reste de la boucle reste le même.
Normaliser entre plateformes
Pour un seul produit sur une plateforme, la sortie du parseur est généralement assez propre pour être stockée directement. Dès que vous récupérez des avis de plus d'une source, de petites incohérences apparaissent : un site note sur 10, un autre utilise des dates relatives comme "il y a 3 jours", et les noms de champs divergent. Une légère passe de normalisation garde tout comparable.
def normalize(review, scale=5): rating = review.get("rating") if rating is not None and scale != 5: # map any scale onto a common 0-5 range review["rating"] = round(rating / scale * 5, 2) review["body"] = " ".join(review["body"].split()) return review
Convertissez les notes sur une échelle commune, réduisez les espaces dans le corps, et alignez les noms de champs si une source les nomme différemment. Pour une seule plateforme, vous pouvez sauter cette étape ; pour l'analyse multi-plateformes, c'est ce qui empêche les données de diverger.
Stocker les résultats
La journalisation dans la console convient pendant l'itération, mais vous voulez les données sur le disque. Le CSV est la cible la plus simple et s'ouvre dans n'importe quel tableur ; le module csv de la bibliothèque standard mappe chaque clé de dictionnaire sur une colonne.
import csv def save_csv(reviews, path="reviews.csv"): fields = ["rating", "title", "body", "date", "verified", "url"] with open(path, "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=fields) writer.writeheader() writer.writerows(reviews) print(f"saved {len(reviews)} reviews to {path}")
Si vous préférez interroger les données avec SQL, écrivez les mêmes lignes dans une table SQLite avec le module sqlite3 de la bibliothèque standard ; l'analyse et la pagination restent identiques. JSON Lines est une autre bonne option quand vous voulez diffuser des enregistrements dans un pipeline en aval. Reliez les pièces et l'exécution complète n'est qu'une poignée d'appels :
if __name__ == "__main__": base = "https://www.example.com/product/123/reviews" reviews = scrape_all_reviews(base, max_pages=25) reviews = [normalize(r) for r in reviews] save_csv(reviews)
Optionnel : exécuter l'analyse de sentiment sur le texte du corps
Une fois les avis structurés, le sentiment est un ajout court. Un modèle léger basé sur des règles comme VADER vous donne un score de polarité par avis sans entraîner quoi que ce soit, ce qui suffit pour signaler les avis les plus négatifs et les plus positifs et pour suivre le sentiment moyen dans le temps.
# pip install vaderSentiment from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer analyzer = SentimentIntensityAnalyzer() def add_sentiment(reviews): for r in reviews: score = analyzer.polarity_scores(r["body"])["compound"] r["sentiment"] = round(score, 3) return reviews
Le score compound va de -1 (très négatif) à +1 (très positif). Pour un travail plus approfondi, passez le même texte du corps à un classificateur basé sur des transformeurs ou un service NLP hébergé ; le pipeline jusqu'ici ne change pas.
Passer à l'échelle sur de nombreux produits
Une boucle et un sleep conviennent pour un produit. Quand vous avez besoin d'avis sur des centaines d'URLs, vous commencez à vous battre avec la concurrence, les reprises et la planification dans votre propre code. C'est là que le Crawler entre en jeu : au lieu de récupérer les pages une par une, vous envoyez une liste d'URLs à Crawlbase et il les traite dans le cloud, rendant chaque page et livrant le HTML terminé à votre point de terminaison via un webhook. Les requêtes échouées sont réessayées pour vous, vous n'avez donc pas à surveiller une file. Pour quelques pages, la Crawling API suffit ; au-delà, le Crawler supprime la majeure partie de la charge opérationnelle.
Rester non bloqué
Même avec le rendu géré, les sites d'avis surveillent le trafic en forme de scraper. Quelques habitudes maintiennent une exécution saine, et elles s'appliquent à toute cible commerciale difficile.
-
Rythmez vos requêtes. Surcharger les avis du même produit dans une boucle serrée est le moyen le plus rapide d'être limité. Le
time.sleepdans la boucle de pagination est là intentionnellement ; gardez-le. - Misez sur la rotation. Un pool de proxies résidentiels répartit les requêtes sur de nombreuses IPs d'utilisateurs réels afin qu'aucune adresse unique n'atteigne une limite de débit. La Crawling API gère cela pour vous ; si vous construisez votre propre pile, c'est la partie à bien faire.
- Lisez les codes de statut. Une exécution qui commence à renvoyer des défis ou des erreurs vous indique que le débit actuel ou le niveau d'IP n'est plus suffisant. Traitez le statut de réponse comme un signal, pas du bruit, et ralentissez quand il change.
Pour le guide plus large, consultez comment scraper des sites sans être bloqué. Si vous souhaitez comparer cette approche gérée avec une pile headless construite à la main, le web scraping avec Python et Selenium parcourt cette construction.
La partie honnête : CGU et données personnelles
Scraper un grand site commercial se situe dans une zone grise légale, et la légalité dépend des conditions d'utilisation de la plateforme, de votre juridiction et de ce que vous faites avec les données. De nombreux sites d'avis restreignent l'accès automatisé dans leurs conditions, le scraping peut donc aller à l'encontre de ces conditions quelle que soit la prudence de vos outils. Aucun code ici ne change cela ; il fait juste fonctionner la partie technique.
Quelques lignes à respecter. Ne collectez que des avis publics : la note, le titre, le corps, la date et l'indicateur vérifié que n'importe qui peut voir sans compte. Respectez le robots.txt du site et ses attentes de débit déclarées, et maintenez votre volume de requêtes suffisamment bas pour ne pas surcharger les serveurs de quiconque. Ne collectez pas de données personnelles au-delà de ce que la plateforme affiche déjà publiquement, et n'essayez jamais de désanonymiser un évaluateur ni de joindre ses avis à des données d'ailleurs. Si vous prévoyez de réutiliser les données commercialement, obtenez une autorisation ou un accord de données officiel plutôt que de supposer que le silence est un consentement.
Ce guide est délibérément limité au contenu d'avis public car c'est la ligne qui rend le travail défendable. Il ne couvre pas ce qui se trouve derrière une connexion, les données de compte ou de profil, ni les actions effectuées en tant qu'utilisateur connecté. Si votre projet a besoin de plus que des avis publics, la bonne démarche est une API officielle ou un accord de données avec la plateforme, pas un scraper plus ingénieux. Pour un contexte sur la différence entre l'accès géré et le scraping brut, le web scraping e-commerce est une lecture complémentaire utile.
Points clés
- Les pages d'avis sont rendues côté client. Une simple récupération renvoie une coquille vide, vous devez donc rendre la page avant de l'analyser.
-
Le rendu et une IP de confiance vont ensemble. La Crawling API avec un token JS fait les deux en un seul appel ;
ajax_waitetpage_waitcontrôlent la durée d'attente du contenu. - Analysez dans un seul schéma. Capturez la note, le titre, le corps, la date et l'indicateur vérifié avec des sélecteurs candidats, et attendez-vous à ce que ces sélecteurs dérivent dans le temps.
- La pagination, c'est les données. Bouclez les URLs de pages, arrêtez-vous sur une page vide et rythmez les requêtes pour rester sous les limites de débit.
- Stockez, puis analysez. Écrivez en CSV, SQLite ou JSON Lines, et ajoutez le sentiment une fois que les données sont structurées.
- Restez sur les avis publics. Respectez les CGU et le robots.txt ; pas de comptes, pas de données personnelles au-delà de ce qui est affiché publiquement.
Foire aux questions
Comment scraper les avis clients depuis des sites à fort contenu JavaScript ?
La plupart des plateformes d'avis rendent leurs fiches côté client, donc une requête HTTP brute renvoie le statut 200 avec les avis manquants. Vous avez besoin d'une récupération basée sur un navigateur. Envoyez l'URL à la Crawling API avec un token JS et elle rend la page dans un vrai navigateur avant de remettre le HTML, de sorte que chaque avis est présent quand BeautifulSoup l'analyse. Les options ajax_wait et page_wait contrôlent la durée d'attente du contenu à chargement tardif.
Ai-je besoin du token normal ou du token JS pour scraper les avis clients ?
Le token JS. Le token normal récupère le HTML statique, qui sur un site d'avis est la même coquille vide qu'une simple récupération renvoie. Le token JS rend la page dans un vrai navigateur d'abord, de sorte que les fiches d'avis existent dans le HTML au moment où votre parseur s'exécute.
Comment obtenir tous les avis et pas seulement la première page ?
La plupart des plateformes paginaient avec un paramètre de style ?page=2. Construisez les URLs de pages dans une boucle, récupérez et analysez chacune, et arrêtez-vous quand une page ne renvoie aucun avis ou que vous atteignez une limite que vous fixez. Pour les sites qui utilisent le défilement infini plutôt que des pages numérotées, passez l'option scroll de la Crawling API plutôt que de construire des URLs de pages ; le reste de la boucle reste le même.
Mes sélecteurs renvoient des champs vides. Qu'est-ce qui a changé ?
Presque certainement le balisage de la plateforme. Les sites d'avis changent leurs noms de classes et leurs attributs de données sans préavis, de sorte que des sélecteurs qui fonctionnaient le mois dernier peuvent se casser. Réinspectez une page d'avis en direct dans les outils de développement de votre navigateur et mettez à jour les listes de sélecteurs candidats dans le parseur. La maintenance périodique des sélecteurs est normale pour tout scraper en production.
Puis-je exécuter une analyse de sentiment sur les avis scrapés ?
Oui. Une fois les avis normalisés dans un schéma cohérent, le texte du corps s'insère directement dans une étape NLP. Un modèle basé sur des règles comme VADER vous donne un score de polarité par avis sans entraînement ; pour plus de nuance, passez le même texte à un classificateur de transformeurs ou à un service NLP hébergé. Le pipeline de scraping ne change pas.
Est-il légal de scraper les avis clients ?
Cela dépend des conditions d'utilisation de la plateforme, de votre juridiction et de votre objectif, et de nombreux sites restreignent l'accès automatisé. Restez strictement sur le contenu d'avis public, respectez le robots.txt et les attentes de débit, et ne collectez pas de données personnelles au-delà de ce qui est affiché publiquement ni n'essayez d'identifier des évaluateurs individuels. Pour une réutilisation commerciale, obtenez une autorisation ou un accord de données officiel plutôt que de vous appuyer sur un scraper.
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.

