Groupon publie des milliers d'offres locales par jour dans les domaines de la restauration, du voyage, du bien-être et du commerce de détail, et chaque annonce contient exactement le type de données structurées qu'un outil de suivi d'offres, un observateur de prix ou un tableau de bord de veille concurrentielle recherche : un titre, le prix remisé, le prix d'origine, l'économie réalisée, la zone géographique couverte par l'offre et un lien vers l'annonce. Le problème, c'est que Groupon construit ses pages de catégorie dans le navigateur avec JavaScript, de sorte qu'une simple requête HTTP vous renvoie une coque quasi vide plutôt que les offres que vous cherchez.
Ce guide vous montre comment scraper Groupon avec Python de manière fiable. Vous construisez un petit scraper fonctionnel qui récupère une page de catégorie rendue via la Crawling API, parse chaque carte d'offre avec BeautifulSoup et affiche une sortie structurée propre. L'ensemble du tutoriel se limite aux données d'offres publiques, et la section sur la légalité vers la fin n'est pas du texte de remplissage, alors lisez-la avant de pointer ce script sur un volume réel.
Ce que vous allez construire
Un script Python qui prend une URL publique de catégorie Groupon, récupère le HTML rendu via la Crawling API, et extrait un enregistrement structuré pour chaque offre sur la page. Nous utiliserons une page d'offres locales comme exemple et extrairons ces champs de chaque carte :
- Titre le titre de l'offre, par exemple « Massage en couple de 55 minutes ».
- Prix actuel le prix remisé que vous payez réellement.
- Prix d'origine le prix barré affiché.
- Remise l'économie réalisée, calculée à partir des deux prix.
- Localisation l'adresse ou la zone à laquelle l'offre s'applique.
- Lien l'URL de la page de l'offre individuelle.
Pourquoi une requête simple échoue sur Groupon
Si vous interrogez une URL de catégorie Groupon avec un client HTTP basique, vous obtenez une réponse avec le statut 200 et presque aucune des données d'offres dans le corps. Deux éléments jouent contre vous. Premièrement, Groupon rend ses cartes d'offres dans le navigateur avec JavaScript, donc le HTML initial est un cadre qui ne se remplit qu'après l'exécution des scripts de la page. Deuxièmement, Groupon signale rapidement le trafic automatisé : les IP de datacenters et les schémas de requêtes qui ne ressemblent pas à un vrai navigateur sont challengés ou bloqués avant même d'atteindre les cartes rendues.
Un scraper Groupon 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 reconnaît comme un vrai visiteur. 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 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 elle renvoie le HTML terminé pour que vous le parsiez.
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. Groupon est rendu côté client, donc vous avez besoin du token JS ici. L'utilisation du token normal renvoie le même cadre vide qu'une simple requête, et il n'y a rien à parser. Vous pouvez commencer avec jusqu'à 20 000 requêtes gratuites, sans carte de crédit.
Prérequis
Vous avez besoin de quelques éléments en place avant d'écrire du code. Aucun ne prend longtemps.
Python de base. Vous devez être à l'aise pour écrire et exécuter un script Python et installer des packages avec pip. Si BeautifulSoup est nouveau pour vous, notre guide sur l'utilisation de BeautifulSoup en Python couvre les bases du parsing que ce tutoriel suppose connues.
Python 3.8 ou ultérieur. 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 ne le mettez pas dans le contrôle de version.
Configurer le projet
Créez un environnement virtuel pour que les dépendances du projet restent isolées, puis installez les deux bibliothèques dont le scraper a besoin.
python --version python -m venv groupon_env source groupon_env/bin/activate pip install crawlbase beautifulsoup4
Sous Windows, activez l'environnement avec groupon_env\Scripts\activate au lieu de la ligne source. Deux dépendances font le travail : crawlbase est le client officiel pour la Crawling API, et beautifulsoup4 parse le HTML renvoyé afin que vous puissiez extraire des champs individuels par sélecteur CSS.
Étape 1 : Récupérer la page de catégorie rendue
Commencez par obtenir la page terminée. Importez la classe CrawlingAPI, initialisez-la avec votre token JS, et requêtez l'URL de la catégorie. Vérifier le statut avant de parser rend les échecs bruyants 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.groupon.com/local/washington-dc" html = crawl(page_url) print(html[:500] if html else "No HTML returned")
Les deux options d'attente comptent pour une cible rendue côté client comme celle-ci. ajax_wait demande à l'API d'attendre que le contenu asynchrone finisse de se charger, et page_wait attend un nombre fixe de millisecondes après le chargement pour que les cartes à rendu tardif apparaissent avant la capture de la page. Cinq secondes est un bon point de départ ; augmentez si les cartes d'offres reviennent vides. Exécutez le script avec python scraper.py et vous devriez voir le vrai balisage des offres, pas le cadre vide qu'une requête simple renvoie. Cela confirme que le rendu fonctionne avant d'écrire un seul sélecteur.
Groupon 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 terminé, vous épargnant la gestion d'une flotte headless et d'un pool de proxies. Pointez-la sur une page publique d'offres locales sur le niveau gratuit d'abord.
Étape 2 : Parser les cartes d'offres avec BeautifulSoup
Avec le HTML rendu en main, chargez-le dans BeautifulSoup et extrayez chaque offre par son sélecteur. Groupon dispose ses cartes d'offres dans une structure répétée, vous sélectionnez donc toutes les cartes une fois puis lisez les mêmes champs sur chacune. Inspectez la page en direct dans les outils de développement de votre navigateur pour confirmer les noms de classes actuels ; les sélecteurs ci-dessous correspondent à la mise en page au moment de la rédaction.
from bs4 import BeautifulSoup def text_of(card, selector): el = card.select_one(selector) return el.get_text(strip=True) if el else None def parse_deals(html): soup = BeautifulSoup(html, "html.parser") cards = soup.select('div[data-item-type="card"] > article') deals = [] for card in cards: link = card.select_one("a[href]") deals.append({ "title": text_of(card, "h2.text-dealCardTitle"), "current_price": text_of(card, 'div[data-testid="green-price"]'), "original_price": text_of(card, 'div[data-testid="strike-through-price"]'), "location": text_of(card, "h2 + div span"), "link": link["href"] if link else None, }) return deals
L'assistant text_of fait deux choses utiles à la fois : il interroge un seul élément à l'intérieur de la carte et renvoie None lorsque cet élément est absent, au lieu de lever une exception lors d'un appel .get_text() sur rien. Cela rend l'extraction résistante lorsqu'un champ est absent sur une carte donnée, ce qui est courant car toutes les offres n'indiquent pas un prix d'origine barré ou une localisation. Le lien est lu depuis l'attribut href d'une ancre plutôt que depuis son texte, il est donc traité séparément.
Les noms de classes et les attributs data-testid de Groupon changent sans préavis. Traitez les sélecteurs ci-dessus comme un modèle de départ, pas comme un contrat. Quand un champ revient comme None pour toutes les cartes, réinspectez une offre 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 de production, ce n'est pas le signe que quelque chose est cassé.
Étape 3 : Calculer la remise
Groupon affiche parfois un badge de remise, mais l'économie la plus fiable est celle que vous calculez vous-même à partir des deux prix déjà extraits. Parsez la valeur numérique de chaque chaîne de prix, puis calculez le pourcentage. Faire le calcul vous-même signifie que le champ est cohérent sur toutes les cartes, que la page affiche ou non un badge.
import re def to_amount(price): if not price: return None match = re.search(r"[\d,.]+", price) return float(match.group().replace(",", "")) if match else None def discount_percent(original, current): o, c = to_amount(original), to_amount(current) if not o or not c or o <= 0: return None return round((o - c) / o * 100)
L'assistant to_amount supprime les symboles monétaires et les séparateurs de milliers pour que "$45" et "$1,299.00" soient tous deux parsés proprement, et il renvoie None plutôt que de planter quand un prix est manquant. discount_percent vérifie qu'il n'y a pas de prix d'origine nul ou absent avant de diviser, de sorte qu'une carte sans prix barré produit simplement une remise à None au lieu de lever une exception. Intégrez le résultat dans chaque enregistrement dans la boucle avec "discount": discount_percent(original_price, current_price).
Étape 4 : Tout assembler
Câblez maintenant la récupération, le parsing et le calcul de remise en un seul script exécutable. Récupérez le HTML rendu, passez-le au parser, enrichissez chaque enregistrement avec la remise calculée, et écrivez le résultat en JSON.
import json import re 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} 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(card, selector): el = card.select_one(selector) return el.get_text(strip=True) if el else None def to_amount(price): if not price: return None match = re.search(r"[\d,.]+", price) return float(match.group().replace(",", "")) if match else None def discount_percent(original, current): o, c = to_amount(original), to_amount(current) if not o or not c or o <= 0: return None return round((o - c) / o * 100) def parse_deals(html): soup = BeautifulSoup(html, "html.parser") cards = soup.select('div[data-item-type="card"] > article') deals = [] for card in cards: link = card.select_one("a[href]") current = text_of(card, 'div[data-testid="green-price"]') original = text_of(card, 'div[data-testid="strike-through-price"]') deals.append({ "title": text_of(card, "h2.text-dealCardTitle"), "current_price": current, "original_price": original, "discount": discount_percent(original, current), "location": text_of(card, "h2 + div span"), "link": link["href"] if link else None, }) return deals def main(): page_url = "https://www.groupon.com/local/washington-dc" html = crawl(page_url) if not html: return deals = parse_deals(html) with open("groupon_deals.json", "w") as f: json.dump(deals, f, indent=2) print(f"Saved {len(deals)} deals") 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 chaque offre, prêt à être écrit en JSON, CSV ou dans une base de données.
[ { "title": "55-Minute Couples Massage or 50-Minute Deep Tissue Massage", "current_price": "$65", "original_price": "$95", "discount": 32, "location": "4238 Wilson Boulevard, Arlington", "link": "https://www.groupon.com/deals/refresh-therapeutic-massage-3" }, { "title": "Spa Package with Glass of Wine at Spa Logic", "current_price": "$219", "original_price": "$330", "discount": 34, "location": "1721 Connecticut Avenue Northwest, Washington", "link": "https://www.groupon.com/deals/spa-logic-12" } ]
Boucler sur les pages de catégorie et réguler les requêtes
Une catégorie est une démo ; un vrai travail s'étend sur plusieurs villes ou verticales. La structure reste la même : conservez une liste d'URL de catégories, récupérez chacune via la Crawling API, parsez-la avec la même fonction et collectez les lignes. Comme chaque page de catégorie partage la même structure de carte, le parser que vous avez déjà écrit fonctionne sur toutes sans modification. L'habitude qui maintient un long run en bonne santé est la régulation : faites une pause entre les requêtes pour ne pas marteler Groupon dans une boucle serrée.
import time categories = [ "https://www.groupon.com/local/washington-dc", "https://www.groupon.com/local/new-york-city", "https://www.groupon.com/local/chicago", ] results = [] for url in categories: html = crawl(url) if html: results.extend(parse_deals(html)) time.sleep(3) with open("groupon_deals.json", "w") as f: json.dump(results, f, indent=2)
Groupon charge également plus d'offres derrière un bouton « Charger plus » plutôt que sur une page numérotée. Si vous voulez les cartes sous le pli, passez une option css_click_selector à la Crawling API pointant vers ce bouton, et l'API clique dessus avant de capturer la page. Inspectez le bouton en direct dans les outils de développement pour lire son sélecteur actuel, car cet attribut évolue comme le reste du balisage.
Maintenir l'accès
Même avec le rendu géré, Groupon surveille le trafic de type scraper. Quelques habitudes maintiennent un run en bonne santé, et elles s'appliquent à n'importe quelle cible commerciale difficile.
- Réglez le débit de vos requêtes. Marteler les pages de catégorie dans une boucle serrée est le moyen le plus rapide d'être throttlé. Espacez les requêtes et variez vos cibles au lieu de crawler une seule ville à 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 afin qu'aucune ne dépasse une limite de débit. La Crawling API gère cela pour vous ; si vous construisez votre propre pile, c'est la partie à bien maîtriser.
- Lisez les codes de statut. Un run qui commence à renvoyer des challenges ou des erreurs vous indique que le débit ou le niveau d'IP actuel n'est plus suffisant. Traitez cela comme un signal pour ralentir, pas comme du bruit à ignorer.
Pour le guide complet, consultez comment scraper des sites web sans se faire bloquer et l'analyse approfondie de comment contourner les captchas lors du web scraping. Si votre cible est rendue côté client comme Groupon, notre guide sur le crawl des sites web JavaScript explique pourquoi le rendu est important. Et si vous préférez acheminer votre propre trafic via un pool rotatif plutôt que d'utiliser l'API gérée, le Smart AI Proxy (aussi appelé l'AI Proxy) vous offre la même rotation d'IP résidentielles comme endpoint proxy clé en main.
Est-il légal de scraper Groupon ?
La légalité du scraping de Groupon dépend des conditions d'utilisation de Groupon, de votre juridiction et de ce que vous faites des données. Les conditions de Groupon imposent des limites à l'accès automatisé, donc le scraping peut aller à l'encontre de ces conditions quelle que soit la prudence de vos outils. Rien dans ce code ne change cela ; il rend simplement la partie technique fonctionnelle. Lisez les conditions d'utilisation de Groupon et son fichier robots.txt, et traitez les deux comme la frontière de ce que vous collectez.
Quelques règles à respecter. Collectez uniquement les données d'offres publiques : le titre, le prix actuel, le prix d'origine, la remise, la localisation et le lien que n'importe qui peut voir sur une page de catégorie sans compte. Respectez les attentes de débit déclarées par Groupon et maintenez votre volume de requêtes suffisamment bas pour ne pas surcharger ses serveurs. Si vous prévoyez de réutiliser les données commercialement, obtenez la permission ou un accord officiel plutôt que de supposer que le silence vaut consentement.
Ce guide est délibérément limité aux pages d'offres et de catégories publiques car c'est la limite qui rend le travail défendable. Il ne couvre rien derrière une connexion, les données de compte ou personnelles, les détails des clients, les pages nécessitant une authentification, ni toute tentative de contourner l'authentification. Données d'offres publiques uniquement. Si votre projet nécessite davantage, un accord de données officiel est la bonne voie, pas un scraper plus astucieux.
Points clés
- Groupon est rendu côté client. Une simple requête renvoie un cadre vide, vous devez donc rendre la page avant de la parser.
-
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. - BeautifulSoup fait l'extraction. Sélectionnez toutes les cartes d'offres, puis lisez le titre, le prix actuel, le prix d'origine, la localisation et le lien de chacune, et attendez-vous à ce que les sélecteurs évoluent.
- Calculez la remise vous-même. Parser les deux prix et calculer l'économie est plus cohérent que scraper un badge qui n'est pas toujours présent.
- Montez en échelle en bouclant sur les catégories et en régulant le débit. Le même parser fonctionne sur toutes les pages de catégorie, donc un vrai travail est une liste d'URL plus une pause entre les requêtes.
- Restez sur les données publiques. Respectez les CGU et le robots.txt de Groupon, et ne touchez jamais aux comptes, données personnelles ou pages nécessitant une connexion.
Foire aux questions
Pourquoi une simple requête ne renvoie-t-elle aucune offre de Groupon ?
Parce que Groupon rend ses cartes d'offres côté client avec JavaScript. Le HTML initial est un cadre qui ne se remplit qu'après l'exécution des scripts de la page dans un navigateur, donc une requête HTTP brute renvoie le statut 200 avec les cartes vides. Pour obtenir des données réelles, 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 Groupon ?
Le token JS. Le token normal récupère le HTML statique, qui sur Groupon est le même cadre vide qu'une simple requête renvoie. Le token JS rend la page dans un vrai navigateur avant de renvoyer le HTML, de sorte que les cartes d'offres sont présentes quand BeautifulSoup les parse.
Comment scraper toutes les offres sous le bouton « Charger plus » ?
Groupon charge des offres supplémentaires derrière un bouton plutôt que sur une page numérotée. Passez une option css_click_selector à la Crawling API avec le sélecteur CSS du bouton, et l'API clique dessus avant de capturer la page pour que les cartes supplémentaires soient dans le HTML que vous parsez. Confirmez le sélecteur actuel du bouton dans les outils de développement de votre navigateur, car cet attribut change au fil du temps.
Mes sélecteurs renvoient None pour toutes les cartes. Qu'est-ce qui a changé ?
Presque certainement le balisage de Groupon. Ses noms de classes et ses attributs data-testid changent sans préavis, donc les sélecteurs qui fonctionnaient le mois dernier peuvent se casser. Réinspectez une offre 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 de production.
Puis-je scraper les données de compte ou personnelles de Groupon ?
Non, et ce guide ne le couvre pas. Les détails de compte, les données personnelles et tout ce qui se trouve derrière une connexion se situent en dehors des pages d'offres publiques, ils ne font donc pas partie de cette approche et vont à l'encontre des conditions de Groupon. Le scraping de contenu nécessitant une connexion, ou le contournement de l'authentification pour l'atteindre, n'entre pas dans le cadre de cette approche. Restez sur les données d'offres et de catégories publiques.
Comment éviter d'être bloqué en scrapant Groupon ?
Maintenez un faible débit de requêtes par IP, variez vos cibles au lieu de boucler sur une seule ville, et acheminez via des IP résidentielles rotatives afin qu'aucune adresse unique ne dépasse 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 pile, c'est là que vous devez investir. Surveillez les codes de statut et ralentissez 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.
