Retour au blog
Guides & Tutoriels2026-02-12·7 min de lecture

Comment Scraper les Avis Google avec Python — Guide Complet 2025

Par Ibrahim DemolCEO IBLeadMis à jour le 26 mars 2026

Les avis Google sont une mine d'or de retours clients, mais collecter manuellement des milliers d'entre eux ? C'est inefficace et peu pratique.

Avec Python, vous pouvez automatiser l'ensemble du processus d'extraction et rassembler des milliers d'avis en quelques heures. Ce guide vous accompagne à travers tout — de la configuration de base aux scrapers prêts pour la production qui gèrent les défenses anti-scraping de Google.

À la fin, vous comprendrez comment construire des scrapers d'avis fiables, éviter la détection et extraire des informations clients exploitables à grande échelle.


Qu'est-ce que le Scraping des Avis Google ?

Le scraping des avis Google est l'extraction automatisée des données de retour client à partir de Google Maps et des profils Google Business en utilisant des scripts Python.

Lorsque vous scrapez les avis Google, vous capturez :

  • Évaluations par étoiles (1 à 5 étoiles)
  • Texte de l'avis (commentaires complets des clients)
  • Noms et profils des évaluateurs
  • Dates et horodatages des avis
  • Réponses des entreprises (si le propriétaire a répondu)
  • Nombre de votes utiles

Contrairement à l'API officielle de Google — qui est coûteuse et fortement limitée en termes de taux — le scraping vous donne accès à tous les avis disponibles sans quotas ni restrictions.

Pourquoi Scraper au Lieu d'Utiliser l'API de Google ?

L'API officielle de Google Places pour les avis a de sérieuses limitations :

Fonctionnalité API Officielle Web Scraping
Avis par demande ~10 (récents seulement) Tous disponibles
Coût 7 $ par 1 000 demandes Gratuit à faible coût
Limites de taux 100 demandes/seconde max Configurables
Avis des concurrents ❌ Non ✅ Oui
Complexité de configuration Modérée (authentification) Plus élevée (technique)

Pour l'analyse concurrentielle, la recherche de marché ou la collecte de données à grande échelle, le scraping est le choix pratique.


Pourquoi Python est le Meilleur Langage pour Cette Tâche

Python domine le web scraping pour trois raisons principales :

1. Écosystème de Scraping Riche

Python dispose de bibliothèques spécialement conçues pour le web scraping :

  • Playwright — Automatisation de navigateur moderne et rapide
  • Selenium — Éprouvé, compatibilité maximale
  • BeautifulSoup — Analyse HTML légère
  • Scrapy — Framework de scraping de niveau entreprise
  • Pandas — Manipulation et exportation de données

Vous pouvez passer de HTML brut à CSV nettoyé en un seul script.

2. Exécution de JavaScript

Les avis Google se chargent dynamiquement via JavaScript, et non dans le HTML initial. Les outils d'automatisation de navigateur de Python (Playwright, Selenium) exécutent JavaScript comme un vrai navigateur, vous permettant de :

  • Déclencher le défilement infini
  • Cliquez sur les boutons "Afficher plus"
  • Attendre que le contenu apparaisse
  • Interagir avec les éléments de la page

3. Fonctionnalités Anti-Détection Intégrées

Les bibliothèques Python modernes sont livrées avec des capacités de furtivité prêtes à l'emploi :

# Masquer les indicateurs d'automatisation
options.add_argument("--disable-blink-features=AutomationControlled")
options.add_experimental_option("excludeSwitches", ["enable-automation"])

Ces indicateurs empêchent Google de détecter votre script comme un bot.

4. Pipeline de Traitement de Données Transparent

Avec Python, vous scrapez → nettoyez → analysez → exportez le tout dans un seul script. Aucun changement d'outil requis.


Le Défi : Pourquoi Google Rend le Scraping Difficile

Google emploie plusieurs couches anti-scraping. Comprendre cela vous aide à construire des scrapers résilients.

1. Chargement de Contenu Dynamique

Les avis Google ne se chargent pas tous en même temps. Au lieu de cela :

  • Le chargement initial de la page montre 10 à 20 avis
  • Des avis supplémentaires se chargent via AJAX au fur et à mesure que vous faites défiler
  • Chaque lot nécessite des requêtes réseau séparées
  • Le mécanisme change fréquemment

Solution : Utilisez l'automatisation de navigateur (Playwright/Selenium) qui exécute JavaScript et simule le défilement.

2. Détection de Bots Sophistiquée

Google analyse :

  • Empreinte du navigateur — Résolution d'écran, polices installées, fuseau horaire
  • Modèles comportementaux — Mouvements de la souris, vitesse de défilement, timing des clics
  • Fréquence des requêtes — Détection des modèles de requêtes non humains
  • Réputation IP — Signalement des adresses suspectes

Solution : Faites tourner des proxies, ajoutez des délais aléatoires, utilisez des agents utilisateurs réalistes.

3. Limitation de Taux et CAPTCHA

Frappez Google trop fort, et vous ferez face à :

  • Blocages IP temporaires (24 à 48 heures)
  • Défis CAPTCHA
  • Refus d'accès complet
  • Throttling progressif

Solution : Mettez en œuvre un throttling des requêtes (délais de 1 à 3 secondes entre les actions).

4. Structure HTML en Évolution Constante

Google met régulièrement à jour la structure de sa page, cassant les sélecteurs CSS du jour au lendemain.

Solution : Utilisez plusieurs sélecteurs de secours et des requêtes basées sur les attributs au lieu de noms de classe fragiles.


Méthode 1 : Playwright — L'Approche Moderne

Playwright est le choix recommandé pour 2025. Il est plus rapide que Selenium, possède de meilleures fonctionnalités anti-detection et gère sans effort les sites modernes riches en JavaScript.

Configuration de Playwright

Tout d'abord, créez un environnement virtuel et installez les dépendances :

# Créer un environnement virtuel
python -m venv google_scraper
source google_scraper/bin/activate  # Sur Windows : google_scraper\Scripts\activate

# Installer les paquets requis
pip install playwright pandas beautifulsoup4 lxml emoji

# Installer le navigateur
playwright install chromium

Code Complet du Scraper Playwright

Voici un scraper prêt pour la production qui gère les complexités des avis Google :

from playwright.sync_api import sync_playwright
import pandas as pd
import re
import emoji
import logging
import time
import random
from datetime import datetime

# Configurer la journalisation
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

class GoogleReviewsScraper:
    def __init__(self, headless=True):
        self.headless = headless
        self.reviews_data = []

    def clean_text(self, text):
        """Supprimer les emojis et normaliser le texte"""
        if not text:
            return ""
        text = emoji.replace_emoji(text, replace='')
        text = re.sub(r'\s+', ' ', text).strip()
        return text

    def random_delay(self, min_delay=1, max_delay=3):
        """Ajouter des délais aléatoires pour imiter le comportement humain"""
        delay = random.uniform(min_delay, max_delay)
        time.sleep(delay)

    def initialize_browser(self):
        """Initialiser Playwright avec des paramètres furtifs"""
        playwright = sync_playwright().start()
        browser = playwright.chromium.launch(
            headless=self.headless,
            args=[
                '--disable-blink-features=AutomationControlled',
                '--disable-extensions',
                '--no-sandbox',
                '--disable-dev-shm-usage',
                '--disable-gpu'
            ]
        )

        context = browser.new_context(
            user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            viewport={'width': 1366, 'height': 768}
        )

        page = context.new_page()

        # Masquer la propriété webdriver
        page.add_init_script("""
            Object.defineProperty(navigator, 'webdriver', {
                get: () => undefined,
            });
        """)

        return playwright, browser, page

    def search_business(self, page, business_name):
        """Rechercher une entreprise sur Google Maps"""
        try:
            page.goto("https://www.google.com/maps", wait_until="networkidle")
            self.random_delay(2, 4)

            # Trouver la boîte de recherche et entrer le nom de l'entreprise
            search_box = page.locator("input[id='searchboxinput']")
            search_box.fill(business_name)
            search_box.press("Enter")

            # Attendre que les résultats se chargent
            page.wait_for_timeout(5000)
            logger.info(f"Recherche de : {business_name}")
            return True

        except Exception as e:
            logger.error(f"Erreur lors de la recherche de l'entreprise : {e}")
            return False

    def navigate_to_reviews(self, page):
        """Naviguer vers la section des avis"""
        try:
            # Chercher l'onglet des avis
            reviews_tab = page.get_by_role("tab", name=re.compile("Avis|avis", re.IGNORECASE))
            if reviews_tab.is_visible():
                reviews_tab.click()
                page.wait_for_timeout(3000)
                logger.info("Navigué vers la section des avis")
                return True
            else:
                logger.warning("Onglet des avis non trouvé")
                return False

        except Exception as e:
            logger.error(f"Erreur lors de la navigation vers les avis : {e}")
            return False

    def scroll_and_load_reviews(self, page, max_reviews=100):
        """Faire défiler pour charger plus d'avis dynamiquement"""
        loaded_reviews = 0
        scroll_attempts = 0
        max_scroll_attempts = 20

        while loaded_reviews < max_reviews and scroll_attempts < max_scroll_attempts:
            try:
                # Faire défiler vers le bas
                page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
                self.random_delay(2, 4)

                # Compter les avis actuels
                current_reviews = page.locator('[data-review-id]').count()

                if current_reviews > loaded_reviews:
                    loaded_reviews = current_reviews
                    logger.info(f"Chargé {loaded_reviews} avis...")
                    scroll_attempts = 0  # Réinitialiser le compteur
                else:
                    scroll_attempts += 1

                # Essayer de cliquer sur le bouton "Plus d'avis" si disponible
                try:
                    more_button = page.locator("button:has-text('Plus'), button:has-text('plus')")
                    if more_button.is_visible():
                        more_button.click()
                        self.random_delay(2, 3)
                except:
                    pass

            except Exception as e:
                logger.error(f"Erreur lors du défilement : {e}")
                break

        logger.info(f"Chargement terminé. Total des avis : {loaded_reviews}")
        return loaded_reviews

    def extract_review_data(self, page):
        """Extraire les données d'avis individuelles de la page chargée"""
        reviews = []

        try:
            review_elements = page.locator('[data-review-id]').all()

            for element in review_elements:
                try:
                    review_data = {}

                    # Extraire le nom de l'évaluateur
                    try:
                        name_element = element.locator('div[class*="name"] span').first
                        review_data['reviewer_name'] = name_element.inner_text() if name_element.is_visible() else "Anonyme"
                    except:
                        review_data['reviewer_name'] = "Anonyme"

                    # Extraire l'évaluation
                    try:
                        rating_element = element.locator('[role="img"][aria-label*="star"]').first
                        if rating_element.is_visible():
                            rating_text = rating_element.get_attribute('aria-label')
                            rating_match = re.search(r'(\d+)', rating_text)
                            review_data['rating'] = int(rating_match.group(1)) if rating_match else None
                        else:
                            review_data['rating'] = None
                    except:
                        review_data['rating'] = None

                    # Extraire le texte de l'avis
                    try:
                        text_elements = element.locator('span[class*="review-text"], div[class*="review-text"]').all()
                        review_text = ""
                        for text_elem in text_elements:
                            if text_elem.is_visible():
                                review_text += text_elem.inner_text() + " "
                        review_data['review_text'] = self.clean_text(review_text.strip())
                    except:
                        review_data['review_text'] = ""

                    # Extraire la date
                    try:
                        date_element = element.locator('span[class*="date"], div[class*="date"]').first
                        review_data['review_date'] = date_element.inner_text() if date_element.is_visible() else "Inconnu"
                    except:
                        review_data['review_date'] = "Inconnu"

                    # Extraire le nombre d'aides
                    try:
                        helpful_element = element.locator('[aria-label*="helpful"], [aria-label*="Helpful"]').first
                        if helpful_element.is_visible():
                            helpful_text = helpful_element.get_attribute('aria-label')
                            helpful_match = re.search(r'(\d+)', helpful_text)
                            review_data['helpful_count'] = int(helpful_match.group(1)) if helpful_match else 0
                        else:
                            review_data['helpful_count'] = 0
                    except:
                        review_data['helpful_count'] = 0

                    # Ajouter uniquement les avis avec un contenu textuel réel
                    if review_data['review_text']:
                        reviews.append(review_data)

                except Exception as e:
                    logger.warning(f"Erreur lors de l'extraction d'un avis individuel : {e}")
                    continue

            logger.info(f"Extraction réussie de {len(reviews)} avis")
            return reviews

        except Exception as e:
            logger.error(f"Erreur lors de l'extraction des avis : {e}")
            return []

    def scrape_reviews(self, business_name, max_reviews=100):
        """Méthode principale de scraping"""
        playwright, browser, page = self.initialize_browser()

        try:
            # Rechercher l'entreprise
            if not self.search_business(page, business_name):
                return []

            # Naviguer vers l'onglet des avis
            if not self.navigate_to_reviews(page):
                return []

            # Charger les avis en faisant défiler
            self.scroll_and_load_reviews(page, max_reviews)

            # Extraire les données des avis
            reviews = self.extract_review_data(page)
            self.reviews_data = reviews
            return reviews

        except Exception as e:
            logger.error(f"Le scraping a échoué : {e}")
            return []

        finally:
            browser.close()
            playwright.stop()

    def save_to_csv(self, filename="google_reviews.csv"):
        """Enregistrer les avis dans un fichier CSV"""
        if self.reviews_data:
            df = pd.DataFrame(self.reviews_data)
            df['extraction_date'] = datetime.now().isoformat()
            df.to_csv(filename, index=False, encoding='utf-8')
            logger.info(f"Avis enregistrés dans {filename}")
            print(f"✅ Exporté {len(self.reviews_data)} avis dans {filename}")
        else:
            logger.warning("Aucun avis à enregistrer")

# Exemple d'utilisation
if __name__ == "__main__":
    scraper = GoogleReviewsScraper(headless=False)  # Mettre à True pour la production

    business_name = "Starbucks Times Square New York"
    reviews = scraper.scrape_reviews(business_name, max_reviews=50)

    if reviews:
        scraper.save_to_csv(f"avis_{business_name.replace(' ', '_')}.csv")
        print(f"✅ Scraping réussi de {len(reviews)} avis !")
    else:
        print("❌ Aucun avis n'a été scrapé.")

Comment Ce Scraper Fonctionne

1. Configuration Furtive

Le scraper cache les indicateurs d'automatisation que Google utilise pour détecter les bots :

options.add_argument("--disable-blink-features=AutomationControlled")
page.add_init_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")

2. Délais Aléatoires

Au lieu de lancer des requêtes instantanément, le scraper attend 1 à 3 secondes entre les actions pour imiter la navigation humaine.

3. Défilement Dynamique

Il fait défiler jusqu'en bas de la section des avis à plusieurs reprises, déclenchant le mécanisme de défilement infini de Google pour charger plus d'avis.

4. Gestion des Erreurs Robuste

Si un sélecteur échoue (parce que Google a changé le HTML), le scraper essaie des sélecteurs de secours au lieu de planter.

5. Nettoyage des Données

Les avis sont nettoyés pour supprimer les emojis, normaliser les espaces et garantir des données de qualité.


Méthode 2 : Selenium — L'Alternative Éprouvée

Bien que Playwright soit plus rapide, Selenium reste la référence en matière de compatibilité et dispose d'une communauté massive. Utilisez Selenium si vous avez besoin d'un support maximal pour les navigateurs ou si vous avez une infrastructure Selenium existante.

Installation de Selenium

pip install selenium webdriver-manager pandas beautifulsoup4

# Télécharger ChromeDriver (géré automatiquement par webdriver-manager)

Implémentation Complète de Selenium

```python from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.action_chains import ActionChains from selenium.common.exceptions import TimeoutException, NoSuchElementException import pandas as pd import time import random import re import logging from datetime import datetime

logging.basicConfig(level=logging.INFO) logger = logging.getLogger(name)

class SeleniumGoogleReviewsScraper: def init(self, headless=True): self.headless = headless self.driver = None self.wait = None self.reviews_data = []

def setup_driver(self):
    """Configurer et initialiser le driver Chrome"""
    options = Options()

    if self.headless:
        options.add_argument("--headless")

    # Mesures anti-détection
    options.add_argument("--disable-blink-features=AutomationControlled")
    options.add_experimental_option("excludeSwitches", ["enable-automation"])
    options.add_experimental_option('useAutomationExtension', False)
    options.add_argument("--disable-extensions")
    options.add_argument("--no-sandbox")
    options.add_argument("--disable-dev-shm-usage")
    options.add_argument("--disable-gpu")
    options.add_argument("--window-size=1366,768")
    options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")

    self.driver = webdriver.Chrome(options=options)

    # Masquer la propriété webdriver
    self.driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined,});")

    self.wait = WebDriverWait(self.driver, 20)
    logger.info("Driver Chrome initialisé")

def random_delay(self, min_seconds=1, max_seconds=3):
    """Ajouter des délais aléatoires entre les actions"""
    delay = random.uniform(min_seconds, max_seconds)
    time.sleep(delay)

def search_google_maps(self, business_name):
    """Rechercher une entreprise sur Google Maps"""
    try:
        self.

Prêt à commencer ?

Accédez à toutes les entreprises Google Maps, enrichies avec emails et données légales.

Essayer IBLead gratuitement