Retour au blog
Guides
Suciu Dan13 avril 20238 min de lecture

Analyser du HTML comme un pro : maîtriser le web scraping avec Python et les expressions régulières

Analyser du HTML comme un pro : maîtriser le web scraping avec Python et les expressions régulières

Analyse de base des expressions régulières

La plupart des langages de programmation polyvalents prennent en charge les regex. Vous pouvez utiliser les regex dans une grande variété de langages de programmation, notamment Python, C, C++, Java, Rust, OCaml et JavaScript.

Voici à quoi ressemble une règle d'expression régulière permettant d'extraire la valeur de la balise <title> :

<title>(.*?)</title>

Effrayant, n'est-ce pas ? Gardez à l'esprit que ce n'est que le début. Nous allons bientôt nous plonger dans le vif du sujet.

Pour cet article, j’utilise Python 3.11.1. Prenons cette règle et transposons-la en code. Créez un fichier nommé main.py et collez-y cet extrait :

import re

html = "<html><head><title>Scraping</title></head></html>"

title_search = re.search("<title>(.*?)</title>", html)

title = title_search.group(1)

print(title)

Vous pouvez exécuter ce code en lançant la commande `python main.py`. Le résultat que vous verrez s'afficher est le mot « Scraping ».

Dans cet exemple, nous utilisons le module `re` pour travailler avec les expressions régulières. La fonction `re.search()` recherche un motif spécifique dans une chaîne de caractères. Le premier argument est le motif d'expression régulière, et le deuxième argument est la chaîne dans laquelle nous effectuons la recherche.

Le motif d'expression régulière dans cet exemple est « <title>(.*?)</title> ». Il se compose de plusieurs parties :

  • <title> : il s'agit d'une chaîne littérale, qui correspondra exactement aux caractères « <title> ».
  • (.*?): Il s'agit d'un groupe de capture, indiqué par des parenthèses. Le caractère . correspond à n'importe quel caractère unique (à l'exception d'un saut de ligne), et le quantificateur * signifie qu'il faut trouver 0 ou plusieurs occurrences du caractère précédent. De plus, le ? rend le * non-gourmand, ce qui signifie qu'il s'arrêtera dès qu'il trouvera la balise de fermeture.
  • </title> : Il s'agit également d'une chaîne littérale, qui correspondra exactement aux caractères « </title> ».

La fonction re.search() renvoie un objet de correspondance si une correspondance est trouvée, et la méthode group(1) est utilisée pour extraire le texte correspondant au premier groupe de capture, c'est-à-dire le texte situé entre les balises d'ouverture et de fermeture title.

Ce texte sera assigné à la variable title, et la sortie sera « Scraping ».

Analyse avancée des expressions régulières

Extraire les données d’une seule balise HTML n’est pas très utile. Cela vous donne un aperçu de ce que vous pouvez faire avec les expressions régulières, mais vous ne pouvez pas l’utiliser dans une situation réelle.

Consultons le site web PyPI, l'index des paquets Python. Sur la page d'accueil, quatre statistiques sont affichées : le nombre de projets, le nombre de versions, le nombre de fichiers et le nombre d'utilisateurs.

Nous voulons extraire le nombre de projets. Pour ce faire, nous pouvons utiliser cette expression régulière :

([0-9,]+) projects

L'expression régulière correspondra à toute chaîne commençant par un ou plusieurs chiffres, éventuellement séparés par des virgules, et se terminant par le mot « projects ». Voici comment cela fonctionne :

  • ([0-9,]+): Il s'agit d'un groupe de capture, indiqué par les parenthèses ; les crochets [0-9,] correspondent à n'importe quel chiffre compris entre 0 et 9 et au caractère `,` ; le quantificateur + signifie qu'il faut trouver au moins un des caractères précédents.
  • projects : il s'agit d'une chaîne littérale, elle correspondra exactement à « projects ».

Il est temps de tester cette règle. Mettez à jour le code `main.py` avec cet extrait :

import urllib.request

import re

response = urllib.request.urlopen("https://pypi.org/")

html = response.read().decode("utf-8")

matches = re.search("([0-9,]+) projects", html)

projects = matches.group(1)

print(projects)

Nous utilisons la méthode urlopen de la bibliothèque urllib pour effectuer une requête GET vers le site web pypi.org. Nous lisons la réponse dans la variable html. Nous appliquons la règle d'expression régulière au contenu HTML et nous affichons le premier groupe correspondant.

Exécutez le code avec la commande `python main.py` et vérifiez le résultat : il affichera le nombre de projets du site.

Extraction de liens

Maintenant que nous disposons d'un simple scraper capable de récupérer le document HTML d'un site, jouons un peu avec le code.

Nous pouvons extraire tous les liens à l'aide de cette règle :

href=[\'"]?([^\'" >]+)

Cette expression régulière se compose de plusieurs parties :

  • href= : il s'agit d'une chaîne littérale, elle correspondra exactement aux caractères « href= ».
  • [\'"]?: les crochets [] correspondent à n'importe quel caractère unique qu'ils contiennent, dans ce cas, les caractères ' ou " ; le quantificateur ? signifie qu'il faut trouver zéro ou un des caractères précédents, ce qui signifie que la valeur href peut être entourée de " ou de ', ou ne pas l'être du tout.
  • ([^\'" >]+): il s'agit d'un groupe de capture, indiqué par les parenthèses ; le ^ à l'intérieur des crochets signifie la négation, il correspondra à tout caractère qui n'est pas un ', ", > ou un espace ; le quantificateur + signifie qu'il faut trouver 1 ou plusieurs des caractères précédents, ce qui signifie que le groupe capturera un ou plusieurs caractères correspondant au motif.

Extraction des images

Encore une chose et nous aurons presque terminé la rédaction des règles d'expressions régulières : nous devons extraire les images. Utilisons cette règle :

<img.*?src="(.*?)"

Cette expression régulière se compose de plusieurs parties :

  • <img : il s'agit d'une chaîne littérale, elle correspondra exactement aux caractères « <img ».
  • .*?: le .* correspond à n'importe quel caractère (à l'exception d'un saut de ligne) zéro ou plusieurs fois, et le quantificateur ? signifie qu'il faut faire correspondre le moins de caractères précédents possible ; cela sert à faire correspondre n'importe quel caractère apparaissant avant l'attribut src dans la balise <img>, et permet au motif de correspondre à n'importe quelle balise <img>, quel que soit le nombre d'attributs qu'elle possède.
  • src=" : il s'agit d'une chaîne littérale, elle correspondra exactement aux caractères « src= ».
  • (.*?): il s'agit d'un groupe de capture, indiqué par les parenthèses ; le .*? correspond à n'importe quel caractère (à l'exception d'un saut de ligne) 0 ou plusieurs fois, et le quantificateur ? signifie qu'il faut faire correspondre le moins possible de caractères précédents ; ce groupe capture la valeur src de la balise <img>.
  • ": il s'agit d'une chaîne littérale, elle correspondra exactement au caractère "".

Testons cela. Remplacez l'extrait de code précédent par celui-ci :

import urllib.request

import re

response = urllib.request.urlopen("https://pypi.org/")

html = response.read().decode("utf-8")

images = re.findall('<img.*?src="(.*?)"', html)

print(*images, sep = "\n")

La sortie de ce code affichera une liste contenant tous les liens vers les images de la page Pypi.

Limites

Le web scraping avec des expressions régulières peut être un outil puissant pour extraire des données de sites web, mais il a aussi ses limites. L'un des principaux problèmes liés à l'utilisation des expressions régulières pour le web scraping est qu'elles peuvent échouer lorsque la structure du code HTML change.

Prenons par exemple l'extrait de code suivant, dans lequel nous essayons d'extraire le texte de la balise h2 à l'aide d'expressions régulières :

<html>

   <head>

       <title>Example Title</title>

   </head>

   <body>

       <h1>Page Title</h1>

       <p>This is a paragraph under the title</p>

       <h2>First Subtitle</h2>

       <p>First paragraph under the subtitle</p>

       <h2>Second Subtitle</p>

   </body>

</html>

Comparez la première balise <h2> avec la seconde. Vous remarquerez peut-être que la seconde balise <h2> n'est pas correctement fermée, et que le code contient </p> au lieu de </h2>. Modifions l'extrait de code comme suit :

import re

html = "<html><head><title>Example Title</title></head><body><h1>Page Title</h1><p>This is a paragraph under the title</p><h2>First Subtitle</h2><p>First paragraph under the subtitle</p><h2>Second Subtitle</p></body></html>"

headingTags = re.findall("<h2>(.*?)</h2>", html)

print(*headingTags, sep = "\n")

Exécutons le code et vérifions le résultat :

First Subtitle

Le texte de la deuxième balise de titre est manquant. Cela se produit parce que la règle d'expression régulière ne correspond pas à la balise de titre non fermée.

Une solution à ce problème consiste à utiliser une bibliothèque telle que BeautifulSoup, qui vous permet de naviguer et d'effectuer des recherches dans la structure arborescente HTML, plutôt que de vous fier aux expressions régulières. Avec BeautifulSoup, vous pouvez extraire le titre d'une page web comme ceci :

from bs4 import BeautifulSoup

html = "<html><head><title>Example Title</title></head><body><h1>Page Title</h1><p>This is a paragraph under the title</p><h2>First Subtitle</h2><p>First paragraph under the subtitle</p><h2>Second Subtitle</p></body></html>"

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

for headingTag in soup.findAll('h2'):

   print(headingTag.text)

BeautifulSoup parvient à extraire les balises mal formées et le résultat ressemble à ceci :

First Subtitle

Second Subtitle

Cette approche est plus robuste face aux changements dans la structure HTML, car elle ne repose pas sur des motifs spécifiques du code HTML. Si vous souhaitez en savoir plus sur BeautifulSoup, cet article est une lecture idéale.

Une autre solution consiste à utiliser une API de web scraping telle que WebScrapingAPI, qui fait abstraction des complexités du web scraping et vous permet d'extraire facilement les données dont vous avez besoin sans vous soucier de la structure HTML sous-jacente.

Avec WebScrapingAPI, vous pouvez extraire des données de n'importe quel site web à l'aide d'un simple appel d'API, et l'API gère automatiquement les modifications de la structure HTML.

Conclusion

L'analyse de données à l'aide d'expressions régulières peut être un outil puissant pour extraire des données de sites web.

Dans cet article, nous avons abordé les bases des expressions régulières, leur utilisation pour analyser le HTML, ainsi que certains des défis que vous pourriez rencontrer lors de leur utilisation. Nous avons également vu comment des bibliothèques telles que BeautifulSoup peuvent être utilisées comme solution alternative.

Vous avez appris à extraire des données de pages web à l'aide d'expressions régulières, et à améliorer la fiabilité de votre code en utilisant une bibliothèque plus robuste telle que BeautifulSoup.

Le web scraping peut être une tâche chronophage, mais avec les bons outils, cela peut être simple et efficace. Si vous recherchez une solution de web scraping qui vous fera gagner du temps et vous épargnera des efforts, essayez WebScrapingAPI.

Nous proposons un essai gratuit de 14 jours, pendant lequel vous pourrez tester notre service et découvrir les avantages d'une API de web scraping.

À propos de l'auteur
Suciu Dan, cofondateur @ WebScrapingAPI
Suciu Dancofondateur

Suciu Dan est le cofondateur de WebScrapingAPI et rédige des guides pratiques destinés aux développeurs sur le web scraping avec Python et Ruby, ainsi que sur les infrastructures de proxy.

Commencez à créer

Prêt à faire évoluer votre système de collecte de données ?

Rejoignez plus de 2 000 entreprises qui utilisent WebScrapingAPI pour extraire des données Web à l'échelle de l'entreprise, sans aucun coût d'infrastructure.