Technologies et protections 8 min de lecture

Expressions régulières pour le parsing : syntaxe, motifs et limites

Les expressions régulières dans le parsing : syntaxe, motifs utiles, limites d'application et pourquoi le HTML exige un parseur, pas une regex.

ÉW
Équipe Web-Scraping.fr
Collecte de données pour votre activité
Publié le: 27 avril 2025

C'est le troisième volet du cycle. Dans « Le parseur DOM », nous avons appris à transformer une page en arbre de nœuds, et dans l'article sur les sélecteurs CSS — à adresser précisément les éléments voulus de cet arbre. Mais une fois le nœud trouvé, il contient souvent du texte « sale » : « Prix : 12 990,50 € (ancien 15 990) », un téléphone truffé de caractères parasites, une référence mêlée à la description. Extraire d'une telle chaîne précisément le nombre ou le code, c'est le rôle des expressions régulières.

Une expression régulière (regex) est un motif qui décrit un ensemble de chaînes. Le moteur regex parcourt le texte et trouve les fragments correspondant au motif. Dans le parsing de sites, c'est l'outil irremplaçable du nettoyage final et de l'extraction de données.

Quand les regex sont nécessaires — et quand non

D'abord, un avertissement important. On ne parse pas du HTML entier avec des expressions régulières. Le HTML est une structure imbriquée, non régulière ; tenter de la décomposer avec un seul motif mène à du code fragile et illisible. Pour naviguer dans le balisage, il y a les parseurs DOM et les sélecteurs CSS des articles précédents.

Les regex excellent dans un autre rôle — traiter un texte court déjà extrait :

  • tirer le nombre du prix d'une chaîne avec devise et espaces ;
  • normaliser un téléphone ou un email ;
  • extraire une référence, un SKU, une date, un montant de remise ;
  • décomposer les paramètres d'une URL ou le texte d'un script <script>.

La règle est simple : la structure se prend avec les sélecteurs, la valeur à l'intérieur du nœud — avec une regex.

Syntaxe de base

La plupart des caractères d'un motif se désignent eux-mêmes : le motif prix trouvera la sous-chaîne « prix ». La puissance vient avec les caractères spéciaux (métacaractères).

Symbole Signification
. n'importe quel caractère (sauf le saut de ligne)
\d un chiffre 0–9
\D un non-chiffre
\w une lettre, un chiffre ou _
\W non-\w
\s un caractère d'espacement (espace, tabulation, saut)
\S un caractère non blanc
\b une frontière de mot

Pour utiliser un métacaractère comme caractère ordinaire (par exemple le point d'un nombre), on l'échappe avec une barre oblique inverse : \., \$, \(.

Classes de caractères

Les crochets définissent un ensemble de caractères admis à une position donnée :

code
[aeiouy]          une des voyelles énumérées
[0-9]             n'importe quel chiffre (équivaut à \d)
[a-zA-Z]          n'importe quelle lettre latine
[À-ÿ]             les lettres accentuées (é, è, à, ç…)
[^0-9]            tout caractère SAUF un chiffre (^ entre crochets = négation)
[\d.,]            un chiffre, un point ou une virgule

Les plages (a-z, 0-9) et les énumérations se combinent. ^ en début de classe inverse l'ensemble.

Quantificateurs : combien de répétitions

Le quantificateur indique combien de fois se répète l'élément précédent.

Quantificateur Combien de fois
* 0 ou plus
+ 1 ou plus
? 0 ou 1 (facultatif)
{3} exactement 3
{2,5} de 2 à 5
{2,} 2 et plus
code
\d+               un chiffre ou plus : « 12990 »
\d{1,3}           de un à trois chiffres (groupe de milliers)
colou?r           « color » et « colour »

Quantificateurs gourmands et paresseux

Par défaut, les quantificateurs sont gourmands — ils capturent le plus possible. Un ? ajouté les rend paresseux — ils capturent le minimum :

code
".*"              gourmand : du premier guillemet au DERNIER de la ligne
".*?"             paresseux : du premier guillemet au PLUS PROCHE

C'est l'un des pièges les plus fréquents : le motif gourmand « avale » du superflu. En pratique, pour extraire des valeurs courtes, il faut presque toujours la variante paresseuse ou une classe de caractères plus étroite au lieu de ..

Ancres et frontières

Les ancres ne correspondent pas à des caractères — elles fixent le motif à une position :

code
^début             ^ — début de la chaîne
fin$               $ — fin de la chaîne
^\d+$              la chaîne est entièrement composée de chiffres
\bSKU\b            « SKU » comme mot isolé, pas comme partie d'un autre

Les ancres protègent des faux positifs : ^\d+$ garantit que la cellule contient uniquement un nombre, et non « 12 pcs ».

Groupes

Les parenthèses ( ) regroupent une partie du motif et capturent le fragment correspondant, pour pouvoir le récupérer ensuite.

code
(\d+)[.,](\d+)        partie entière et partie décimale du nombre séparément

Les groupes se numérotent de gauche à droite : le groupe 1 — (\d+) avant le séparateur, le groupe 2 — après.

Groupes non capturants

Si les parenthèses ne servent qu'à grouper (par exemple sous un quantificateur) mais que le résultat est inutile — on utilise (?: ) :

code
(?:https?://)?(\S+)   « http:// » ou « https:// » optionnel, sans le conserver

Groupes nommés

Pour ne pas compter les numéros, on donne un nom au groupe : (?P<name>...) (en Python) ou (?<name>...) :

code
(?P<euros>\d+)[.,](?P<cents>\d{2})

On y accède ensuite par match.group("euros") — lisible et robuste au réordonnancement des groupes.

Références arrière

À l'intérieur du motif, on peut renvoyer à un groupe déjà capturé via \1, \2 :

code
<(\w+)>.*?</\1>       balise appariée : l'ouvrante et la fermante portent le même nom
(["']).*?\1           chaîne entre guillemets simples ou doubles

\1 exige qu'à la seconde position se trouve le même caractère que celui capturé par le premier groupe.

Assertions avant et arrière

Les constructions lookaround vérifient le contexte sans l'inclure dans le résultat — très utile pour « s'accrocher » à une étiquette tout en ne renvoyant que la valeur.

Construction Signification
(?=...) devant, il y a... (positive lookahead)
(?!...) devant, il n'y a PAS... (negative lookahead)
(?<=...) derrière, il y a... (positive lookbehind)
(?<!...) derrière, il n'y a PAS... (negative lookbehind)
code
\d+(?=\s*€)           un nombre SUIVI du signe euro — seul le nombre est renvoyé
(?<=Prix :\s)\d+      un nombre PRÉCÉDÉ de « Prix : » — seul le nombre est renvoyé
\d+(?!\d)             le dernier chiffre du nombre

Le lookbehind est particulièrement pratique quand il faut extraire la valeur qui suit une étiquette fixe, sans traîner l'étiquette dans le résultat.

Drapeaux

Les drapeaux modifient le comportement de tout le motif :

Drapeau (Python) Effet
re.I / (?i) insensible à la casse
re.S / (?s) . correspond aussi au saut de ligne
re.M / (?m) ^ et $ agissent sur chaque ligne
re.X / (?x) mode « verbeux » — on peut commenter le motif
python
re.findall(r"prix", text, re.I)   # « Prix », « PRIX », « prix »

Exemples pratiques pour le parsing

Rassemblons les motifs typiques en Python (module re). Fonctions utiles : re.search (première correspondance), re.findall (toutes, en liste), re.sub (remplacement), re.finditer (itérateur avec groupes).

Le prix depuis une chaîne « sale » :

python
import re

text = "Prix : 12 990,50 €  (ancien 15 990 €)"

# On extrait le premier nombre, en ignorant les espaces de milliers
m = re.search(r"(\d[\d\s]*\d|\d)(?:[.,](\d{2}))?", text)
eur = re.sub(r"\s", "", m.group(1))     # « 12990 »
cts = m.group(2) or "00"                # « 50 »
price = float(f"{eur}.{cts}")           # 12990.5

Tous les prix du texte (le nouveau et l'ancien) :

python
prices = re.findall(r"\d[\d\s]*\d(?=\s*€)", text)
# ['12 990', '15 990']  → on nettoie ensuite les espaces
clean = [int(re.sub(r"\D", "", p)) for p in prices]   # [12990, 15990]

Référence / SKU :

python
sku = re.search(r"\bRéf[.:\s]+([A-Z0-9\-]+)", text)

Téléphone (normalisation en chiffres) :

python
digits = re.sub(r"\D", "", "+33 1 23 45 67 89")   # « 33123456789 »

Email :

python
emails = re.findall(r"[\w.+-]+@[\w-]+\.[\w.-]+", text)

Extraire une valeur JSON d'un script inline :

python
# Sur une fiche produit, le prix se trouve souvent dans <script> sous window.__DATA__ = {...}
m = re.search(r'"price"\s*:\s*"?(\d+(?:\.\d+)?)"?', script_text)

Les regex en tandem avec le DOM et les CSS

Les regex donnent le meilleur non pas à la place, mais avec les outils des articles précédents :

python
from bs4 import BeautifulSoup
import re

soup = BeautifulSoup(html, "html.parser")

# 1) Le sélecteur CSS extrait le nœud voulu
raw = soup.select_one("span.price").get_text(strip=True)

# 2) La regex nettoie le texte jusqu'au nombre
price = int(re.sub(r"\D", "", raw))

Le sélecteur répond au « où », la regex — au « quoi exactement dans ce texte ». Cette séparation rend le parseur à la fois précis et lisible.

Application à la veille tarifaire

Dans la veille tarifaire des boutiques en ligne, les expressions régulières couvrent le « dernier kilomètre » de l'extraction de données :

  • Normalisation du prix vers un format numérique unique : retirer les espaces de milliers, la devise, ramener la virgule au point — sinon les valeurs de différentes plateformes sont incomparables.
  • Décomposition de la remise depuis des chaînes comme « −23 % » ou « économisez 3 000 € ».
  • Nettoyage du stock : de « plus que 4 en stock », extraire la quantité \d+.
  • Identifiants produit — SKU, EAN/code-barres (\b\d{13}\b), référence — pour rapprocher un même produit chez différents vendeurs.
  • Données des scripts : beaucoup de boutiques placent le prix dans du JSON à l'intérieur de <script type="application/ld+json"> ou de variables inline — la regex y prélève le champ voulu sans parsing complet du JS.

Le tandem « sélecteur → regex » est le socle de notre moteur d'extraction de données web : il parcourt les fiches produit selon un calendrier, prélève les nœuds par CSS et ramène leur contenu à des nombres propres grâce aux expressions régulières.

Performance et pièges

  • Compilez les motifs fréquemment utilisésre.compile(...) une seule fois, plutôt qu'une réanalyse répétée dans une boucle sur des milliers de produits.
  • Évitez le backtracking catastrophique. Les quantificateurs ambigus imbriqués comme (\d+)+ ou (.*)* peuvent « geler » sur une chaîne choisie exprès (ou par hasard). Faites des classes de caractères étroites et préférez les quantificateurs paresseux aux gourmands là où c'est pertinent.
  • Ne compliquez pas à l'excès. Si le motif devient illisible, découpez la tâche en deux regex simples ou revenez aux sélecteurs — la valeur cherchée se trouve peut-être dans un attribut data- séparé, plus simple à prendre directement (voir l'article sur les sélecteurs CSS).
  • Testez sur des exemples réels. Les prix s'écrivent de mille façons : 1 990, 1.990, 1990.00, à partir de 1990. Passez le motif sur un lot de chaînes vivantes issues de différentes boutiques.

En résumé

Les expressions régulières sont un outil d'extraction de valeurs dans du texte, pas d'analyse de structure. Les métacaractères et les classes décrivent quels caractères sont attendus, les quantificateurs — combien, les groupes et le lookaround — comment isoler le fragment voulu dans son contexte. Associées au parsing DOM et aux sélecteurs CSS, elles forment le pipeline complet du parsing de sites : trouver le nœud, prendre le texte, le ramener à des données propres. C'est ce tandem qui garantit une veille tarifaire des boutiques en ligne fiable, où il faut extraire régulièrement des nombres comparables du balisage bigarré des concurrents.

Le cycle de base sur les outils du parsing s'achève ici : le DOM donne l'arbre, les sélecteurs CSS — l'adressage, les expressions régulières — le nettoyage final des données.