Le même produit coûte presque toujours un prix différent chez deux revendeurs. Un ordinateur portable affiché à un certain prix sur une place de marché peut être dix pour cent moins cher sur la boutique d'une marque, et cet écart évolue quotidiennement. Vérifier chaque site à la main ne passe pas à l'échelle au-delà de quelques produits, ce qui est exactement le type de recherche répétitive qu'un script devrait prendre en charge.

Ce tutoriel vous montre comment construire un outil de comparaison de prix en Python qui récupère le même produit sur deux ou plusieurs pages publiques d'e-commerce, normalise les données dans une forme unique et propre, fait correspondre les annonces entre les sources, stocke le résultat et affiche le prix le plus bas. Nous récupérons chaque page via la Crawling API pour que le rendu et la rotation des IP soient gérés pour nous, et nous limitons l'ensemble du tutoriel aux données de catalogue publiques : nom du produit, prix, devise, revendeur et URL du produit.

Ce que vous allez construire

Un petit projet Python qui transforme une poignée d'URLs de produits en un tableau de comparaison soigné. Chaque annonce scrapée devient un enregistrement avec un ensemble fixe de champs, de sorte que deux revendeurs qui décrivent un produit différemment s'alignent quand même. Au terme de ce tutoriel, vous aurez :

  • Nom. Le titre du produit tel que le revendeur le liste.
  • Prix. Une valeur numérique extraite du texte brut affiché sur la page.
  • Devise. Le code de devise pour ne jamais comparer des dollars et des euros.
  • Revendeur. La source dont provient l'annonce.
  • URL. La page exacte, pour qu'un humain puisse vérifier l'offre.
  • Sortie stockée. Chaque enregistrement écrit en JSON et CSV, ainsi qu'un résumé du prix le plus bas.

Pourquoi une requête classique échoue sur les pages de vente au détail

Vous pouvez aller loin avec la bibliothèque requests sur des sites simples, mais les pages d'e-commerce modernes résistent de deux façons. Premièrement, beaucoup d'entre elles rendent le prix côté client : le HTML initial est une coquille presque vide et JavaScript remplit le catalogue après le chargement. Un simple requests.get ne voit que la coquille, de sorte que le prix recherché n'est tout simplement pas dans le texte téléchargé.

Deuxièmement, les revendeurs surveillent le trafic automatisé. Quelques requêtes depuis une seule adresse de datacenter, selon un modèle reconnaissable, entraînent une limitation du débit, un blocage par CAPTCHA ou un bannissement pur. Pour comparer les prix entre plusieurs boutiques, vous avez besoin que chaque page soit rendue et que chaque requête arrive depuis une adresse de confiance et rotative. C'est la combinaison que la Crawling API offre en un seul appel, ce qui explique pourquoi nous acheminons chaque récupération à travers elle plutôt que de frapper les sites directement. Si le rendu JavaScript sur les pages de vente au détail est nouveau pour vous, notre guide sur le scraping de pages JavaScript avec Python couvre le contexte.

Prérequis

Trois éléments avant d'écrire du code.

Python 3.8 ou version ultérieure. Vérifiez avec python --version. N'importe quelle version 3.x récente convient ; téléchargez la version actuelle depuis le site officiel Python si nécessaire.

Un compte Crawlbase et un token. Inscrivez-vous pour un compte gratuit et copiez votre token depuis le tableau de bord. Vous obtenez un token normal (pour le HTML statique) et un token JavaScript (pour les pages rendues côté client) ; gardez les deux à portée de main.

Des bases en Python. Vous devez être à l'aise pour exécuter un script depuis le terminal, installer des packages avec pip et lire un dictionnaire. La familiarité avec HTML et les sélecteurs CSS est utile, car vous les adapterez aux pages que vous ciblez.

Mettre en place le projet

Créez un dossier et installez les dépendances. Nous utilisons le client crawlbase officiel pour la récupération, beautifulsoup4 pour le parsing HTML et pandas pour stocker et afficher la comparaison. Les modules json et csv sont intégrés, il n'y a donc rien à installer pour eux.

bash
mkdir price-comparison && cd price-comparison

pip install crawlbase beautifulsoup4 pandas

Si vous souhaitez approfondir spécifiquement la partie parsing, notre présentation de comment utiliser BeautifulSoup en Python va plus loin que les extraits ci-dessous.

Étape 1 : récupérer la première source

Commencez par une seule page d'un revendeur. Le client Crawling API prend votre token, récupère l'URL derrière une adresse IP résidentielle rotative et renvoie le HTML rendu. Pour une page rendue en JavaScript, passez ajax_wait et page_wait afin que l'API attende le contenu asynchrone et marque un temps d'arrêt après le chargement avant de capturer. Utilisez votre token JavaScript pour ces pages et votre token normal pour les pages statiques classiques.

python
from crawlbase import CrawlingAPI

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"})

def fetch_page(url):
    response = api.get(url, {"ajax_wait": "true", "page_wait": 3000})
    if response["status_code"] == 200:
        return response["body"].decode("utf-8")
    raise RuntimeError(f"Crawl failed: {response['status_code']}")

html = fetch_page("https://store-a.example.com/product/smartphone-xyz")
print(html[:500])

L'exécution de ce code affiche les 500 premiers caractères du HTML rendu. Si vous voyez le vrai balisage de la page plutôt qu'une coquille vide, la récupération a fonctionné et l'API a géré à la fois le rendu et l'adresse IP pour vous. La vérification de status_code empêche un échec silencieux de se propager en aval comme s'il s'agissait d'un bon HTML.

Crawlbase Crawling API

Cet unique appel fetch_page que vous venez d'exécuter a fait les deux tâches difficiles pour vous : il a rendu le JavaScript pour que le prix soit réellement dans le HTML, et il a envoyé la requête via une adresse IP résidentielle rotative pour que le revendeur ne vous bloque pas. Vous évitez de mettre en place une flotte de navigateurs sans tête et un pool de proxies, ce qui importe dès que vous récupérez depuis un deuxième et un troisième magasin selon un calendrier. Pointez-le vers les pages qui revenaient vides.

Étape 2 : récupérer la deuxième source

Une comparaison nécessite au moins deux revendeurs. La logique de récupération ne change pas : le même fetch_page fonctionne pour n'importe quelle URL, car l'API gère ce que chaque site lui oppose. Ce qui change, c'est le parsing, puisque chaque revendeur balise son titre et son prix différemment. Écrivez un petit parseur par source qui lit la page et renvoie un enregistrement normalisé.

python
import re
from bs4 import BeautifulSoup

def parse_price(text):
    # Pull a number out of strings like "$279.99" or "1,299.00 USD"
    match = re.search(r"[\d,]+\.?\d*", text.replace(",", ""))
    return float(match.group()) if match else None

def parse_store_a(html, url):
    soup = BeautifulSoup(html, "html.parser")
    return {
        "name": soup.select_one("h1.product-title").get_text(strip=True),
        "price": parse_price(soup.select_one(".price").get_text()),
        "currency": "USD",
        "retailer": "Store A",
        "url": url,
    }

def parse_store_b(html, url):
    soup = BeautifulSoup(html, "html.parser")
    return {
        "name": soup.select_one("#productName").get_text(strip=True),
        "price": parse_price(soup.select_one("span.amount").get_text()),
        "currency": "USD",
        "retailer": "Store B",
        "url": url,
    }

Les deux parseurs renvoient les mêmes cinq clés, ce qui est tout l'intérêt : name, price, currency, retailer et url. Les sélecteurs (h1.product-title, .price, #productName, span.amount) sont des exemples indicatifs. Ouvrez chaque cible dans les outils de développement de votre navigateur, trouvez les éléments qui contiennent réellement le titre et le prix, et remplacez-les. L'assistant parse_price fait le travail ingrat de transformer "$279.99" en nombre 279.99 afin que les prix soient comparables.

Selectors drift

Les revendeurs redesignent souvent, et un sélecteur qui fonctionnait le mois dernier peut silencieusement commencer à renvoyer None. Traitez les sélecteurs par source comme quelque chose à maintenir. Quand un prix revient vide, réinspectez la page en direct et mettez à jour ce seul parseur, pas le reste du pipeline.

Étape 3 : normaliser et faire correspondre les sources

Connectez maintenant la récupération et le parsing. Associez chaque URL au parseur qui la comprend, constituez une liste d'enregistrements normalisés et regroupez les enregistrements décrivant le même produit afin de pouvoir comparer ce qui est comparable. La correspondance la plus fiable est une clé de produit explicite que vous attribuez par URL, plutôt que d'essayer de la deviner à partir de titres légèrement différents.

python
# Each entry: (product key, retailer URL, parser)
sources = [
    ("Smartphone XYZ", "https://store-a.example.com/product/smartphone-xyz", parse_store_a),
    ("Smartphone XYZ", "https://store-b.example.com/p/smartphone-xyz", parse_store_b),
]

def collect(sources):
    products = {}
    for key, url, parser in sources:
        try:
            record = parser(fetch_page(url), url)
        except Exception as err:
            print(f"Skipping {url}: {err}")
            continue
        products.setdefault(key, []).append(record)
    return products

Le résultat est un dictionnaire indexé par produit, avec une liste d'enregistrements par revendeur sous chaque clé. La forme reflète le modèle de données du tutoriel original, un nom de produit avec une liste de boutiques et de prix, mais ici chaque enregistrement est scrapé en direct et normalisé plutôt que lu depuis un fichier statique. Encapsuler chaque récupération dans un try/except signifie qu'une URL morte est journalisée et l'exécution continue, au lieu de s'interrompre à mi-chemin.

Étape 4 : comparer et stocker les résultats

Avec des enregistrements correspondants en main, trouver le prix le plus bas est une courte boucle, et stocker la sortie se fait en quelques lignes. La comparaison garde la logique exacte de la version classique : parcourir les enregistrements de chaque produit et garder le moins cher.

python
def find_lowest_price(records):
    lowest_price = float("inf")
    best = None
    for record in records:
        if record["price"] is not None and record["price"] < lowest_price:
            lowest_price = record["price"]
            best = record
    return best

Assemblez ensuite le script complet. Il collecte toutes les sources, affiche la meilleure offre par produit et écrit la liste plate d'enregistrements en JSON et CSV avec pandas pour que vous puissiez ouvrir la comparaison dans un tableur ou l'alimenter à un autre outil.

python
import json
import pandas as pd

def main():
    products = collect(sources)
    rows = []

    for name, records in products.items():
        rows.extend(records)
        best = find_lowest_price(records)
        if best:
            print(f"Product: {name}")
            print(f" - Best Price: {best['currency']} {best['price']} at {best['retailer']}")

    # Store the full comparison as JSON and CSV
    with open("comparison.json", "w") as f:
        json.dump(rows, f, indent=2)

    pd.DataFrame(rows).to_csv("comparison.csv", index=False)
    print(f"Saved {len(rows)} records.")

if __name__ == "__main__":
    main()

Exécutez-le avec python compare.py. Vous obtenez une ligne de meilleur prix par produit dans le terminal et deux fichiers sur le disque : comparison.json pour une utilisation programmatique et comparison.csv pour un tableur. Si vous préférez stocker l'historique des prix dans le temps, remplacez l'écriture CSV par une insertion SQLite avec le module intégré sqlite3 ; la forme des enregistrements reste la même, rien en amont ne change.

À quoi ressemble la sortie

Le fichier JSON contient un objet par annonce scrapée, tous partageant les cinq champs normalisés. Pour deux revendeurs vendant le même téléphone, cela ressemble à ceci :

json
[
  {
    "name": "Smartphone XYZ",
    "price": 299.99,
    "currency": "USD",
    "retailer": "Store A",
    "url": "https://store-a.example.com/product/smartphone-xyz"
  },
  {
    "name": "Smartphone XYZ",
    "price": 279.99,
    "currency": "USD",
    "retailer": "Store B",
    "url": "https://store-b.example.com/p/smartphone-xyz"
  }
]

Et le résumé dans le terminal, piloté par find_lowest_price, affiche :

bash
Product: Smartphone XYZ
 - Best Price: USD 279.99 at Store B
Saved 2 records.

Le CSV contient les mêmes lignes sous forme plate, une ligne d'en-tête puis une ligne par annonce, ce qui s'ouvre directement dans n'importe quel tableur pour le tri et la visualisation.

Passer à l'échelle vers plus de produits et de sources

La conception passe à l'échelle en ajoutant des lignes à la liste sources, pas en réécrivant la logique. Pour suivre dix produits chez quatre revendeurs, ajoutez un parseur par nouveau revendeur et un tuple par paire (produit, revendeur). Exécutez le script selon un calendrier (une tâche cron une ou deux fois par jour suffit pour le suivi des prix) et ajoutez chaque exécution à un fichier daté ou à une table SQLite pour construire l'historique des prix.

Quelques bonnes pratiques maintiennent la santé à volume. Espacez vos requêtes pour ne pas récupérer des dizaines de pages en une rafale serrée. Gérez chaque récupération de façon isolée, ce que le try/except dans collect fait déjà, afin qu'une page en échec ne tue jamais l'exécution. Et mettez en cache le HTML récupéré sur le disque pendant que vous itérez sur les sélecteurs, afin de ne pas frapper les sites en direct à chaque changement de code. Si une page passe ultérieurement du statique au rendu JavaScript, la Crawling API absorbe cela pour vous, car le rendu n'est qu'une option sur le même appel. Pour les pages correspondant à une mise en page connue, la Crawling API peut auto-parser les champs produit et renvoyer du JSON structuré directement, ce qui vous permet de ne pas écrire de parseur par source quand un modèle pris en charge convient.

Construire un comparateur de prix de façon responsable

Un outil de comparaison de prix ne consulte que les mêmes pages de catalogue public qu'un acheteur consulterait, mais cela ne rend pas tout permis. Lisez les conditions d'utilisation de chaque revendeur et vérifiez son robots.txt avant de pointer une boucle dessus, et maintenez votre volume de requêtes modeste : quelques récupérations par jour et par produit suffisent pour suivre les prix et sont bien moins intrusives qu'un bombardement constant. Limitez-vous aux données publiques de produits (nom, prix, devise, URL) et restez à l'écart de tout ce qui se trouve derrière une connexion, de tout ce qui est personnel et des médias protégés par le droit d'auteur que vous redistribueriez.

Lorsqu'un revendeur propose une API officielle de produits ou un flux de données affilié, préférez-la. Ces canaux sont conçus précisément pour cet usage, renvoient généralement des données structurées plus propres que le scraping, et vous maintiennent du bon côté de la relation. Traitez le scraping comme le recours pour les sources qui ne proposent pas une telle option, et vous obtenez un outil à la fois fiable et respectueux des sites dont il dépend. Pour le manuel anti-blocage plus complet, notre guide sur comment scraper des sites web sans être bloqué entre dans les détails.

Récapitulatif

Points clés

  • Normaliser avant de comparer. Transformez la page de chaque revendeur en cinq champs identiques (name, price, currency, retailer, URL) pour que des balises différentes s'alignent quand même.
  • Un parseur par source, un pipeline partagé. La récupération est identique sur tous les sites ; seuls les sélecteurs par source changent, ce qui maintient la stabilité du reste du code.
  • La Crawling API gère le rendu et les adresses IP. Un seul appel fetch_page renvoie du HTML rendu derrière une adresse IP résidentielle rotative, vous évitant une flotte de navigateurs sans tête et un pool de proxies.
  • Stocker en format plat, en JSON et CSV. Une liste d'enregistrements plats se sérialise proprement dans les deux, et passe à SQLite quand vous voulez un historique des prix, sans changements en amont.
  • Passer à l'échelle par les données, pas par le code. Ajoutez des produits et des revendeurs en étendant la liste sources et en exécutant selon un calendrier, pas en réécrivant la logique de comparaison.

Foire aux questions

Qu'est-ce qu'un outil de comparaison de prix ?

Un outil de comparaison de prix collecte le prix du même produit chez plusieurs revendeurs et vous indique lequel est le moins cher. En termes de code, il scrape la page publique de chaque revendeur, normalise les données dans une forme unique, fait correspondre les annonces décrivant le même article et affiche le prix le plus bas. Le projet Python de ce tutoriel fait exactement cela sur deux sources ou plus.

Pourquoi utiliser la Crawling API plutôt que requests classique ?

requests classique ne télécharge que le HTML initial, ce qui fait manquer les prix chargés côté client avec JavaScript, et une seule adresse IP est bloquée dès que vous récupérez depuis plusieurs boutiques à répétition. La Crawling API rend la page et achemine chaque requête via une adresse IP résidentielle rotative en un seul appel, de sorte que le prix se trouve bien dans le HTML parsé et que vous restez non bloqué sur toutes les sources.

Comment faire correspondre le même produit chez différents revendeurs ?

L'approche la plus fiable est une clé de produit explicite que vous attribuez par URL, comme le fait la liste sources dans ce tutoriel, plutôt que d'essayer de faire correspondre automatiquement des titres de produits légèrement différents. Pour les catalogues plus grands, vous pouvez faire correspondre sur un identifiant partagé comme un numéro de modèle, un GTIN ou un SKU lorsque les revendeurs en exposent un, ce qui est plus fiable que la correspondance approximative par titre.

Dois-je stocker les données en JSON ou en CSV ?

Utilisez les deux, car ils répondent à des besoins différents. JSON préserve la structure imbriquée complète et est facile à relire dans un autre script, tandis que CSV est plat et s'ouvre directement dans un tableur pour le tri et la visualisation. Le script de ce tutoriel écrit les deux à partir de la même liste d'enregistrements. Pour suivre les prix dans le temps, passez à SQLite afin de pouvoir interroger l'historique sans jongler avec de nombreux fichiers.

Puis-je suivre les prix automatiquement dans le temps ?

Oui. Exécutez le script selon un calendrier, par exemple une tâche cron une ou deux fois par jour, et ajoutez les enregistrements de chaque exécution à un fichier daté ou à une table SQLite. Des récupérations modestes et régulières suffisent pour repérer les baisses de prix et construire un historique sans bombarder aucun revendeur. Maintenez une cadence légère et respectez les conditions de chaque site.

Ai-je besoin d'un navigateur sans tête comme Selenium pour cela ?

Pas lorsque vous récupérez via la Crawling API. Un navigateur sans tête comme Selenium ou Playwright peut rendre les pages JavaScript, mais chaque instance est un navigateur complet qui consomme de la mémoire et du CPU, et vous devez toujours gérer les proxies vous-même à grande échelle. La Crawling API rend la page côté serveur et fait tourner les adresses IP pour vous, de sorte qu'un simple script Python avec BeautifulSoup suffit.

Commencer à construire

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.

En libre-service · Sans appel commercial requis · Volumes de crawl entreprise disponibles