Les expressions régulières décrivent des sous-ensembles de chaînes de caractères. Souvent peu appréciées pour leur syntaxe parfois alambiquée, elles offrent pourtant de réelles possibilités pour les développeurs et les référenceurs. Les regex (mot-valise issu de regular expression) répondent à d’innombrables usages, notamment en matière d’extraction de contenus ou de reconnaissance de chaînes, souvent utiles dans les outils ou programmes préférés des SEO.

La maîtrise des expressions régulières passe par une bonne connaissance de la syntaxe des motifs (modèles ou pattern en anglais). Chaque développeur ou référenceur doit être capable de lire et d’écrire des regex plus ou moins complexes pour atteindre ses objectifs ou aller plus loin dans les optimisations.

Comment aborder les expressions régulières ?

La complexité d’écriture de certaines expressions rationnelles (autre nom attribué aux regex) a plus tendance à rebuter les profanes qu’à les enjouer. Les exemples ci-dessous, qui décrivent le schéma typique d’une adresse email, ont de quoi faire peur au premier abord :

# Version longue :
^[A-Za-z0-9'._+-]+@([A-Za-z0-9_-]+.)*+[A-Za-z]{2,}

# Variante en version « compressée » :
^[w-.]+@([w-]+.)+[w-]{2,}$

Ces deux premiers exemples, équivalents, démontrent l’étendue des possibilités d’écriture mais aussi la complexité apparente de la syntaxe des motifs. Vous pouvez donc vous demander par où commencer, et comment arriver à atteindre des objectifs encore plus complexes que la description d’un schéma d’adresse email. En réalité, c'est sûrement bien plus simple qu’en apparence, car l’idée est avant tout de formaliser à l’oral ou à l’écrit ce dont on a besoin.

L’idée générale est de démarrer avec un objectif clair qui peut être facilement décrit. Ensuite, vous n’avez plus qu’à transcrire cette formalisation dans la syntaxe propre aux regex. Avant d'aborder la syntaxe en détail, prenez l’exemple d’un hashtag. Comment les réseaux sociaux ont-ils réussi à extraire ou intégrer ce type de chaîne dans les plateformes ? D’abord il a fallu décrire simplement ce à quoi ressemble un hashtag, puis développer l’extraction/intégration. Vous pouvez donc raisonner ainsi : un hashtag est un mot, précédé d’un hash (croisillon représenté par le caractère « dièse » en France), lui-même précédé par un espace ou une ponctuation (ou le début de la phrase), et conclut par un espace ou une ponctuation (selon que l’on est au milieu ou en fin de phrase).

Il ne reste qu’à développer, à l’aide de la syntaxe des regex, ce que vous venez de décrire, étape par étape :

// Étape 1 : le hashtag est avant tout un mot :
[a-zA-Z0-9_-]+ // On prend toutes les lettres (minuscules et majuscules), les chiffres, l’underscore et le tiret.
[w-]+ // Variante équivalente pour décrire la même chaîne que précédemment.

// Étape 2 : le mot est précédé d’un hash (« dièse ») :
#[a-zA-Z0-9_-]+ // ou #[w-]+ (la double syntaxe ne sera plus présentée mais vous avez compris le principe)

// Étape 3 : le hashtag est précédé par un espace :
[ ]+#[a-zA-Z0-9_-]+ // [ ] peut aussi être remplacé par [[:space:]] ou [s] (sens plus large)

// Étape 4 : le hashtag peut aussi être précédé par une ponctuation plutôt qu’un espace :
[ !?;:(.]+#[a-zA-Z0-9_-]+ // En théorie, seuls l’espace et la parenthèse ouvrante devraient suffire.

// Étape 5 : le hashtag est suivi par un espace ou un caractère de ponctuation :
[ !?;:).]+#[a-zA-Z0-9_-]+[ !?;:).]+

// Étape 6 : le hashtag peut être encadré pour pouvoir l’extraire (si nécessaire) :
[ !?;:).]+(#[a-zA-Z0-9_-]+)[ !?;:).]+

Ce développement point par point montre comment il est possible de réaliser des expressions régulières plus ou moins complexes. L’idée est toujours de partir du sous-ensemble commun, puis de décrire ce qui peut exister autour de lui. Dans la description d’un email, le dénominateur commun est le « @ » par exemple, qui est encadré par des suites de caractères précises en amont et en aval.

L’exemple du hashtag décrit ici est incomplet et n’est pas la meilleure forme possible en termes de regex. De plus, il faudrait forcer l’ajout d’un espace avant et après le texte analysé afin d’éviter les problèmes de début et de fin de chaîne. En effet, si le hashtag est placé en début de phrase par exemple, l’expression écrite ici attend soit un espace, soit une ponctuation, mais pas rien du tout. En ajoutant volontairement un espace en début et fin de chaîne, on évite ces cas problématiques.

La syntaxe générale des expressions régulières

Les concepteurs de regex doivent connaître les divers niveaux de syntaxe des motifs, qui se classent en plusieurs catégories :

  • Délimiteurs : signes qui délimitent ou encadrent les sous-chaînes (pas obligatoire dans certains cas ou certains langages).
  • Meta-caractères : caractères types nécessaires pour le bon fonctionnement des expressions régulières.
  • Quantificateurs : signes qui indiquent le nombre de fois pour lequel un caractère ou un groupe de caractères doit être répété.
  • Classes et intervalles : groupe de caractères
  • Caractères : les signes ou caractères que l’on veut repérer ou extraire (lettres, chiffre, ponctuation…).

Tous les signes de la syntaxe des expressions régulières doivent être échappés (avec un antislash «  ») si vous voulez l’utiliser en tant que caractères au sens propre. L’échappement permet d’éviter les confusions entre la syntaxe initiale des regex et les caractères à analyser ou extraire dans les chaînes de caractères.

Les délimiteurs

Les délimiteurs sont utiles dans certains langages de programmation pour marquer le début et la fin d’une expression régulière. Cela peut être n’importe quel caractère mais deux conditions doivent être respectées :

  1. le même signe doit être utilisé pour le début et la fin du motif ;
  2. le signe doit être échappé partout dans le motif s’il doit être utilisé.

Idéalement, il est conseillé d’utiliser un signe qui n’apparaît pas dans les motifs, et qui est suffisamment rare pour être marquant. Très souvent, les signes utilisés sont le hash « # », le pourcent « % », le point d’exclamation « ! » ou encore le slash « / », mais d’autres signes peuvent être choisis à la place si le cas se présente.

Cet article ne présente pas les « drapeaux », c’est-à-dire des options qui se placent après le délimiteur final et qui permettent de donner des comportements différents aux regex. L’exemple le plus courant est le drapeau « g » en JavaScript (pour « global ») qui indique que les méthodes utilisées doivent appliquer les recherches de sous-chaînes à l’ensemble du texte, et pas seulement à la première occurrence. Chaque langage et outil utilisent les drapeaux différemment, et parfois pas du tout, donc il ne semblait pas opportun d’entrer en matière avec des spécificités si complexes.

Les méta-caractères

Il s’agit de signes qui ont une signification précise dans les expressions régulières :

  • ^ : marque le début d’une expression régulière.
  • $ : marque la fin d’une expression régulière.
  • : sert de caractère d’échappement. Par exemple « $ » indique qu’il faut chercher le signe « $ », et non qu’il s’agit de la fin d’une chaine. Dans le même esprit, « » indique qu’on vise le signe antislash puisque ce dernier est échappé.
  • | : le pipe est l’équivalent du « ou » inclusif, il est utile au sein d’une classe notamment (voir plus loin) mais peut aussi être utiliser sans.
  • . : le point est le signe universel, qui correspond à n’importe quel signe (sauf le retour charriot n)

Les quantificateurs

Ces signes doivent être placés après un caractère, une classe ou un intervalle, pour marquer le nombre de répétitions du motif :

  • ? : 0 ou 1 fois
  • + : au moins 1 fois
  • * : 0 ou x fois
  • {x,} : au moins x fois
  • {0,x} : de 0 à x fois
  • {x,y} : entre x et y fois

Les classes

Les classes permettent de grouper des caractères :

  • () : les parenthèses permettent de créer des groupes de signes.
  • [] : les crochets encadrent les signes à rechercher dans une chaîne (lettres, chiffres…).
  • [^ ] : les crochets débutant par un chapeau forment une classe de signes à exclure au sein d’une recherche dans une chaîne (c’est l’inverse des crochets classiques).
  • [ - ] : un tiret compris entre deux caractères au sein d’une classe (crochets) créé un intervalle de caractères. Par exemple, [a-z] signifie que tous les caractères alphabétiques en minuscule sont à rechercher.

Le cas du tiret est particulier car il peut être considéré comme le signe indiquant un intervalle, ou tout simplement un vrai tiret. Pour éviter toute confusion, il faut indiquer le tiret en fin de classe pour qu’il ne soit pas lu comme le signe de l’intervalle. Si vous souhaitez par exemple que toutes les lettres, les chiffres, l’underscore et le tiret soient autorisés, il faudrait écrire le tiret à la fin, comme ceci : [a-zA-Z0-9_-].

Les caractères

Il s’agit de tous les signes qui doivent être recherchés au sein des motifs. Tout type de signe est donc autorisé, même ceux déjà utilisés par la syntaxe des expressions régulières (il faut alors les échapper avec le slash). Vous pouvez écrire les motifs à la main ou en utilisant des classes de caractères déjà établies pour certains langages.

Au-delà des classes créées manuellement (comme [a-zA-Z0-9_-]), des classes raccourcies ont été créées pour aider les développeurs à ne pas écrire des tartines de signes :

Raccourci Signification
d Indique un chiffre. Équivalent de [0-9]
D Indique tout ce qui n’est pas un chiffre. Équivalent de [^0-9]
w Indique un caractère alphanumérique ou un underscore. Équivalent de [a-zA-Z0-9_]
W Indique tout ce qui n’est pas un caractère alphanumérique ou underscore. Équivalent de [^a-zA-Z0-9_]
t Indique une tabulation
n Indique une nouvelle ligne
r Indique un retour chariot
s Indique un espace blanc (correspond à t n r)
S Indique tout ce qui n’est pas un espace blanc (t n r)
. Indique n’importe quel signe (sauf n)

 

Il existe également d’autres classes, les POSIX, qui permettent de signifier des types de signes ou groupes de caractères. Elles ne seront pas présentées ici car elles sont rarement fonctionnelles avec les outils et langages courants (seuls quelques-uns en profitent).

Écrire et utiliser des expressions régulières en Javascript et PHP

Les expressions régulières peuvent être utilisées dans pléthores de langages de programmation ou d’outils mais les cas de regex rencontrés les plus couramment sur le Web proviennent généralement de PHP ou JavaScript, dont l’écriture et les usages diffèrent quelque peu…

Regex et JavaScript

Il est possible de créer des expressions régulières de deux manières en JavaScript : soit avec une instance de la classe native RegExp() avec une regex en paramètres, soit en écrivant une expression littérale encadrée par des délimiteurs (slash). Il faut ensuite utiliser ce regex pour effectuer des opérations diverses (extraction de contenus, modifications de données à la volée, etc.).

Pour instancier la classe dédiée, voici comment procéder :

// Méthode littérale (sans guillemets)
let regex = /motif/;

// Méthode instanciée (Objet)
let regex = new RegExp('motif');

Il faut ensuite appliquer des méthodes spécifiques à une instance pour réaliser des tâches intéressantes. Parmi elles, vous pouvez par exemple utiliser :

  • match() : permet de vérifier l’existence d’une chaîne au sein d’un texte. Si la chaîne existe, la méthode va l’extraire. Sinon, elle retourne null.
  • search() : effectue une recherche au sein d’une chaîne, et retourne la position de départ si elle existe. Si la chaîne n’existe pas, la méthode retourne -1.
  • replace() : recherche et remplace une chaîne par une autre (fournie en argument).
  • split() : permet de découper une chaîne lorsqu’un motif ou un caractère donné est repéré (par exemple, on peut utiliser cette fonction pour créer des sacs de mots ou pour récupérer tous les mots d’une requête).
  • exec() : fonction proche de match() qui permet de récupérer un tableau des correspondances, ou retourne null si rien n’est trouvé.
  • test() : analyse si une correspondance existe (retourne true) ou non (retourne false).

Ces méthodes s’appliquent tantôt sur les expressions régulières, tantôt sur les chaînes à analyser. Voici juste quelques exemples simples de fonctionnement, tout d’abord avec split() :

// Texte et expression régulière
let query = "Définition référencement web"; // Exemple de requête
let regex = /[ ]+/; // On cherche les espaces

// On découpe la requête avec split()
let decoupage = query.split(regex);

// Affichage des résultats dans la console
console.log(decoupage);

Et maintenant avec test() :
// Texte et expression régulière
let texte = "Bonjour je m'appelle Mathieu, et vous ?";
let regex = /Mathieu/; // On cherche le prénom

// On évalue la correspondance entre le regex et la chaine
if(regex.test(texte)) {
// Affichage du résultat positif dans la console
console.log("Mathieu est bien présent dans la chaîne");
} else {
// Affichage du résultat négatif dans la console
console.log("Ce prénom n'est pas présent, désolé");
}

Regex et PHP

PHP permet également d’utiliser les expressions régulières sous toutes ses formes, à l’instar de JavaScript. Les développeurs ont plus l’habitude de passer par lui pour la création d’outils avancées, car certaines de ses fonctions spécifiques aux expressions régulières sont très efficaces (en réalité, celles de JavaScript le sont tout autant, cela semble plus être une question d’habitude que de réelles différences de performances).

Dans ce langage, les fonctions d’usage des regex sont toutes préfixées par preg_.

  • preg_match($regex, $chaine, $resultats) : recherche de correspondance dans une chaine. Retourne 0 ou false si aucune correspondance n’est trouvée. La variable $resultats contient un tableau des premières correspondances trouvées.
  • preg_match_all($regex, $chaine, $resultats) : équivalent de la fonction précédente, mais appliquée cette fois-ci de manière globale, c’est-à-dire à l’ensemble des occurrences correspondantes dans un contenu. C’est la fonction idéale pour faire de l’extraction de contenus…
  • preg_replace($regex, $remplacement, $chaine) : permet de remplacer une correspondance de sous-chaîne ($regex) par une chaine souhaitée ($remplacement). La fonction peut aussi accueillir un tableau de regex et de chaînes de remplacement pour effectuer plusieurs tâches en même temps.
  • preg_filter ($regex, $remplacement, $chaine) : équivalent de preg_replace() mais elle ne fait que retourner les occurrences réellement trouvées ou remplacées (donc pas les occurrences originales si les correspondances ne collent pas).
  • preg_grep($regex, $tableau) : recherche des correspondances et retourne un tableau des occurrences trouvées (ou false si aucune correspondance n’existe).
  • preg_split($regex, $chaine) : permet de découper ou d’éclater une chaîne en sous-chaîne. Ici, le motif sert de « séparateur » pour la découpe des chaînes. Comme dans l’exemple cité pour JavaScript, « [ ] » permettrait donc de découper chaque mot séparé par un espace (bien qu’il ne faille jamais oublié tous les cas de ponctuations à nettoyer également).

PHP propose la fonction preg_quote() pour générer automatiquement l’échappement des caractères problématiques dans une expression régulière. Elle peut être bien pratique par moment…

Comme pour JavaScript, les expressions régulières en PHP peuvent rendre de fiers services pour le SEO. Il est possible d’extraire des contenus, de modifier des textes à la volée (offuscation de liens par exemple), de compresser des ressources pour booster les performances web (suppression de caractères à l’aide d’expressions rationnelles précises), tester une URL et ajouter le « https:// » en début d’URL, vérifier la présence de balises d’optimisations, etc. Les cas d’usage sont multiples, en voici quelques-uns pour l’exemple :

// Vérifier la présence du protocole d'une URL
$url_exemple = "www.abondance.com";
$regex = "^https?://";
if(!preg_match($regex, $url_exemple)) {
$url_exemple = "http://".$url_exemple; // On ajoute le protocole s'il est manquant
}

// Récupérer tous les liens provenant d'une URL
$regex = '#<a href="([^"]*)">(.*)</a>#Usi';
$contenu = file_get_contents('https://www.abondance.com');
preg_match_all($regex, $contenu, $matches); // Extrait l'ensemble des liens
var_dump($matches); // Affiche les résultats

Utiliser des regex dans la Google Search Console

Les expressions régulières ne sont pas seulement utiles en programmation, elles peuvent aussi être utilisées dans bien d’autres outils ou fichiers. Couramment, on retrouve des regex dans les fichiers .htaccess ou robots.txt par exemple. Les motifs peuvent aussi être utilisés dans les éditeurs de code comme Notepad++, SublimeText ou Visual Studio Code pour rechercher des éléments avec précision. Et enfin, pléthores d’outils acceptent les regex, comme Google Analytics et l’objet de notre exemple ici, la Google Search Console…

Dans l’outil d’aide aux webmasters de Google, les expressions régulières sont très utiles dans certains rapports pour mettre en exergue des données particulières. Par exemple, cela peut servir pour afficher des types d’URL précis (fiches produits, portfolios, galeries photo/vidéo…) ou pour filtrer des requêtes d’utilisateurs, etc.

Les regex sont essentiellement utiles au sein des rapports de performances, en créant un filtre à l’aide du bouton + Nouveau. Cela fonctionne pour les requêtes et les pages uniquement mais peut déjà rendre de fiers services. Il faut sélectionner l’option Personnalisée (expression régulière).

Ajout d’une expression régulière pour filtrer les pages

 

Il est possible de créer des expressions régulières pour trouver des correspondances ou pour exclure les correspondances, ce qui laisse davantage de liberté.

Si on souhaite filtrer les pages qui contiennent une portion spécifique, il suffit d’écrire le motif adapté, sans avoir besoin de délimiteurs. C’est donc relativement plus simple qu’en JavaScript ou PHP notamment. Par exemple, dans un blog réalisé avec WordPress, les URL peuvent être de la forme « DOMAINE.EXT/ANNEE/MOIS/JOUR/TITRE », il peut être intéressant de comparer les performances des pages à des périodes équivalentes de l’année (par exemple au mois de décembre). Dans ce cas, on peut écrire « /12/ » pour ne retourner que les articles publiées le même mois sur plusieurs années.

L’intérêt est limité dans le cas d’un blog, mais bien plus intéressant dans le cas d’une boutique en ligne, qui pourrait souhaiter distinguer les fiches-produits des catégories de produits ou d’autres pages. En cas de marqueurs mensuels comme pour les blogs WordPress, on pourrait aussi imaginer la comparaison de performances du site sur plusieurs années au même mois, etc. Il est également possible de chercher des URL qui proviendraient de catégories utilisant des navigations à facettes (filtres), à l’aide de paramètres d’URL (par exemple, écrire un motif comme ?prix= pour chercher des URL contenant un paramètre de tarif).

Distinction des performances des fiches-produits

 

Enfin, les regex peuvent aussi être utiles pour repérer rapidement les URL qui utilisent des paramètres de tracking spécifique. Google Analytics propose notamment les variables UTM pour suivre des données en ajoutant des paramètres dans les adresses. La Google Search Console peut les filtrer avec un motif de la forme utm_(source|medium|campaign|content|term) par exemple.

Les expressions régulières sont également très intéressantes pour l’analyse des requêtes. Vous pouvez tester le nombre de mots d’une requête à l’envi, et ainsi dissocier les performances issues de requêtes composées d’un seul mot, de deux mots, etc. Plusieurs exemples sont possibles :

  • ^([^ ]+)$ : la requête ne doit contenir qu’un mot.
  • ^([^ ]+[ ]){X-1,}[^ ]+$ : la requête doit contenir au moins X-1 Par exemple, X-1 = 1 pour les requêtes de deux mots et plus, X-1 = 2 pour trois mots et plus, etc.
  • ^([^ ]+[ ]?){0,X}$ : la requête doit contenir au maximum X
  • ^([^ ]+[ ]){X-1,X-1}[^ ]+$ : la requête doit contenir précisément X-1 Par exemple, X-1 = 2 pour filtrer uniquement les requêtes de 3 mots.
  • ^[wW ]{X,}$ : la requête fait minimum X caractères de longueur (intéressant pour trouver les requêtes de longue traîne par exemple).
  • ^[wW ]{0,X}$ : la requête fait maximum X caractères de longueur.
  • (achat|vente|commande|pas cher|prix|tarif): exemple de requête transactionnelle.

Filtrage des requêtes de 3 mots uniquement

 

Enfin, il est possible d’utiliser des filtres de requêtes pour chercher des mots précis ou par exemple pour trouver des questions courantes. Il est complexe de définir une expression régulière pour toutes les formes de questions mais voici des possibilités :

  • ^(qui|quoi|où|ou|comment|lequel|laquelle|lesquels|que|quel|quelle|quand|pourquoi|combien)+[ ] : filtre les principales questions commençant par un mot clé significatif.
  • ^([^ ]+[-]+(il|ils|elle|elles|on))+[ ] : filtre les questions sous les formes « est-il… », « existe-t-il… », etc.
  • ^([^ ]+[ ]?)+[ ?]+$ : récupère les requêtes qui se terminent par un point d’interrogation.

Pour vous aider également, Google fournit beaucoup d’exemples d’expressions régulières qui peuvent fonctionner dans ses outils au sein de sa documentation (https://support.google.com/a/answer/1371417?hl=fr).

Quelques outils pour vous aider…

Même les plus habitués des expressions régulières ont parfois des soucis pour concevoir correctement les motifs, il n’est donc jamais exclu de devoir passer par des aides en ligne pour se parfaire, ou tout simplement des outils pour créer aisément les regex. Voici une liste (non exhaustive) de ressources en la matière :

À vous de jouer maintenant ! 😊

Mathieu Chartier, Consultant-Formateur et webmaster indépendant chez Internet-Formation (https://www.internet-formation.fr)