Le web scraping transforme des pages web publiques en données structurées que vous pouvez analyser, et l'étape d'analyse détermine la qualité des données obtenues. Python dispose de plusieurs bibliothèques d'analyse, mais Parsel se distingue par sa légèreté, sa rapidité et son intégration des deux langages de sélecteurs que la plupart des scrapers connaissent déjà: XPath et CSS. C'est le même moteur qui alimente Scrapy, et il fonctionne tout aussi bien de manière autonome quand vous disposez du HTML brut et souhaitez en extraire des champs en quelques lignes lisibles.
Ce guide est une présentation pas-à-pas exécutable. Vous installez Parsel, récupérez une page rendue via la Crawling API, chargez le HTML dans un Selector et extrayez des données avec XPath et CSS en utilisant .get() et .getall(). Ensuite, vous itérez sur une liste d'éléments, lisez du texte et des attributs, nettoyez les valeurs et exportez le résultat en JSON et CSV. La cible d'exemple est books.toscrape.com, un bac à sable public conçu spécifiquement pour pratiquer le scraping, vous pouvez donc exécuter chaque extrait de bout en bout sans toucher à un vrai site de production.
Ce que vous allez construire
Un petit script Python qui récupère une page de catalogue, construit un Selector Parsel à partir du HTML retourné, itère sur les cartes produit et extrait un enregistrement structuré par élément. De chaque carte de livre, nous extrayons ces champs:
- Titre le titre du livre, lu depuis un attribut de lien.
- Prix le prix affiché, nettoyé en nombre.
- Disponibilité le texte de disponibilité affiché sur la carte.
- Note la note en étoiles, lue depuis une classe CSS.
- Lien l'URL absolue de la page de détail du livre.
Pourquoi Parsel pour l'analyse en Python
Parsel est une bibliothèque de sélecteurs autonome. Vous lui fournissez une chaîne de HTML, elle construit un arbre et vous interrogez cet arbre avec des expressions XPath ou CSS. Elle occupe un juste milieu utile: plus légère qu'un framework complet comme Scrapy, et plus orientée sélecteurs que BeautifulSoup, qui s'appuie sur le chaînage de méthodes Python plutôt que sur des chaînes de sélecteurs. Les raisons pour lesquelles elle mérite une place dans une boîte à outils de scraping sont simples:
- Deux langages de sélecteurs. Utilisez XPath quand vous avez besoin de naviguer dans la structure ou de correspondre sur du texte, et CSS quand un sélecteur de classe ou de balise court est plus clair. Parsel supporte les deux sur le même objet.
- Petit et rapide. Il est construit sur lxml, donc l'analyse de grands documents reste rapide, et il n'y a presque aucune configuration au-delà de l'importation d'une classe.
-
Syntaxe propre.
.get()retourne la première correspondance,.getall()retourne toutes les correspondances, et les sélecteurs chaînés gardent le code d'extraction court et facile à maintenir.
Pour une référence plus approfondie sur les langages de sélecteurs eux-mêmes, l'article sur XPath et les sélecteurs CSS couvre la syntaxe en détail. Ici, nous nous concentrons sur leur mise en pratique avec Parsel.
Pourquoi récupérer via la Crawling API
Parsel analyse le HTML; il ne récupère pas les pages. Vous avez encore besoin de quelque chose pour récupérer d'abord le balisage, et c'est là que la plupart des scrapers rencontrent des problèmes. Une requête HTTP simple fonctionne bien sur une page statique simple, mais de nombreux sites modernes rendent leur contenu avec JavaScript, de sorte que la réponse brute est une coquille vide avec les données réelles manquantes. D'autres surveillent le trafic automatisé et limitent ou bloquent les requêtes qui ne ressemblent pas à un vrai navigateur.
La récupération via la Crawling API résout les deux problèmes en un seul appel. Vous lui envoyez une URL, elle rend la page si nécessaire, achemine la requête via une IP tournante de confiance et retourne le HTML fini, que vous introduisez directement dans un Selector Parsel. Cela garde les préoccupations de récupération (rendu, rotation, blocage) séparées des préoccupations d'analyse (sélecteurs, champs), ce qui est la séparation qui maintient un scraper maintenable.
Prérequis
Python de base. Vous devez être à l'aise avec l'exécution d'un script et l'installation de paquets avec pip. Aucune expérience préalable avec Parsel n'est nécessaire; ce guide présente l'API au fur et à mesure.
Python 3.8 ou version ultérieure. Vérifiez votre version avec python --version. Si vous n'avez pas Python, installez-le depuis python.org et assurez-vous qu'il est dans votre PATH.
Un compte Crawlbase et un token. Inscrivez-vous, ouvrez votre tableau de bord et copiez votre token de requête. Crawlbase inclut 1 000 requêtes gratuites pour commencer, ce qui est largement suffisant pour parcourir ce guide. Traitez le token comme un mot de passe et gardez-le hors du 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 script a besoin.
python --version python -m venv parsel_env source parsel_env/bin/activate pip install parsel crawlbase
Sur Windows, activez l'environnement avec parsel_env\Scripts\activate au lieu de la ligne source. parsel effectue l'extraction, et crawlbase est le client officiel qui récupère les pages rendues pour vous. Les modules json et csv sont inclus dans la bibliothèque standard, il n'y a donc rien d'autre à installer pour l'étape d'exportation.
Étape 1: Récupérer une page et construire un Selector
Commencez par récupérer une page de catalogue via la Crawling API et chargez son HTML dans un Selector Parsel. Importez CrawlingAPI, initialisez-le avec votre token, demandez l'URL et vérifiez l'en-tête pc_status avant d'analyser pour que les échecs restent visibles plutôt que silencieux.
from crawlbase import CrawlingAPI from parsel import Selector api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) def fetch_html(page_url): response = api.get(page_url) if response["headers"]["pc_status"] == "200": return response["body"].decode("utf-8") print(f"Request failed: {response['headers']['pc_status']}") return None if __name__ == "__main__": url = "https://books.toscrape.com/catalogue/page-1.html" html = fetch_html(url) if html: selector = Selector(text=html) print(selector.xpath("//title/text()").get())
Selector(text=html) est le point d'entrée de tout ce qui suit: il analyse la chaîne une fois et vous donne un objet que vous interrogez avec .xpath() et .css(). La dernière ligne lit le titre de la page avec une expression XPath, où /text() sélectionne le noeud texte et .get() retourne la première correspondance sous forme de chaîne. Exécutez le fichier et vous devriez voir le titre de la page de catalogue affiché, ce qui confirme que la récupération et l'analyse fonctionnent toutes deux avant d'écrire un seul sélecteur de champ.
L'étape fetch_html ci-dessus est la partie que Parsel ne peut pas faire seul, et sur une vraie cible, c'est là que le rendu et le blocage deviennent difficiles. La Crawling API prend votre token, rend les pages JavaScript si nécessaire, effectue la rotation via des IP résidentielles côté serveur et retourne le HTML fini, vous pouvez donc le passer directement dans un Selector sans faire tourner une flotte de navigateurs sans en-tête ou un pool de proxies vous-même. Commencez sur le niveau gratuit avec vos 1 000 requêtes.
Étape 2: Extraire avec XPath et CSS
Parsel vous permet d'interroger le même Selector avec l'un ou l'autre langage. XPath signifie XML Path Language et navigue dans l'arbre du document par structure, tandis que les sélecteurs CSS ciblent les éléments par balise, classe ou id de la même façon qu'une feuille de style. Les deux exemples ci-dessous extraient le même type de valeur pour que vous puissiez comparer directement les styles.
# XPath: select the text of the first h1 heading = selector.xpath("//h1/text()").get() # CSS: select the text inside a known element price = selector.css("p.price_color::text").get() # Attributes: @attr in XPath, ::attr() in CSS link_xpath = selector.xpath("//article//h3/a/@href").get() link_css = selector.css("article h3 a::attr(href)").get()
Deux schémas couvrent la majeure partie du travail. Pour lire du texte, utilisez /text() en XPath ou ::text en CSS. Pour lire un attribut comme href ou src, utilisez @attribute en XPath ou ::attr(attribute) en CSS. Dans tous les cas, .get() retourne la première correspondance, ou None si rien ne correspond, donc un élément manquant ne lève pas d'erreur.
.get() retourne la première valeur correspondante sous forme de chaîne. .getall() retourne une liste de toutes les correspondances. Recourez à .get() quand vous attendez une valeur unique comme un prix, et à .getall() quand vous voulez une colonne entière comme tous les titres d'une page.
Étape 3: Itérer sur une liste d'éléments
Les vraies pages contiennent de nombreux éléments répétés, pas un seul. Le schéma consiste à sélectionner le conteneur répétitif une fois, puis à itérer, en exécutant des sélecteurs délimités sur chaque élément pour construire un enregistrement par élément. Sur le bac à sable des livres, chaque produit est un <article class="product_pod">, c'est donc le conteneur sur lequel nous itérons.
def parse_books(selector): books = [] for card in selector.css("article.product_pod"): title = card.css("h3 a::attr(title)").get() price = card.css("p.price_color::text").get() availability = card.css("p.instock.availability::text").getall() rating = card.css("p.star-rating::attr(class)").get() href = card.css("h3 a::attr(href)").get() books.append({ "title": title, "price": price, "availability": availability, "rating": rating, "href": href, }) return books
Appeler .css("article.product_pod") retourne une SelectorList sur laquelle vous pouvez itérer; chaque card est elle-même un Selector, donc les appels .css() internes s'exécutent uniquement sur cette carte. Le titre se trouve dans l'attribut title du lien, le prix dans un paragraphe price_color, et la note dans une classe comme star-rating Three, c'est pourquoi nous lisons l'attribut class entier et le nettoyons à l'étape suivante. Le champ availability utilise .getall() car son texte est réparti entre des noeuds d'espacement; les joindre et les supprimer donne une chaîne propre unique.
Étape 4: Nettoyer et normaliser les valeurs
La sortie brute des sélecteurs nécessite généralement une légère passe avant d'être utilisable. Les prix portent un symbole monétaire, la note revient sous forme d'une classe à deux mots, et le texte de disponibilité arrive avec des espaces environnants. Quelques opérations de chaîne standard transforment chacun en une valeur propre.
BASE = "https://books.toscrape.com/catalogue/" WORDS = {"One": 1, "Two": 2, "Three": 3, "Four": 4, "Five": 5} def clean_book(card): price_text = card.css("p.price_color::text").get(default="") price = float(price_text.replace("£", "").strip() or 0) rating_class = card.css("p.star-rating::attr(class)").get(default="") rating_word = rating_class.replace("star-rating", "").strip() rating = WORDS.get(rating_word) stock = " ".join(card.css("p.instock.availability::text").getall()) href = card.css("h3 a::attr(href)").get(default="") return { "title": card.css("h3 a::attr(title)").get(), "price": price, "availability": stock.strip(), "rating": rating, "link": BASE + href, }
Deux petites habitudes rendent ce code résilient. Premièrement, .get(default="") fournit une valeur de secours pour qu'un élément manquant donne une chaîne vide plutôt que None, ce qui empêche les appels .replace() et .strip() en aval de lever une erreur. Deuxièmement, l'analyse du prix supprime le symbole monétaire (l'échappement £ est le signe livre sterling) et convertit en float, de sorte que la valeur peut être triée et filtrée comme un nombre. La note associe le mot dans la classe à un entier, et le href relatif est joint à l'URL de base pour produire un lien absolu.
Étape 5: Assembler le script complet
Maintenant, reliez les pièces en un seul script exécutable: récupérez la page, construisez le Selector, itérez sur les cartes via clean_book et exportez les enregistrements en JSON et CSV.
import csv import json from crawlbase import CrawlingAPI from parsel import Selector api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) BASE = "https://books.toscrape.com/catalogue/" WORDS = {"One": 1, "Two": 2, "Three": 3, "Four": 4, "Five": 5} def fetch_html(page_url): response = api.get(page_url) if response["headers"]["pc_status"] == "200": return response["body"].decode("utf-8") print(f"Request failed: {response['headers']['pc_status']}") return None def clean_book(card): price_text = card.css("p.price_color::text").get(default="") price = float(price_text.replace("£", "").strip() or 0) rating_class = card.css("p.star-rating::attr(class)").get(default="") rating = WORDS.get(rating_class.replace("star-rating", "").strip()) stock = " ".join(card.css("p.instock.availability::text").getall()) href = card.css("h3 a::attr(href)").get(default="") return { "title": card.css("h3 a::attr(title)").get(), "price": price, "availability": stock.strip(), "rating": rating, "link": BASE + href, } def parse_books(html): selector = Selector(text=html) return [clean_book(card) for card in selector.css("article.product_pod")] def save_outputs(records): with open("books.json", "w") as f: json.dump(records, f, indent=2) if not records: return with open("books.csv", "w", newline="") as f: writer = csv.DictWriter(f, fieldnames=records[0].keys()) writer.writeheader() writer.writerows(records) def main(): url = "https://books.toscrape.com/catalogue/page-1.html" html = fetch_html(url) if not html: return records = parse_books(html) save_outputs(records) print(f"Saved {len(records)} books") if __name__ == "__main__": main()
parse_books construit le Selector une fois et retourne une liste d'enregistrements nettoyés via une compréhension de liste sur les cartes. save_outputs écrit un fichier JSON et un CSV qui utilise les clés du premier enregistrement comme en-tête, vous obtenez donc les données dans la forme souhaitée par votre outil en aval. Pour couvrir l'ensemble du catalogue, enveloppez main dans une boucle sur page-1.html jusqu'à page-50.html et étendez une liste combinée unique; la logique d'analyse ne change pas.
À quoi ressemble la sortie
Exécutez le script avec python books_scraper.py et vous obtenez un enregistrement structuré propre par livre, prêt pour l'analyse, une base de données ou un tableur.
[ { "title": "A Light in the Attic", "price": 51.77, "availability": "In stock", "rating": 3, "link": "https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html" }, { "title": "Tipping the Velvet", "price": 53.74, "availability": "In stock", "rating": 1, "link": "https://books.toscrape.com/catalogue/tipping-the-velvet_999/index.html" } ]
Le CSV correspondant porte les mêmes colonnes, une ligne par livre, qui s'intègre directement dans pandas ou n'importe quel tableur pour le tri par prix ou le filtrage par note.
Erreurs courantes à éviter
Quelques habitudes distinguent un scraper qui tient la route d'un qui se casse à la prochaine exécution.
- Inspectez la page avant d'écrire des sélecteurs. Ouvrez la page dans les outils de développement de votre navigateur et confirmez les noms de classes et la structure. Un sélecteur visant un élément inexistant ne retourne rien, et c'est la raison la plus courante pour laquelle un scrape revient vide.
-
Gérez toujours les données manquantes. Utilisez
.get(default="")ou protégez-vous contreNonepour qu'un seul champ absent ne fasse pas planter toute la boucle. Les pages sont rarement aussi uniformes qu'elles le semblent. -
Nettoyez et normalisez le texte. Le texte web porte des espaces parasites et des symboles monétaires. Nettoyez-le avec
.strip()et.replace()au moment de l'analyse afin que vos valeurs stockées soient cohérentes. - Rythmez vos requêtes. Récupérer des pages dans une boucle serrée est le moyen le plus rapide d'être throttlé. Ajoutez un court délai entre les requêtes et maintenez votre volume raisonnable.
Scraper de manière responsable
Parsel n'analyse que le HTML que vous détenez déjà, mais la façon dont vous l'obtenez compte tout de même. Quelques principes maintiennent tout projet de scraping du bon côté de la ligne, quelle que soit la cible.
Vérifiez les conditions d'utilisation du site et son robots.txt avant de collecter quoi que ce soit, et traitez les deux comme des limites plutôt que des suggestions. Restez sur les données publiques que tout visiteur peut voir sans se connecter, et maintenez votre taux de requêtes raisonnable pour ne pas surcharger les serveurs du site. Quand un projet touche des données personnelles, les obligations croissent: des réglementations comme le RGPD et le CCPA régissent la façon dont les informations personnelles peuvent être collectées et utilisées, donc traitez ces cas avec un soin particulier ou évitez-les entièrement. L'exemple ici utilise un bac à sable conçu pour la pratique précisément pour que vous puissiez apprendre les mécaniques sans aucune de ces préoccupations, et la même discipline s'applique quand vous pointez votre scraper sur un vrai site. Pour en savoir plus sur le fonctionnement dans les limites d'un site, voir comment scraper des sites web sans se faire bloquer.
Points clés
-
Parsel est orienté sélecteurs. Construisez un
Selector(text=html)et interrogez-le avec XPath ou CSS, selon ce qui est le plus clair pour l'élément en question. -
get et getall couvrent la plupart des extractions.
.get()retourne la première correspondance sous forme de chaîne,.getall()retourne toutes les correspondances sous forme de liste, et.get(default="")empêche les champs manquants de faire planter l'exécution. -
Le texte et les attributs suivent un schéma fixe. Lisez le texte avec
/text()ou::text, et les attributs avec@attrou::attr(), en XPath et CSS respectivement. - Itérez sur un conteneur, pas sur la page entière. Sélectionnez l'élément répétitif une fois, puis exécutez des sélecteurs délimités sur chaque élément pour construire un enregistrement propre par élément, et exportez en JSON et CSV.
- Séparez la récupération de l'analyse. Laissez la Crawling API gérer le rendu, la rotation et les blocages, puis transmettez le HTML fini à Parsel pour que votre code d'extraction reste simple.
Foire aux questions
Qu'est-ce que Parsel et pourquoi l'utiliser pour le web scraping?
Parsel est une petite bibliothèque Python rapide pour extraire des données de HTML et XML en utilisant des sélecteurs XPath et CSS. C'est le même moteur de sélecteurs que Scrapy utilise, et il fonctionne bien comme outil autonome quand vous avez déjà le HTML et souhaitez en extraire des champs. On le choisit pour la syntaxe propre, le support des deux langages de sélecteurs sur le même objet, et la facilité avec laquelle il s'insère dans un pipeline existant.
Quelle est la différence entre Parsel et BeautifulSoup?
Les deux analysent le HTML, mais ils diffèrent dans leur style. Parsel est orienté sélecteurs: vous écrivez des expressions XPath ou CSS et appelez .get() ou .getall(). BeautifulSoup s'appuie sur le chaînage de méthodes Python comme find et find_all. Parsel supporte aussi XPath nativement, ce que BeautifulSoup ne fait pas. Choisissez celui qui correspond à la façon dont vous préférez exprimer vos sélections.
Quelle est la différence entre get et getall dans Parsel?
.get() retourne la première valeur correspondante sous forme de chaîne, ou None si rien ne correspond. .getall() retourne une liste de toutes les valeurs correspondantes. Utilisez .get() pour un seul champ comme un prix ou un titre, et .getall() quand vous voulez un ensemble entier, comme tous les liens d'une page. Passer .get(default="value") fournit une valeur de secours pour les éléments manquants.
Comment gérer les pages qui chargent du contenu avec JavaScript?
Parsel analyse le HTML que vous lui donnez, donc la question est de savoir comment vous récupérez ce HTML. Si une page rend son contenu avec JavaScript, une requête brute retourne une coquille vide avec les données manquantes. Récupérer via la Crawling API rend d'abord la page et retourne le HTML fini, que vous chargez ensuite dans un Selector exactement comme montré ici. Le code d'analyse ne change pas.
Puis-je exporter les résultats Parsel en JSON ou CSV?
Oui. Parsel vous donne des valeurs Python simples, donc une fois que vous avez construit une liste de dictionnaires, vous écrivez du JSON avec le module standard json et du CSV avec csv.DictWriter, comme le fait le script complet. Ensuite, les données s'intègrent dans pandas ou une base de données sans aucune conversion supplémentaire.
Pourquoi utiliser Parsel avec la Crawling API plutôt qu'une requête simple?
Une requête simple échoue souvent avant que Parsel ne s'exécute: la page peut rendre côté client, ou le site peut bloquer le trafic qui ne ressemble pas à un vrai navigateur. La Crawling API gère le rendu, la rotation des IP et les défis CAPTCHA, puis retourne du HTML propre. Cela maintient les préoccupations de récupération hors de votre code d'analyse, pour que Parsel puisse se concentrer sur la seule chose qu'il fait bien: transformer le HTML en champs structurés.
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.
