Le scraping vous donne des lignes ; il ne vous donne pas des réponses. Les données qui sortent d'un crawler sont presque toujours en désordre : des prix stockés sous forme de chaînes avec des symboles monétaires, des dates dans trois formats différents, des colonnes à moitié remplies, et le même produit listé deux fois parce que deux pages y pointaient. Avant que tout cela ne soit utile, vous devez le nettoyer, et l'outil standard pour ce travail en Python est pandas.
Ce guide prend un jeu de données scrapé réaliste, un CSV de fiches produits, et le mène entièrement des lignes brutes jusqu'à l'insight avec pandas. Vous le chargez dans un DataFrame, vous l'inspectez, vous gérez les valeurs manquantes et les doublons, vous corrigez les types de colonnes, vous analysez les prix et les dates en vrais nombres et horodatages, puis vous filtrez, regroupez, agrégez, triez et exportez le résultat. Chaque extrait est réel et copiable-collable. La seule hypothèse est que l'entrée a d'abord été collectée de manière responsable à partir de sources publiques.
Ce que vous allez construire
Un pipeline de nettoyage et d'analyse court et répétable qui transforme un scrape brut en un jeu de données soigné et quelques tableaux récapitulatifs. À la fin, vous aurez touché chaque champ d'une ligne produit typique :
- product_name : du texte libre, souvent avec une casse et des espaces parasites.
- category : la colonne de regroupement sur laquelle vous agrégerez.
-
price : scrapé comme une chaîne du type
"$1,299.00"qui doit devenir un flottant. - rating : une colonne numérique avec des valeurs manquantes à gérer.
- in_stock : un indicateur quasi booléen stocké de manière incohérente sous forme de texte.
- scraped_at : une chaîne de date à analyser en un véritable horodatage.
Le même flux de travail s'applique à n'importe quel scrape tabulaire, fiches, avis, offres d'emploi ou lignes immobilières. Si votre crawler écrit déjà du JSON ou du CSV propre, vous passez directement à la moitié analyse. Pour un regard plus approfondi sur les deux formats et quand choisir chacun, voir JSON vs CSV : les principales différences.
Pourquoi les données scrapées doivent d'abord être nettoyées
Les données scrapées brutes sont rarement prêtes pour l'analyse. La couche d'extraction récupère le texte qui se trouve dans un sélecteur, donc un prix revient sous la forme "$1,299.00" plutôt que le nombre 1299.0, une cellule de note peut être vide quand la page ne l'a jamais rendue, et la même fiche peut apparaître deux fois quand la pagination se chevauche. Injectez cela directement dans un calcul et vous obtenez des erreurs ou, pire, des réponses discrètement fausses.
Le nettoyage corrige trois catégories de problèmes avant que toute analyse ne s'exécute : les valeurs manquantes dues à une extraction incomplète, les lignes en double dues à des crawls qui se chevauchent, et les formats incohérents sur les colonnes de prix, de date et de texte. Réglez bien cela et le reste de pandas, le tri, le regroupement, l'agrégation, devient quelques lignes uniques. Si vous intégrez cette discipline dans un pipeline plus large, les modèles de structurer et nettoyer les données web scrapées pour l'IA et le ML se généralisent bien au-delà d'un seul CSV.
Prérequis
Vous n'avez pas besoin de grand-chose pour suivre.
Python de base. Vous devriez être à l'aise pour exécuter un script et lire quelques lignes de code. Savoir ce qu'est une liste et un dictionnaire suffit ; pandas fait le gros du travail.
Python 3.8 ou ultérieur. Vérifiez votre version avec python --version. Si vous ne l'avez pas, installez-le depuis python.org.
Un fichier CSV ou JSON scrapé. N'importe quel export tabulaire fera l'affaire. Les exemples ci-dessous supposent un fichier nommé products.csv avec les colonnes listées plus haut, mais les méthodes sont les mêmes quel que soit le nom de vos colonnes.
Configurer le projet
Créez un environnement virtuel pour que le projet reste isolé, puis installez pandas. C'est la seule dépendance requise ; matplotlib est facultatif et n'est nécessaire que pour le graphique rapide de la fin.
python --version python -m venv pandas_env source pandas_env/bin/activate pip install pandas matplotlib
Sous Windows, activez l'environnement avec pandas_env\Scripts\activate au lieu de la ligne source. pandas vous donne le DataFrame et chaque méthode de nettoyage ci-dessous ; matplotlib est ce à travers quoi pandas trace lorsque vous appelez .plot() à la fin.
Étape 1 : charger les données dans un DataFrame
Tout dans pandas commence par un DataFrame, une structure de type tableau de lignes et de colonnes, très proche d'un tableur. Une seule colonne de celui-ci est une Series. Lire un CSV scrapé dans un DataFrame tient en un appel, et le JSON est tout aussi facile avec read_json.
import pandas as pd # Load the scraped product listings df = pd.read_csv("products.csv") # JSON works the same way: # df = pd.read_json("products.json") # Show the first five rows print(df.head())
La méthode head() affiche les cinq premières lignes, votre première confirmation que le fichier s'est chargé et que les colonnes ont atterri là où vous l'attendiez. Si votre scrape a produit un objet JSON par ligne, passez lines=True à read_json. À partir d'ici, chaque opération travaille sur l'objet df en mémoire ; rien ne touche au fichier d'origine jusqu'à l'export final.
Plus votre products.csv est propre avant d'atteindre pandas, moins vous avez réellement besoin de ce guide. La Scraper API analyse automatiquement les pages prises en charge en champs JSON structurés, prix, titres, notes et stock déjà répartis en valeurs typées, de sorte que les lignes que vous chargez sont proches d'être prêtes pour l'analyse au lieu d'un mur de chaînes brutes. Moins de temps à corriger les dtypes, plus de temps à trouver l'insight.
Étape 2 : inspecter le jeu de données
Avant de changer quoi que ce soit, regardez ce que vous avez. Trois méthodes vous disent presque tout : head() montre des lignes d'exemple, info() indique les types de colonnes et combien de valeurs non nulles chacune contient, et describe() donne des statistiques récapitulatives pour les colonnes numériques.
# Shape: (rows, columns) print(df.shape) # Column names, dtypes, and non-null counts df.info() # Summary stats for numeric columns print(df.describe())
Lisez info() attentivement : c'est là que vous repérez les ennuis tôt. Si price apparaît comme object plutôt que comme un nombre, c'est encore une chaîne et l'arithmétique dessus échouera. Si rating a moins de valeurs non nulles que le nombre de lignes, vous avez des données manquantes à gérer. Ce seul appel vous dit lesquelles des étapes suivantes vous sont réellement nécessaires.
Étape 3 : gérer les valeurs manquantes
Les cellules manquantes proviennent d'une extraction incomplète, d'un champ qui ne s'est pas rendu, d'un sélecteur qui n'a rien trouvé sur une page. Comptez-les d'abord par colonne, puis décidez de supprimer les lignes concernées ou de les remplir avec une valeur par défaut sensée.
# Count missing values per column print(df.isnull().sum()) # Drop rows missing a product name or price (can't analyse those) df = df.dropna(subset=["product_name", "price"]) # Fill missing ratings with the column average instead of dropping df["rating"] = df["rating"].fillna(df["rating"].mean())
Le choix entre supprimer et remplir relève du jugement, pas d'une règle. Supprimez les lignes quand le champ manquant est essentiel et ne peut pas être deviné, une fiche sans prix est inutile pour l'analyse des prix. Remplissez quand la colonne est secondaire et qu'un remplaçant raisonnable vaut mieux que perdre toute la ligne ; ici un rating manquant devient la moyenne de la colonne pour que ces produits comptent encore dans les agrégats par catégorie. Utiliser subset sur dropna garde la suppression ciblée au lieu de jeter toute ligne ayant une seule cellule vide.
Étape 4 : supprimer les lignes en double
Les crawls qui se chevauchent et les ré-exécutions laissent des lignes en double, et elles gonflent discrètement les comptages et les moyennes. Trouvez-les, puis supprimez-les en conservant la première occurrence.
# How many fully duplicated rows are there? print(df.duplicated().sum()) # Drop exact duplicate rows, keep the first df = df.drop_duplicates(keep="first") # Or de-duplicate on a unique key, like a product URL or ID df = df.drop_duplicates(subset=["product_name", "category"])
Un simple drop_duplicates() ne supprime que les lignes identiques sur chaque colonne, ce qui rate les quasi-doublons où un champ diffère. Quand votre scrape a une clé naturelle, un identifiant produit ou une URL canonique, dédupliquez là-dessus avec subset pour que deux captures du même article se condensent en une seule même si l'horodatage scrapé diffère. Exécutez ceci avant tout comptage pour que vos totaux reflètent des produits distincts, pas des artéfacts de crawl.
Étape 5 : corriger les types de données et analyser les prix
C'est l'étape qui transforme les chaînes scrapées en valeurs sur lesquelles vous pouvez calculer. Un price scrapé arrive sous forme de texte comme "$1,299.00" ; vous retirez le symbole monétaire et le séparateur de milliers, puis vous convertissez en flottant. La même idée standardise l'indicateur de stock et ordonne les colonnes de texte.
# "$1,299.00" -> 1299.0 df["price"] = ( df["price"] .str.replace(r"[$,]", "", regex=True) .astype("float") ) # Tidy text: strip whitespace and normalise case df["product_name"] = df["product_name"].str.strip() df["category"] = df["category"].str.lower() # "Yes"/"No" text flag -> real booleans df["in_stock"] = df["in_stock"].str.lower().map({"yes": True, "no": False})
L'accesseur .str vous laisse exécuter des opérations de chaîne sur toute une colonne d'un coup, donc l'expression régulière [$,] retire à la fois le signe dollar et la virgule de chaque prix avant que astype("float") ne rende la colonne numérique. Mettre category en minuscules compte plus qu'il n'y paraît : si la moitié de vos lignes dit "Phones" et l'autre moitié "phones", le regroupement les traite comme deux catégories tant que vous ne normalisez pas. Mapper l'indicateur de stock vers de vrais booléens vous permet de filtrer dessus proprement à l'étape suivante.
Après toute conversion de type, relancez df.info() et confirmez que la colonne se lit comme float64, bool ou datetime64 plutôt que object. Un dtype object sur une colonne que vous croyez numérique est la raison la plus courante pour qu'un calcul ultérieur lève une erreur ou renvoie discrètement n'importe quoi.
Étape 6 : analyser les dates
Les chaînes de date sont inutiles pour l'analyse temporelle tant que pandas ne les comprend pas comme des horodatages. to_datetime convertit la colonne, et une fois qu'elle est un vrai datetime vous pouvez trier dessus, filtrer des plages et en extraire des parties comme le mois.
# Parse the scraped timestamp into a real datetime df["scraped_at"] = pd.to_datetime(df["scraped_at"], errors="coerce") # Now date parts are available df["scrape_month"] = df["scraped_at"].dt.to_period("M")
Passer errors="coerce" est la valeur par défaut sûre sur des données scrapées : toute valeur que pandas ne peut pas analyser devient NaT (l'équivalent datetime de null) au lieu de lever une erreur et d'arrêter tout le script. Si chaque ligne partage un format connu, vous pouvez passer format="%Y-%m-%d" pour rendre l'analyse plus rapide et plus stricte. Avec une vraie colonne datetime, l'accesseur .dt expose year, month, day et des aides comme to_period pour un regroupement mensuel.
Étape 7 : filtrer et sélectionner
Avec les données propres et typées, vous pouvez les interroger. L'indexation booléenne filtre les lignes par condition, et vous pouvez combiner les conditions pour affiner davantage.
# Only in-stock products over $100 premium = df[(df["in_stock"]) & (df["price"] > 100)] # Just the columns you care about premium = premium[["product_name", "category", "price", "rating"]] print(premium.head())
Chaque condition entre crochets produit une Series booléenne, et & les combine ligne par ligne, alors enveloppez chaque condition entre parenthèses pour garder la priorité des opérateurs correcte. Sélectionner une liste de colonnes avec des doubles crochets renvoie un DataFrame plus étroit, pratique quand un scrape a trente colonnes et que vous n'en voulez que quatre pour une question particulière.
Étape 8 : regrouper, agréger et trier
Le vrai gain, c'est la synthèse. groupby répartit les lignes par une clé, puis vous agrégez chaque groupe, le prix moyen par catégorie, le nombre de produits par catégorie, la meilleure note dans chacune, et vous triez le résultat pour le lire d'un coup d'œil.
# Average price per category, highest first avg_price = ( df.groupby("category")["price"] .mean() .sort_values(ascending=False) ) # Several aggregates at once summary = df.groupby("category").agg( product_count=("product_name", "count"), avg_price=("price", "mean"), max_price=("price", "max"), avg_rating=("rating", "mean"), ) print(avg_price) print(summary)
La forme à métrique unique, groupby("category")["price"].mean(), renvoie un nombre par catégorie et se lit presque comme de l'anglais. Quand vous voulez plusieurs métriques dans un seul tableau, l'agrégation nommée avec .agg() est plus propre : chaque mot-clé devient une colonne de sortie, et le tuple (column, function) indique quel champ synthétiser et comment. Enchaîner sort_values à la fin ordonne le résultat pour que les catégories les plus et les moins chères se trouvent en haut et en bas.
Étape 9 : appliquer des statistiques
Pour une lecture numérique rapide d'une seule colonne, les méthodes statistiques intégrées couvrent l'essentiel de ce dont vous avez besoin sans quitter pandas.
# Full statistical summary of the price column print(df["price"].describe()) # Individual measures median_price = df["price"].median() std_price = df["price"].std() print(f"Median price: {median_price:.2f}") print(f"Std deviation: {std_price:.2f}")
L'appel describe() sur une seule colonne vous donne le comptage, la moyenne, l'écart-type, le min, le max et les quartiles d'un seul coup, un moyen rapide de repérer les valeurs aberrantes comme une erreur de scraping qui a laissé un prix à zéro ou à des millions. median() est souvent plus honnête que la moyenne sur des données de prix car quelques articles très chers tirent la moyenne vers le haut, et comparer les deux vous dit à quel point la distribution est déséquilibrée.
Étape 10 : visualiser et exporter
Un graphique rend une distribution évidente, et pandas trace directement à travers matplotlib. Quand l'analyse est terminée, réécrivez les données nettoyées pour que l'étape suivante, un notebook, un tableau de bord, un collègue, puisse les reprendre.
import matplotlib.pyplot as plt # Average price per category as a bar chart avg_price.plot(kind="bar", title="Average price per category") plt.ylabel("Price") plt.tight_layout() plt.show() # Export the cleaned dataset df.to_csv("products_clean.csv", index=False) summary.to_csv("category_summary.csv") # JSON export works the same way df.to_json("products_clean.json", orient="records", indent=2)
Passer index=False à to_csv empêche pandas d'écrire les numéros de ligne comme une première colonne parasite, ce qui est presque toujours ce que vous voulez pour un export propre. Pour le JSON, orient="records" produit une liste d'un objet par ligne, la forme que la plupart des autres outils attendent. Avec cela, le scrape brut est devenu un fichier soigné et un tableau récapitulatif que vous pouvez transmettre ou représenter graphiquement.
À quoi ressemble la sortie
Après l'exécution du pipeline, le récapitulatif par catégorie se lit comme un petit tableau propre. Exporté en JSON avec orient="records", chaque ligne ressemble à ceci :
[ { "category": "laptops", "product_count": 128, "avg_price": 942.55, "max_price": 2499.00, "avg_rating": 4.31 }, { "category": "phones", "product_count": 204, "avg_price": 611.20, "max_price": 1399.00, "avg_rating": 4.12 } ]
Ces nombres sont illustratifs, mais la forme est exactement ce que le code produit : un enregistrement par catégorie, chacun portant les comptages et les agrégats que vous avez définis dans .agg(). Cette sortie structurée est ce qui alimente l'étape suivante, qu'il s'agisse d'un tableau de bord de suivi des prix, d'un notebook ou d'un modèle.
Passer à l'échelle au-delà d'un fichier
Un seul CSV est le point de départ, pas la destination. À mesure que vos scrapes grandissent, quelques habitudes maintiennent le même pipeline fonctionnel à grande échelle.
-
Combinez de nombreux fichiers. Quand un crawl écrit un CSV par exécution, lisez-les tous et empilez-les avec
pd.concat([...])dans un seul DataFrame avant le nettoyage, puis dédupliquez pour que les exécutions qui se chevauchent ne comptent pas double. -
Traitez les gros fichiers par morceaux. Si un fichier est trop gros pour la mémoire, passez
chunksizeàread_csvet itérez, en nettoyant et agrégeant chaque morceau pour ne jamais détenir tout le jeu de données à la fois. -
Faites du nettoyage une fonction. Enveloppez les étapes 3 à 6 dans une seule fonction
clean(df)pour que chaque nouveau lot reçoive le même traitement, et que votre pipeline reste reproductible au lieu d'un tas de cellules ponctuelles. - Choisissez le bon format d'entrée. Les données imbriquées, des avis avec réponses, des produits avec listes de variantes, conviennent souvent mieux au JSON qu'au CSV. Le bon choix dépend de la forme de vos données, traitée dans JSON vs CSV.
Si pandas commence à vous sembler limitant sur une colonne que vous scrapez souvent, le plus vaste écosystème de bibliothèques Python de scraping et de données couvre les outils qui prennent le relais là où un seul DataFrame s'arrête.
Points clés
- Nettoyez avant d'analyser. Les données scrapées arrivent avec des cellules manquantes, des lignes en double et des nombres typés en chaînes ; corrigez cela d'abord ou chaque calcul ultérieur est suspect.
- Inspectez avec head, info et describe. Ces trois méthodes vous disent la forme, les dtypes et les comptages de valeurs manquantes, pour que vous sachiez exactement quelles étapes de nettoyage vous sont nécessaires.
-
Analysez les prix et les dates en types réels. Retirez les symboles monétaires et convertissez en flottant, convertissez les chaînes de date avec
to_datetime, et confirmez avecinfo()qu'aucune colonne numérique n'est encoreobject. - groupby plus agg, c'est là que vit l'insight. Répartissez par une clé, agrégez plusieurs métriques d'un coup avec l'agrégation nommée, et triez le résultat pour le lire d'un coup d'œil.
- Une entrée plus propre signifie moins de nettoyage. Plus vos données sont structurées quand elles atteignent pandas, plus tout ce pipeline se raccourcit.
Foire aux questions
Comment installer pandas en Python ?
Exécutez pip install pandas depuis votre terminal, idéalement à l'intérieur d'un environnement virtuel pour qu'il n'entre pas en conflit avec d'autres projets. Puis importez-le dans votre script avec import pandas as pd, l'alias conventionnel utilisé dans presque tout le code pandas. Si vous voulez aussi les graphiques rapides montrés ici, installez matplotlib en même temps avec pip install pandas matplotlib.
Quelle est la différence entre un DataFrame et une Series dans pandas ?
Un DataFrame est le tableau entier, lignes et colonnes ensemble, très proche d'un tableur ou d'une table de base de données. Une Series est une seule colonne de ce tableau, un tableau étiqueté à une dimension. Quand vous sélectionnez une colonne avec df["price"] vous obtenez une Series ; la plupart des méthodes de nettoyage comme fillna et str.replace opèrent sur une Series, et vous réaffectez le résultat à la colonne.
Comment convertir une chaîne de prix scrapée en un nombre ?
Utilisez l'accesseur .str pour retirer les caractères non numériques, puis convertissez la colonne. Pour une valeur comme "$1,299.00" l'appel est df["price"].str.replace(r"[$,]", "", regex=True).astype("float"), qui supprime le signe dollar et la virgule et transforme la colonne en un flottant sur lequel vous pouvez calculer. Revérifiez toujours ensuite avec df.info() que la colonne n'est plus d'un dtype object.
Dois-je supprimer ou remplir les valeurs manquantes dans des données scrapées ?
Cela dépend de l'importance de la colonne. Supprimez la ligne quand le champ manquant est critique et ne peut pas être raisonnablement deviné, une fiche sans prix est inutile pour l'analyse des prix. Remplissez la valeur quand la colonne est secondaire et qu'un remplaçant vaut mieux que perdre toute la ligne, par exemple en remplaçant une note manquante par la moyenne de la colonne pour que le produit compte encore dans les agrégats par catégorie. Utilisez dropna(subset=[...]) pour cibler des colonnes spécifiques plutôt que de supprimer toute ligne ayant une seule cellule vide.
Comment synthétiser des données scrapées par catégorie ?
Utilisez groupby suivi d'une agrégation. Pour une métrique, df.groupby("category")["price"].mean() donne le prix moyen par catégorie. Pour plusieurs d'un coup, utilisez l'agrégation nommée, df.groupby("category").agg(count=("product_name", "count"), avg_price=("price", "mean")), où chaque mot-clé devient une colonne de sortie. Enchaînez sort_values à la fin pour ordonner le résultat.
Puis-je nettoyer et analyser des données scrapées automatiquement ?
Oui. Enveloppez vos étapes de nettoyage dans une fonction et appelez-la sur chaque nouveau lot, puis exécutez tout le script selon une planification pour que chaque nouveau scrape soit traité de la même façon. Garder la logique dans une seule fonction reproductible, plutôt que dispersée dans des cellules de notebook, est ce qui rend le pipeline fiable à mesure que vos données grandissent.
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.
