Chaque scraper réussit ou échoue selon une seule décision : la façon dont il localise l'élément voulu dans une page remplie de balises. Une erreur à ce niveau et votre script casse dès que le site modifie légèrement un nom de classe ou déplace un wrapper ; bien maîtrisée, la même logique de parsing peut tourner pendant des mois. Les deux langages de requête auxquels on fait appel ici sont XPath et les sélecteurs CSS pour le web scraping, et la plupart des scrapers en production combinent les deux plutôt que d'en choisir un seul.

Ce guide est une référence pratique sur les sélecteurs. Nous parcourons les sélecteurs CSS et XPath côte à côte, montrons l'expression équivalente dans chaque langage pour les mêmes éléments, les exécutons tous les deux avec de vraies bibliothèques Python, et expliquons quand l'un l'emporte clairement sur l'autre. À la fin, vous saurez lequel utiliser sur une page donnée, et comment écrire des sélecteurs qui résistent à une refonte plutôt que de se casser sur elle.

Les deux langages en un coup d'oeil

Les sélecteurs CSS sont les motifs que vous écrivez déjà dans les feuilles de style : .price, #header, div > span. Les navigateurs les évaluent en permanence, toutes les bibliothèques de scraping les prennent en charge, et ils se lisent facilement pour les cas courants. C'est le chemin le plus court vers "récupérer cet élément" lorsqu'une page dispose de classes et d'identifiants cohérents.

XPath ("XML Path Language") est un langage de requête complet pour naviguer dans un arbre de document. Il traite la page comme des nœuds qu'on peut parcourir dans toutes les directions : vers les enfants, vers les ancêtres, vers les frères et sœurs. Il peut correspondre sur le contenu textuel, filtrer avec des conditions booléennes, et combiner des prédicats d'une manière que CSS ne peut tout simplement pas exprimer. Cette puissance a un coût en verbosité, mais elle s'avère payante sur les pages désordonnées ou profondément imbriquées.

Les deux ciblent le même DOM. La différence réside dans la portée et l'ergonomie : CSS est concis et familier, XPath est précis et expressif. Savoir où chacun atteint ses limites est tout l'enjeu.

Les sélecteurs CSS, champ par champ

Les sélecteurs CSS localisent les éléments par balise, classe, identifiant, attribut, relation et position. Voici les briques de base que vous utiliserez quotidiennement, avec le balisage que chacune cible.

Balise, classe et identifiant. Les trois points de départ les plus courants. Un nom de balise seul correspond à chaque élément de ce type, un point en tête correspond à une classe, et un dièse en tête correspond à un identifiant.

css
a                  /* every anchor on the page */
.product-title     /* any element with class product-title */
#product-price     /* the element with id product-price */
span.price-label   /* span elements that also have class price-label */

Descendant vs enfant direct. Un espace signifie "n'importe où à l'intérieur", à n'importe quelle profondeur. Un > signifie "enfant direct uniquement", un seul niveau en dessous. La distinction est importante quand une mise en page imbrique la même balise à plusieurs niveaux et que vous voulez seulement l'immédiate.

css
div.price-container span      /* any span inside, at any depth */
div.price-container > span    /* only spans that are direct children */

Attributs. Les crochets permettent de correspondre sur n'importe quel attribut, pas seulement la classe et l'identifiant. Correspondance exacte avec [attr=val], sous-chaîne avec [attr*=val], préfixe avec [attr^=val], suffixe avec [attr$=val]. Les sélecteurs d'attributs sont souvent votre ancre la plus stable, car les attributs de données changent moins que les classes visuelles.

css
a[role='link']            /* anchors with role exactly "link" */
[data-testid='price']     /* any element with that test id */
a[href^='/product/']      /* anchors whose href starts with /product/ */

Position. Les pseudo-classes sélectionnent selon la place dans l'ordre des frères. :first-child, :last-child, et le polyvalent :nth-of-type(n) permettent de saisir le n-ième élément d'une balise donnée, ce qui permet d'extraire "la deuxième ligne" ou "le quatrième élément de liste" d'un bloc répété.

css
.product-list li:first-child       /* first item in the list */
ul.specs li:nth-of-type(3)         /* the third li */
table tr:nth-of-type(2) td         /* cells in the second row */

Cet ensemble couvre la grande majorité des travaux d'extraction réels. Là où CSS manque de place, c'est pour correspondre sur le texte qu'un élément contient, et pour remonter l'arbre depuis un nœud connu. Pour ces cas, on se tourne vers XPath.

XPath, champ par champ

XPath exprime un chemin à travers le document. Un // en tête signifie "chercher n'importe où dans l'arbre", un / simple signifie "enfant direct", et les crochets contiennent des prédicats qui filtrent les nœuds correspondants. Voici les mêmes types de cibles écrits en XPath.

Balise et descendant. Le double slash est votre ouverture habituelle ; il trouve les éléments correspondants à n'importe quelle profondeur.

xpath
//div                         (: every div on the page :)
//div[@class='price-container']/span   (: direct span children :)

Prédicats sur les attributs. À l'intérieur des crochets, on teste les attributs avec @. La correspondance exacte est [@class='x'] ; pour les classes qui portent plusieurs valeurs séparées par des espaces, contains(@class, 'x') est plus sûr car il correspond quand x est l'une parmi plusieurs.

xpath
//*[@id='product-price']                  (: by id :)
//*[contains(@class, 'product-title')]    (: class among many :)
//a[@href]                                (: any anchor that has an href :)

Correspondance textuelle. C'est la fonctionnalité phare de XPath. On peut sélectionner un élément par le texte qu'il contient, exactement avec text()='...' ou de manière approximative avec contains(text(), '...'). CSS n'a rien d'équivalent.

xpath
//button[text()='Add to Cart']
//span[contains(text(), 'In stock')]
//label[normalize-space()='Email address']

Position. Les index XPath sont basés sur 1 et se placent dans des prédicats. On peut aussi utiliser des fonctions comme last() et position() pour sélectionner depuis la fin ou une plage.

xpath
(//div[@class='product'])[1]      (: first matching product :)
//ul[@class='specs']/li[3]        (: the third li :)
//ul/li[last()]                   (: the final li :)

Les axes. Le vrai atout. Les axes permettent de se déplacer dans des directions que CSS ne peut pas atteindre : following-sibling, preceding-sibling, parent, et ancestor. Le cas classique est une paire étiquette-valeur où l'on connaît le texte de l'étiquette et on veut la valeur à côté.

xpath
(: the value cell next to the "Founded" label :)
//th[text()='Founded']/following-sibling::td

(: walk up from a price to its product card :)
//span[@class='price']/ancestor::div[@class='card']

Ces deux dernières requêtes sont le type de requête sans équivalent CSS propre, ce qui explique exactement pourquoi XPath reste dans la boîte à outils.

Côte à côte : le même élément, des deux façons

Voir les langages alignés rend les compromis concrets. Pour les cibles courantes, les deux sont quasi-équivalents, et CSS est généralement plus court.

text
Goal                      CSS                          XPath
all anchors               a                            //a
class match               .product-title               //*[contains(@class,'product-title')]
id match                  #product-price               //*[@id='product-price']
tag + class               span.price-label             //span[@class='price-label']
descendant                .box span                    //*[@class='box']//span
direct child              .box > span                  //*[@class='box']/span
attribute exact           a[role='link']               //a[@role='link']
nth of type               li:nth-of-type(3)            //li[3]
text match                (not possible)               //button[text()='Buy']
walk up the tree          (not possible)               //span/ancestor::div[@class='card']

Le schéma est clair : pour les balises, classes, identifiants, attributs et positions, le choix est surtout une question de goût, et CSS l'emporte en concision. Les deux dernières lignes sont là où XPath se distingue seul.

Exécuter les deux en Python

La théorie ne va que jusqu'à un certain point ; voici à quoi ressemble chaque langage dans le code. Nous utilisons parsel (la bibliothèque de sélecteurs sur laquelle Scrapy est construit) car elle parle à la fois CSS et XPath sur le même document analysé, ce qui permet de les comparer ligne par ligne. BeautifulSoup et lxml sont les autres choix courants, mentionnés ensuite.

bash
python -m venv selectors_env
source selectors_env/bin/activate

pip install parsel

Chargez un balisage une fois, puis interrogez-le des deux façons. Notez que les méthodes .css() et .xpath() de parsel renvoient toutes deux une liste de sélecteurs, donc le motif d'accès est identique quel que soit le langage.

python
from parsel import Selector

html = """
<div class="card">
  <h2 class="product-title">Wireless Mouse</h2>
  <span class="price">$24.99</span>
  <a role="link" href="/product/mouse">Details</a>
</div>
"""

sel = Selector(text=html)

# CSS: concise and familiar
title = sel.css("h2.product-title::text").get()
price = sel.css("span.price::text").get()
link  = sel.css("a[role='link']::attr(href)").get()

# XPath: the same three fields
title = sel.xpath("//h2[@class='product-title']/text()").get()
price = sel.xpath("//span[@class='price']/text()").get()
link  = sel.xpath("//a[@role='link']/@href").get()

print(title, price, link)

Pour BeautifulSoup, le chemin CSS est soup.select_one("span.price") et soup.select(...) pour plusieurs ; elle ne prend pas en charge XPath nativement. Quand vous avez besoin de XPath spécifiquement, lxml est l'outil standard : tree.xpath("//span[@class='price']/text()") sur un document lxml.html analysé. parsel est le juste milieu pratique car il vous offre les deux API sur un seul objet.

CSS se compile en XPath en coulisses

Les bibliothèques comme parsel et lxml traduisent un sélecteur CSS en XPath avant de l'exécuter (via le paquet cssselect). C'est pourquoi tout ce qu'on peut exprimer en CSS a un équivalent XPath, mais pas l'inverse : la correspondance textuelle et les axes ascendants n'ont pas de forme CSS à partir de laquelle traduire. Quand un sélecteur CSS ne peut pas dire ce que vous voulez, passer à XPath est l'étape naturelle suivante, pas un contournement.

Quand XPath l'emporte

Utilisez XPath quand la page résiste. Trois situations en font le choix évident.

  • Vous devez correspondre sur le texte. "Le bouton qui dit Add to Cart" ou "la ligne dont l'étiquette est Founded" ne peut être exprimé que par le contenu. //button[text()='Add to Cart'] et contains(text(), ...) n'ont pas d'équivalent CSS.
  • Vous devez remonter l'arbre. Quand vous pouvez trouver une feuille de façon fiable, comme un prix unique, mais que l'élément que vous voulez vraiment est son conteneur, ancestor::div[@class='card'] remonte. CSS va seulement vers le bas et sur les côtés, jamais vers le haut.
  • Vous avez besoin de conditions composées. Les prédicats XPath se combinent avec and et or : //div[@class='item' and @data-available='true'], ou un filtrage simultané sur la position et l'attribut. Empiler des conditions de cette façon est maladroit ou impossible en CSS.

Le motif étiquette-valeur est celui que vous rencontrerez le plus souvent. Dans un tableau de spécifications ou une barre latérale de profil, le champ voulu se trouve dans une cellule à côté d'une étiquette stable, tandis que sa propre classe est générique ou absente. Ancrer sur le texte de l'étiquette et se déplacer latéralement avec following-sibling est bien plus robuste que compter des positions :nth-of-type qui changent quand un champ est ajouté ou supprimé.

Quand CSS l'emporte

Pour la majorité des cas courants, CSS est le meilleur choix par défaut. Il est plus court, plus lisible, et la syntaxe est celle que la plupart des développeurs connaissent déjà grâce au travail front-end, de sorte qu'un collègue peut examiner vos sélecteurs sans apprendre un second langage. Sur une page bien structurée avec des classes et identifiants cohérents, .product-card .price dit tout ce dont vous avez besoin en moins d'espace que son équivalent XPath.

CSS se marie aussi naturellement avec les outils d'automatisation de navigateur. Quand vous pilotez un navigateur sans tête et devez scraper du contenu dynamique, les mêmes sélecteurs CSS que vous écririez dans document.querySelector se transposent directement, ce qui maintient votre vocabulaire de sélecteurs cohérent entre les parties d'analyse statique et de DOM en direct d'un projet. Pour une extraction simple, rapide et répétée sur une mise en page soignée, CSS est le bon outil, et vous n'escaladez vers XPath que lorsque CSS ne peut vraiment pas exprimer la cible.

Crawlbase Crawling API

Les sélecteurs sont inévitables quand vous analysez du HTML brut vous-même, mais ils ne sont pas toujours votre travail. La Scraper API analyse automatiquement les types de pages courants, tels que les pages produit, les résultats de recherche et les avis, en JSON structuré, de sorte que pour les cibles prises en charge, vous ignorez complètement XPath et CSS et lisez simplement les champs de la réponse. Là où vous avez besoin d'une analyse personnalisée, combinez-la avec du HTML rendu et analysez avec les sélecteurs ci-dessus. Commencez avec le niveau gratuit.

Écrire des sélecteurs qui ne cassent pas

La partie difficile du scraping n'est pas de choisir un langage ; c'est d'écrire des sélecteurs qui survivent au prochain déploiement du site. Les mêmes règles de robustesse s'appliquent à XPath et à CSS.

  • Préférez les attributs stables aux classes visuelles. Les classes hachées ou utilitaires comme css-1x7a9q ou mt-4 sont générées et changent souvent. Un data-testid, un id, un itemprop, ou un role ARIA est bien plus susceptible de survivre à un restyling. Ancrez-vous dessus quand ils existent.
  • Évitez les chaînes longues et profondes. Un sélecteur comme body > div > div > section > div:nth-child(2) > ul > li encode toute la mise en page, donc tout wrapper ajouté n'importe où le long de ce chemin le casse. Ciblez le conteneur significatif le plus proche et une ancre stable plutôt que de tracer toute l'ascendance.
  • Ne vous appuyez pas sur des positions fragiles. :nth-of-type(4) suppose que le compte ne change jamais. Quand une étiquette ou un attribut stable est disponible, ancrez-vous dessus et naviguez relativement (c'est là que les axes XPath brillent), plutôt que de coder en dur un index.
  • Utilisez contains pour les classes à valeurs multiples. Un élément avec class="btn btn-primary active" ne correspondra pas exactement à [@class='btn-primary']. Utilisez contains(@class, 'btn-primary') en XPath, ou le sélecteur de classe simple .btn-primary en CSS, qui correspond déjà à une classe parmi plusieurs.
  • Échouez bruyamment, pas silencieusement. Encapsulez l'extraction pour qu'un champ manquant retourne None au lieu de planter, puis journalisez quel sélecteur est revenu vide. Cela transforme un changement de site d'une mystérieuse entrée vide en un signal clair indiquant quel sélecteur nécessite une mise à jour.

Traitez les sélecteurs comme du code qui nécessite une maintenance. Le balisage évolue, et un scraper qui fonctionnait bien le trimestre dernier finira par retourner des champs vides. La correction consiste presque toujours à réinspecter l'élément en direct dans les outils de développement et à affiner le sélecteur, pas à reconstruire le scraper. Pour la mise en place globale, le guide sur comment scraper un site web avec Python présente la récupération, l'analyse et le stockage de bout en bout, et ces motifs de sélecteurs s'intègrent directement dans ce flux.

Se passer entièrement de sélecteurs

Parfois, le meilleur sélecteur est l'absence de sélecteur. Si votre cible est un type de page courant, la Crawling API retourne du JSON analysé directement, donc il n'y a rien à sélectionner. Pour tout le reste, vous récupérez et analysez vous-même, et le HTML rendu que vous analysez peut provenir de la Crawling API quand une page est rendue côté client ou protégée. Dans tous les cas, les compétences en sélecteurs présentées ici sont ce qui transforme un balisage brut en enregistrements propres, et connaître les deux langages signifie que vous ne serez jamais bloqué parce que l'un d'eux ne peut pas atteindre un élément.

Récapitulatif

Points clés

  • Les deux ciblent le même DOM. Les sélecteurs CSS sont concis et familiers ; XPath est verbeux mais plus expressif. La plupart des scrapers réels utilisent un mélange des deux.
  • CSS couvre les balises, classes, identifiants, attributs et positions avec des motifs courts et lisibles : .class, #id, div > span, [attr=val], :nth-of-type(n).
  • XPath fait ce que CSS ne peut pas : correspondre sur le texte avec text() et contains(), remonter l'arbre avec ancestor, se déplacer latéralement avec following-sibling, et combiner des conditions avec and/or.
  • Exécutez les deux en Python avec parsel (.css() et .xpath() sur un seul objet) ; BeautifulSoup est uniquement CSS, lxml est la référence pour XPath.
  • La robustesse prime sur l'astuce. Préférez les attributs stables, évitez les chaînes profondes et les index fragiles, utilisez contains pour les classes à valeurs multiples, et échouez bruyamment quand un champ manque.
  • Vous pouvez ignorer les sélecteurs sur les types de pages pris en charge grâce à l'analyse automatique de la Crawling API, et réserver les sélecteurs écrits à la main pour les cibles personnalisées.

Foire aux questions

Lequel est le mieux pour les débutants, XPath ou les sélecteurs CSS ?

Les sélecteurs CSS, dans la plupart des cas. La syntaxe recoupe ce que vous connaissez déjà du style des pages, elle se lit facilement pour les cibles de balises, classes, identifiants et attributs, et toutes les bibliothèques de scraping la prennent en charge. Apprenez XPath ensuite, spécifiquement pour les choses que CSS ne peut pas faire : correspondre sur le contenu textuel et naviguer vers le haut ou latéralement dans l'arbre.

XPath et les sélecteurs CSS sont-ils pris en charge par toutes les bibliothèques de scraping ?

La plupart prennent en charge au moins l'un et beaucoup prennent en charge les deux. parsel et Scrapy gèrent CSS et XPath sur le même objet, lxml est conçu pour XPath, et Selenium et Playwright acceptent les deux. BeautifulSoup est l'exception notable : elle prend en charge CSS via .select() mais n'a pas de XPath natif. Consultez la documentation de votre bibliothèque avant de vous engager dans un style de sélecteur.

Un sélecteur CSS peut-il correspondre à un élément par son texte ?

Non. CSS n'a aucun moyen de sélectionner un élément par le texte qu'il contient ; il correspond uniquement aux balises, classes, identifiants, attributs et positions. Quand vous avez besoin de "le bouton qui dit Add to Cart" ou "la cellule à côté de l'étiquette Founded", c'est exactement le cas pour text() et contains(text(), ...) de XPath, qui n'ont pas d'équivalent CSS.

XPath est-il plus rapide que les sélecteurs CSS ?

Dans la plupart des travaux de scraping, la différence est négligeable, car les bibliothèques compilent souvent CSS en XPath en interne avant de l'exécuter. Choisissez en fonction de l'expressivité et de la lisibilité plutôt que de la vitesse brute. Si un sélecteur CSS exprime clairement ce dont vous avez besoin, utilisez-le ; passez à XPath quand vous avez besoin de correspondance textuelle, de navigation ascendante, ou de conditions composées que CSS ne peut pas exprimer.

Comment écrire des sélecteurs qui ne cassent pas quand le site change ?

Ancrez-vous sur des points d'accroche stables comme id, data-testid, itemprop, ou les rôles ARIA plutôt que sur des classes visuelles générées. Gardez les sélecteurs courts en ciblant le conteneur significatif le plus proche plutôt que de tracer toute l'ascendance, évitez les index de position codés en dur quand une étiquette stable existe, et utilisez contains pour les classes à valeurs multiples. Puis échouez bruyamment sur les champs manquants pour qu'un changement de balisage apparaisse comme un signal clair, pas un blanc silencieux.

Quand faut-il se passer complètement de sélecteurs ?

Quand votre cible est un type de page courant qu'un service d'analyse automatique comprend déjà. La Crawling API retourne du JSON structuré pour les cibles prises en charge comme les pages produit et les résultats de recherche, donc il n'y a pas de HTML à analyser et pas de sélecteur à maintenir. Gardez XPath et CSS écrits à la main pour les pages personnalisées ou les champs que l'analyseur automatique ne couvre pas.

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