Parse HTML like a Pro : Maîtriser le Web Scraping avec Python et Regex

Suciu Dan le 13 avril 2023

blog-image

La quantité de données disponibles sur l'internet a augmenté au cours des dernières décennies. Les êtres humains consomment ces données à des fins très diverses, qu'il s'agisse d'intérêts personnels ou de recherches commerciales.

Cependant, si ces données ne sont pas renvoyées sous une forme formatée, telle que XML ou JSON, il peut être difficile, voire impossible, de les lire à l'aide d'applications logicielles. C'est là qu'intervient la technique du web scraping.

Le web scraping est le processus de collecte et de traitement de données brutes provenant de l'internet. Ces données sont analysées et utilisées à des fins diverses, telles que la veille tarifaire, les études de marché, l'entraînement de modèles d'intelligence artificielle, l'analyse des sentiments, les audits de marque et les audits de référencement.

L'un des principaux aspects du web scraping est l'analyse du code HTML. Cela peut être fait en utilisant une variété d'outils, tels que BeautifulSoup pour Python, Cheerio pour NodeJS, et Nokogiri pour Ruby.

Les expressions régulières (regex) sont une séquence de caractères qui définissent un motif de recherche.

Dans cet article, nous allons voir comment analyser un document HTML en utilisant des expressions rationnelles et Python. Nous discuterons également des défis et des solutions alternatives qui accompagnent le web scraping.

À la fin de l'article, vous aurez une compréhension complète du sujet et des différents outils et techniques disponibles.

Analyse de base des expressions rationnelles

La plupart des langages de programmation à usage général prennent en charge les expressions rationnelles. Vous pouvez utiliser les expressions rationnelles dans un grand nombre de langages de programmation, notamment Python, C, C++, Java, Rust, OCaml et JavaScript.

Here’s what a regex rule for extracting the value from the <title> tag looks like:

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

Effrayant, n'est-ce pas ? N'oubliez pas que ce n'est que le début. Nous descendrons bientôt dans le trou du lapin.

Pour cet article, j'utilise Python 3.11.1. Prenons cette règle et mettons-la en code. Créez un fichier appelé main.py et collez 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 en sortie est le mot "Scraping".

Dans cet exemple, nous utilisons le module `re` pour travailler avec des regex. La fonction `re.search()` recherche un motif spécifique dans une chaîne de caractères. Le premier argument est le motif de la regex, et le second est la chaîne de caractères dans laquelle nous effectuons la recherche.

The regex pattern in this example is "<title>(.*?)</title>". It consists of several parts:

  • <title>: This is a literal string, it will match the characters "<title>" exactly.
  • (.* ?): Il s'agit d'un groupe de capture, indiqué par des parenthèses. Le caractère . correspond à n'importe quel caractère (à l'exception d'une nouvelle ligne), et le quantificateur * signifie qu'il doit correspondre à 0 ou plus du caractère précédent. De plus, ? rend le * non avide, ce qui signifie qu'il s'arrête dès qu'il trouve la balise fermante.
  • </title>: This is also a literal string, it will match the characters "</title>" exactly.

La fonction re.search() renvoie un objet match 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 du titre.

Ce texte sera assigné à la variable title, et le résultat sera "Scraping".

Analyse avancée des expressions rationnelles

Extraire les données d'une seule balise HTML n'est pas très utile. Elle 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, le Python Package Index (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 regex :

([0-9,]+) projets

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 "projets". 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 de 0 à 9 et au caractère `,` ; le quantificateur + signifie qu'il faut correspondre à 1 ou plusieurs des caractères précédents.
  • projets : Il s'agit d'une chaîne littérale, qui correspondra exactement à "projets".

Il est temps de mettre la règle à l'épreuve. Mettez à jour le code de `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 exécutons la règle regex sur le contenu HTML et nous imprimons le premier groupe correspondant.

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

Extraction de liens

Maintenant que nous avons un simple scraper qui peut obtenir le document HTML d'un site, jouons un peu avec le code.

Cette règle permet d'extraire tous les liens :

href=[\N-] ?([^\N-] >]+)

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

  • href= : il s'agit d'une chaîne littérale, qui correspondra exactement aux caractères "href=".
  • [\'"]? : les crochets [] correspondent à n'importe quel caractère à l'intérieur, dans ce cas, les caractères ' ou " ; le quantificateur ? correspond à 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 d'aucun.
  • ([^\'" >]+) : 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 ',", > ou un espace ; le quantificateur + signifie correspondre à 1 ou plusieurs des caractères précédents, ce qui signifie que le groupe capturera un ou plusieurs caractères qui correspondent au motif.

Extraction d'images

Une dernière chose et nous avons presque fini d'écrire des règles regex : nous devons extraire les images. Utilisons cette règle :

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

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

  • <img: This is a literal string, it will match the characters "<img" exactly.
  • .*?: the .* match any character (except a newline) 0 or more times, and the ? quantifier means to match as few as possible of the preceding character; this is used to match any character that appears before the src attribute in the <img> tag, and it allows the pattern to match any <img> tag regardless of the number of attributes it has.
  • src=" : il s'agit d'une chaîne littérale, qui correspondra exactement aux caractères "src=".
  • (.*?): this is a capturing group, denoted by the parentheses; the .*? match any character (except a newline) 0 or more times, and the ? quantifier means to match as few as possible of the preceding character; this group captures the src value of the <img> tag.
  • " : il s'agit d'une chaîne littérale, qui correspondra exactement au caractère ".

Mettons-le à l'épreuve. 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 avec tous les liens d'images de la page Pypi.

Limites

Le web scraping à l'aide d'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.

Par exemple, considérons l'exemple de code suivant dans lequel nous essayons d'extraire le texte du h2 à l'aide d'une expression rationnelle :

<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>

Compare the first <h2> tag with the second one. You may notice the second <h2> is not properly closed, and the code has </p> instead of </h2>. Let’s update the snippet with this:

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 :

Premier sous-titre

Le texte de la deuxième balise d'en-tête est manquant. Cela est dû au fait que la règle regex ne correspond pas à la balise d'en-tête non fermée.

Une solution à ce problème est d'utiliser une bibliothèque comme BeautifulSoup, qui permet de naviguer et de rechercher dans l'arborescence HTML, plutôt que de s'appuyer sur des 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 malformées et le résultat ressemble à ceci :

Premier sous-titre

Deuxième sous-titre

Cette approche est plus robuste aux changements dans la structure HTML, car elle ne dépend pas de modèles spécifiques dans le 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 scraping web telle que WebScrapingAPI, qui fait abstraction des complexités du scraping web et vous permet d'extraire facilement les données dont vous avez besoin sans vous préoccuper de la structure HTML sous-jacente.

Avec WebScrapingAPI, vous pouvez extraire des données de n'importe quel site web par un simple appel API, et il gère automatiquement les changements dans la structure HTML.

Dernières réflexions

L'analyse des 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, comment les utiliser pour analyser du HTML, et certains des défis que vous pouvez rencontrer en les utilisant. Nous avons également vu comment des bibliothèques comme 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 fastidieuse, mais avec les bons outils, il peut être facile et efficace. Si vous êtes à la recherche d'une solution de web scraping qui vous fera gagner du temps et de l'énergie, essayez WebScrapingAPI.

Nous offrons une période d'essai gratuite de 14 jours, au cours de laquelle vous pouvez tester notre service et voir les avantages de l'utilisation d'une API de web scraping.

Nouvelles et mises à jour

Restez au courant des derniers guides et nouvelles sur le web scraping en vous inscrivant à notre lettre d'information.

We care about the protection of your data. Read our <l>Privacy Policy</l>.Privacy Policy.

Articles connexes

vignette
Cas d'utilisationLibérer la puissance des données financières : Explorer les données traditionnelles et alternatives

Plongez dans le rôle transformateur des données financières dans la prise de décision des entreprises. Comprendre les données financières traditionnelles et l'importance émergente des données alternatives.

Suciu Dan
avatar de l'auteur
Suciu Dan
8 minutes de lecture
vignette
La science du Web ScrapingLe Web Scraping en toute simplicité : l'importance de l'analyse des données

Découvrez comment extraire et organiser efficacement des données pour le web scraping et l'analyse de données grâce à l'analyse de données, aux bibliothèques d'analyse HTML et aux métadonnées schema.org.

Suciu Dan
avatar de l'auteur
Suciu Dan
12 minutes de lecture
vignette
Cas d'utilisationXPath et les sélecteurs CSS

Les sélecteurs XPath sont-ils meilleurs que les sélecteurs CSS pour le web scraping ? Découvrez les points forts et les limites de chaque méthode et faites le bon choix pour votre projet !

Mihai Maxim
avatar de l'auteur
Mihai Maxim
8 minutes de lecture