Multi-Channel Selling

Surveiller les prix des concurrents e-commerce avec un scraper

Créez un scraper durable pour surveiller les prix e-commerce : données à collecter, extraction, rapprochement produit, historique et respect de robots.txt.

Photo de profil de Long Nguyen

Long Nguyen

Développeur fullstack · Ingénieur IA · Chercheur

• • 4 min de lecture •

Ce qu'un scraper de surveillance des prix doit faire

Un scraper personnalisé pour surveiller les prix des concurrents e-commerce est un pipeline, pas un simple script. Il récupère les pages produits des concurrents, extrait le prix et la disponibilité, associe chaque page à l'un de vos SKU, enregistre chaque observation et vous informe lorsqu'une donnée change ou qu'un élément ne fonctionne plus.

La récupération et l'analyse des pages sont les parties les plus simples. La fiabilité des données dépend surtout du rapprochement des produits, de l'historique et de la détection des erreurs :

  1. Récupérer les URL produits avec précaution, selon un calendrier défini.
  2. Extraire le prix, la devise, le prix barré, la disponibilité et les frais de livraison.
  3. Associer chaque URL concurrente à l'un de vos SKU.
  4. Enregistrer chaque observation, et pas seulement le dernier prix.
  5. Déclencher des alertes en cas d'évolution significative du prix ou d'échec du scraper.

La suite de ce guide suit cet ordre.

Scraper personnalisé ou outil de surveillance des prix : quand créer sa solution ?

Un outil de surveillance hébergé est généralement le moyen le plus rapide de démarrer. Un scraper personnalisé devient plus pertinent lorsque votre besoin ne correspond pas aux hypothèses de l'outil.

Critère Scraper personnalisé Outil de surveillance hébergé
Sites concurrents Nombreux sites de niche, régionaux ou construits de façon atypique Quelques enseignes généralistes déjà prises en charge par l'outil
Rapprochement des produits Vos règles : GTIN, MPN, références internes, bundles Rapprochement défini par l'outil, avec un contrôle limité
Intégration Connexion directe à votre base de données, votre catalogue ou vos règles de repricing Export ou API dans les limites de l'outil
Historique et propriété des données Historique complet dans votre propre base de données Dépend de l'offre et de la durée de conservation
Structure des coûts Temps de développement initial, puis maintenance Abonnement récurrent, peu de développement nécessaire
Maintenance À votre charge : changements de mise en page, blocages, supervision À la charge du fournisseur
Délai avant les premières données Plus long Plus court

Pour beaucoup de vendeurs, une approche équilibrée consiste à utiliser un outil pour les quelques grands concurrents qu'il couvre correctement, et un scraper pour les sites de niche moins nombreux ainsi que pour toutes les données qui doivent alimenter vos propres règles tarifaires.

Si vous décidez de développer votre solution sans vouloir gérer vous-même le crawler, le service de développement logiciel personnalisé de Netalith couvre la collecte et le traitement des données, notamment les pipelines de collecte de prix présentés ici.

Quels prix et quelles données collecter ?

Ne stockez pas un seul champ appelé « prix ». La documentation de Google sur les fiches marchands distingue trois types de prix : le prix actuel, un prix barré affiché pendant une promotion et un prix réservé aux membres d'un programme de fidélité. Ces données sont encodées dans le balisage Offer et UnitPriceSpecification, avec priceType pour le prix barré et validForMemberTier pour le prix membre. Les mélanger est le meilleur moyen de produire une comparaison erronée.

Donnée Pourquoi elle compte Source courante
Prix actuel Ce que paie un client à cet instant price, ou une spécification de prix sans priceType
Prix barré Permet de distinguer une vraie baisse d'un ancien prix affiché en permanence Spécification de prix avec priceType défini sur StrikethroughPrice
Prix membre Un tarif fidélité n'est pas le prix public validForMemberTier
Devise Ne comparez jamais des montants bruts dans des devises différentes priceCurrency
Disponibilité Un concurrent en rupture de stock ne constitue pas une véritable menace tarifaire availability
Frais de livraison et délai Nécessaires pour comparer le prix réellement payé shippingDetails, ou texte visible sur la page
Période promotionnelle Indique la date de fin d'une promotion validFrom, priceValidUntil, validThrough
Prix à l'unité Permet de comparer au litre ou au kilo, et pas seulement par lot referenceQuantity

Deux remarques pratiques. Lorsqu'un magasin vend dans plusieurs devises, Google recommande une URL par devise : utilisez donc la combinaison URL et devise comme clé de vos observations. Par ailleurs, le balisage peut être en retard sur la page visible ou exclure les taxes. Vérifiez donc un échantillon des prix extraits par rapport à ce que voit réellement un client.

Comment extraire les prix : les données structurées avant le HTML

Testez les méthodes d'extraction de la moins coûteuse et de la plus stable à la plus coûteuse :

  1. Données structurées du produit (JSON-LD ou microdata) présentes dans le code source. Elles sont stables, peu coûteuses et contiennent souvent aussi le GTIN et le MPN.
  2. Sélecteurs CSS ou XPath propres au site pour les sites dépourvus de balisage exploitable.
  3. Page rendue dans un navigateur headless, uniquement lorsque le prix est injecté par JavaScript.

Google recommande d'intégrer le balisage Product dans le HTML initial et avertit que le balisage généré par JavaScript peut rendre les crawls Shopping moins fréquents et moins fiables pour les données qui évoluent rapidement, comme le prix et la disponibilité. En pratique, de nombreuses pages produits concurrentes exposent un prix dans la réponse HTML classique. Une requête HTTP simple associée à un analyseur JSON-LD suffit donc pour couvrir une grande partie des sites avant de devoir utiliser un navigateur.

Cet extracteur lit le JSON-LD Product, parcourt @graph, gère une offre unique ou une liste d'offres, utilise une spécification de prix lorsqu'aucun prix de niveau supérieur n'est disponible et enregistre séparément le prix barré. Il ignore volontairement AggregateOffer, car une fourchette de prix ne correspond pas au prix d'un vendeur donné.

import json
from decimal import Decimal, InvalidOperation

from bs4 import BeautifulSoup


def to_decimal(value):
    """schema.org prices use a dot decimal and no thousands separators."""
    try:
        return Decimal(str(value).strip())
    except (InvalidOperation, AttributeError):
        return None


def iter_nodes(obj):
    """Yield every dict in a JSON-LD payload, including @graph members."""
    if isinstance(obj, list):
        for item in obj:
            yield from iter_nodes(item)
    elif isinstance(obj, dict):
        if "@graph" in obj:
            yield from iter_nodes(obj["@graph"])
        yield obj


def has_type(node, name):
    t = node.get("@type")
    return name in (t if isinstance(t, list) else [t])


def extract_offers(html):
    """Return one record per Offer found in Product JSON-LD on the page."""
    soup = BeautifulSoup(html, "html.parser")
    records = []
    for tag in soup.find_all("script", type="application/ld+json"):
        try:
            data = json.loads(tag.string or "")
        except json.JSONDecodeError:
            continue
        for node in iter_nodes(data):
            if not has_type(node, "Product"):
                continue
            offers = node.get("offers") or []
            if isinstance(offers, dict):
                offers = [offers]
            for offer in offers:
                if has_type(offer, "AggregateOffer"):
                    continue  # a price range, not one seller's price
                specs = offer.get("priceSpecification") or []
                if isinstance(specs, dict):
                    specs = [specs]
                price, currency = offer.get("price"), offer.get("priceCurrency")
                if price is None:  # active price may sit inside a spec
                    active = next(
                        (s for s in specs
                         if "priceType" not in s and "validForMemberTier" not in s),
                        None,
                    )
                    if active:
                        price, currency = active.get("price"), active.get("priceCurrency")
                strike = next(
                    (s for s in specs
                     if "StrikethroughPrice" in str(s.get("priceType", ""))),
                    None,
                )
                records.append({
                    "gtin": node.get("gtin13") or node.get("gtin14")
                            or node.get("gtin12") or node.get("gtin"),
                    "mpn": node.get("mpn"),
                    "sku": node.get("sku"),
                    "price": to_decimal(price),
                    "currency": currency,
                    "list_price": to_decimal(strike.get("price")) if strike else None,
                    "availability": str(offer.get("availability", "")).rsplit("/", 1)[-1],
                })
    return records

Ce que l'extracteur ne couvre pas : les microdata, les variantes produit (les pages de variantes peuvent renvoyer vers un groupe via isVariantOf) et les sites dépourvus de tout balisage. Ces cas relèvent du niveau 2 ou 3. Conservez un extracteur par niveau derrière une même structure de sortie et enregistrez celui qui a produit chaque ligne afin de pouvoir analyser toute baisse de qualité.

Comment associer les produits concurrents à vos SKU

Le rapprochement détermine si une comparaison de prix a un sens. Un prix inférieur sur le mauvais produit est pire que l'absence de données. Classez les méthodes de rapprochement par niveau de confiance et laissez ce niveau déterminer les actions automatisées autorisées.

Méthode de rapprochement Niveau de confiance Usage autorisé
GTIN, EAN ou UPC exact Élevé Alertes et repricing automatisé
MPN et marque Moyen à élevé Alertes ; automatisation après vérification par échantillon
Titre approximatif et attributs Faible File de vérification humaine uniquement

Pièges qui subsistent même après une correspondance correcte de l'identifiant :

  • Taille du lot. Un lot de deux produits comparé à une unité. Normalisez le prix à l'unité lorsque la page fournit une quantité de référence.
  • État du produit. Neuf, reconditionné ou d'occasion : vérifiez itemCondition lorsqu'il est disponible.
  • Variantes. Les variantes de taille ou de couleur peuvent avoir des prix différents sous un même nom de produit.
  • Bundles. Un kit qui comprend votre produit ainsi que des accessoires.

Enregistrez la méthode de rapprochement pour chaque correspondance, comme dans le schéma ci-dessous. Si une correspondance est ensuite invalidée, vous pourrez retrouver et vérifier toutes les lignes qui utilisaient la même méthode.

À quelle fréquence scraper les prix et comment conserver leur historique ?

La fréquence de collecte doit suivre la vitesse à laquelle les prix évoluent, et non un cron global unique. Considérez les valeurs suivantes comme des points de départ, puis ajustez-les selon les taux de variation observés pour chaque concurrent.

Catégorie de SKU Exemple Fréquence de départ
Critique pour les prix Meilleures ventes, produits avec de nombreux concurrents directs Chaque jour ou plusieurs fois par jour
Standard Catalogue à volume intermédiaire Chaque jour
Longue traîne Produits à rotation lente, articles uniques Chaque semaine

Enregistrez une observation non modifiable pour chaque récupération réussie, y compris lorsque le prix n'a pas changé. Vous pourrez ainsi distinguer une absence de changement d'une collecte qui n'a pas eu lieu. Conservez l'extracteur et le statut HTTP dans la ligne. Voici un schéma PostgreSQL minimal :

CREATE TABLE competitor_offer (
  id            BIGSERIAL PRIMARY KEY,
  competitor_id INT  NOT NULL,
  url           TEXT NOT NULL,
  our_sku       TEXT,
  match_method  TEXT,          -- gtin | mpn_brand | manual
  UNIQUE (competitor_id, url)
);

CREATE TABLE price_observation (
  offer_id     BIGINT      NOT NULL REFERENCES competitor_offer(id),
  observed_at  TIMESTAMPTZ NOT NULL,
  price        NUMERIC(12,2),
  list_price   NUMERIC(12,2),
  currency     CHAR(3),
  availability TEXT,
  shipping     NUMERIC(12,2),
  extractor    TEXT,           -- jsonld | css | rendered
  http_status  SMALLINT,
  PRIMARY KEY (offer_id, observed_at)
);

Déduisez les événements de type « prix modifié » à partir d'observations consécutives, plutôt que de remplacer une colonne de prix actuel. L'historique permet de répondre à des questions comme la fréquence des promotions d'un concurrent ou de déterminer si une baisse correspondait à une promotion d'une seule journée.

Pourquoi les scrapers cessent de fonctionner et comment le détecter

La panne la plus coûteuse n'est pas nécessairement un crash. C'est un scraper qui continue de fonctionner et enregistre discrètement des prix erronés. Surveillez le scraper avec autant d'attention que les prix.

Symptôme Cause probable Détection
Nombreux prix nuls chez un concurrent Modification de la mise en page ou du balisage Taux de valeurs nulles par concurrent et par exécution, avec un seuil d'alerte
Prix multiplié par dix ou tombant à zéro Erreur d'analyse, prix « à partir de », mauvais élément sélectionné Plage de cohérence par rapport à la dernière observation ; mettre les valeurs atypiques en attente de vérification
Pic de réponses 403 ou 429 Limitation du débit ou blocage Histogramme des codes de statut par hôte ; ralentir et appliquer un backoff
Même prix pour tous les produits Page de consentement ou interstitiel récupéré à la place du produit Vérification de l'empreinte de la page ; rejet des marqueurs d'interstitiels connus
Prix dans une devise inattendue Le site applique une tarification selon la région Fixer la langue et la région ou utiliser une URL par devise ; valider la devise de chaque ligne
Prix extrait différent du prix visible Balisage obsolète ou hors taxes Vérification planifiée d'un échantillon par rapport à la page rendue

Lorsqu'un concurrent commence à vous bloquer, la bonne réponse consiste à ralentir et à réduire la fréquence. Recourir à des techniques de contournement augmente à la fois les risques juridiques et le coût de maintenance.

Transformer les données de prix en décisions de repricing

Les prix bruts des concurrents doivent alimenter des règles, et non remplacer votre jugement. Quelques garde-fous permettent d'éviter que l'automatisation ne réduise votre marge :

  • Comparez le prix réellement payé (produit et livraison), et pas uniquement le prix affiché.
  • Définissez un prix plancher à partir du coût et d'une marge minimale, en respectant tout prix minimum annoncé auquel vous êtes soumis.
  • Ignorez les concurrents en rupture de stock et les correspondances présentant un faible niveau de confiance.
  • Demandez une validation humaine pour les variations dépassant un pourcentage défini.
  • Consignez chaque modification automatisée ainsi que les observations qui l'ont déclenchée.

Commencez par de simples alertes. Lorsque le taux de rapprochement est satisfaisant et que les alertes d'erreur restent silencieuses pendant quelques semaines, autorisez le repricing automatique des produits disponibles associés à un GTIN, dans la limite du prix plancher, et envoyez tout le reste en vérification.

Si vous souhaitez créer un scraper adapté à vos concurrents, votre catalogue et vos règles tarifaires, envoyez les sites et les SKU importants via le formulaire de devis gratuit de Netalith afin que nous puissions définir le périmètre du projet avec vous.

FAQ

Questions fréquentes

Est-il légal de scraper les prix de ses concurrents ?

Cela dépend du pays, des conditions d'utilisation du site, des données consultées et de la manière de procéder. Les prix affichés sur des pages publiques sont souvent surveillés, mais les conditions d'utilisation, robots.txt, les espaces nécessitant une connexion et les protections anti-bot comptent tous, et robots.txt ne constitue pas une autorisation d'accès. Ne scrapez pas derrière une connexion et ne contournez pas les protections. Demandez un avis juridique avant de scraper à grande échelle.

À quelle fréquence scraper les prix des concurrents ?

Adaptez la fréquence à la vitesse d'évolution des prix. Un point de départ courant consiste à collecter les données chaque jour, voire plus souvent, pour les SKU critiques pour les prix, et chaque semaine pour la longue traîne. Ajustez ensuite la fréquence selon le taux de variation observé pour chaque concurrent.

Faut-il créer un scraper personnalisé ou acheter un outil de surveillance des prix ?

Achetez un outil si vous suivez quelques grandes enseignes et souhaitez obtenir rapidement des données. Créez votre solution si vous surveillez de nombreux sites de niche ou régionaux, si vous avez besoin de vos propres règles de rapprochement produit ou si les prix doivent être intégrés directement à votre base de données et à vos règles de repricing. Beaucoup de vendeurs combinent les deux approches.

Comment associer les produits concurrents à mes propres SKU ?

Privilégiez d'abord un GTIN, EAN ou UPC exact, puis l'association MPN et marque. Utilisez le rapprochement approximatif des titres uniquement pour proposer des candidats à une vérification humaine. Faites attention à la taille des lots, à l'état du produit et aux variantes, et enregistrez la méthode utilisée pour chaque correspondance.

Que faire si un concurrent bloque mon scraper ?

Ralentissez, réduisez la fréquence et vérifiez que vous respectez son robots.txt. Si le site continue de vous exclure, utilisez une autre source légitime, comme une API officielle, un flux sous licence ou un échantillonnage manuel, plutôt que de recourir à des techniques de contournement.

Restez informé avec Netalith

Recevez des ressources de développement, des mises à jour produit et des offres spéciales directement dans votre boîte mail.