Technologies et protections 6 min de lecture

Parser le DOM : analyse détaillée

Qu'est-ce qu'un parser DOM et comment fonctionne-t-il : construction de l'arbre du document, navigation entre les nœuds et différences avec les approches en flux et par regex.

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

Qu'est-ce que le DOM

Le DOM (Document Object Model) est la représentation d'un document HTML ou XML sous forme d'arbre d'objets. Chaque balise, attribut et fragment de texte devient un nœud (node), et l'imbrication des balises définit la hiérarchie « parent → enfant ».

Un parser DOM est un composant qui lit le balisage (une chaîne de caractères ou un flux d'octets) et en construit cet arbre en mémoire. Une fois l'arbre construit, on peut le manipuler par programmation : rechercher des éléments, lire des attributs, modifier, supprimer et ajouter des nœuds, ou le sérialiser à nouveau en texte.

Comment il fonctionne

Le parsing se déroule en deux étapes principales :

  1. Tokenisation — le flux de caractères est découpé en tokens (balise ouvrante, balise fermante, texte, commentaire, etc.).
  2. Construction de l'arbre — les tokens sont assemblés en une structure hiérarchique selon les règles d'imbrication. Pour le HTML, il s'agit des règles de la spécification WHATWG, qui « réparent » notamment le balisage incorrect (balises non fermées, imbrication invalide).

Parsing du DOM et parsing SAX

Il existe deux approches fondamentalement différentes pour analyser un document.

Approche Mémoire Accès Quand l'utiliser
DOM Tout le document en mémoire Accès aléatoire à n'importe quel nœud Web scraping, traitement de fichiers de configuration, la plupart des tâches
SAX / en flux Consommation quasi nulle Séquentiel uniquement, par événements Très gros XML (centaines de Mo à plusieurs Go)

DOM charge le document en entier et offre un accès aléatoire pratique, mais nécessite une quantité de mémoire proportionnelle à la taille du document.

SAX ne construit pas d'arbre : il génère des événements (« balise ouverte », « balise fermée », « texte rencontré ») au fil de la lecture. La mémoire consommée est minime, mais le code est plus complexe à écrire et il n'y a pas d'accès aléatoire.

La plupart des bibliothèques populaires présentées ci-dessous sont des parseurs DOM.

Méthodes de recherche dans l'arbre

Une fois l'arbre construit, on recherche généralement les éléments de deux manières.

Les sélecteurs CSS — les mêmes qu'en intégration web : div.post > a, #main .title, ul li:first-child. Familiers aux développeurs front-end, très lisibles.

XPath — un langage de requête sur l'arbre, plus puissant que CSS : //div[@class="post"]/a/@href. Il prend en charge les conditions, la remontée vers le parent (..), la recherche par texte (//a[contains(text(),"Acheter")]). Plus courant en XML, mais de nombreuses bibliothèques HTML le supportent également.

Règle de choix : si CSS suffit, utilisez CSS, c'est plus lisible. XPath devient nécessaire quand il faut naviguer vers un parent/ancêtre, sélectionner par texte ou exprimer des conditions complexes.


Exemples dans différents langages

Tous les exemples résolvent la même tâche : trouver les liens à l'intérieur de div.post et afficher leur texte et leur href.

Python

Les bibliothèques les plus populaires sont BeautifulSoup (simple, tolérante) et lxml (rapide, basée sur la bibliothèque C libxml2, avec prise en charge de XPath).

BeautifulSoup

python
from bs4 import BeautifulSoup

html = """
<html><body>
  <div class="post">
    <h2>Titre</h2>
    <a href="https://example.com">Lien</a>
  </div>
</body></html>
"""

soup = BeautifulSoup(html, "lxml")   # le parser peut être remplacé par "html.parser"

# Recherche par balise
print(soup.find("h2").text)

# Sélecteurs CSS
for a in soup.select("div.post a"):
    print(a.text, a.get("href"))

lxml avec XPath

python
from lxml import html as lxml_html

tree = lxml_html.fromstring(html)
hrefs = tree.xpath('//div[@class="post"]/a/@href')
print(hrefs)  # ['https://example.com']

La bibliothèque standard de Python inclut également html.parser et xml.dom.minidom, qui ne nécessitent aucune installation.

JavaScript / Node.js

Dans le navigateur, le parser DOM est intégré — DOMParser et querySelector :

javascript
const parser = new DOMParser();
const doc = parser.parseFromString(html, "text/html");

doc.querySelectorAll("div.post a").forEach(a => {
  console.log(a.textContent, a.href);
});

Sous Node.js, on utilise Cheerio (léger, API de type jQuery) ou jsdom (émulation complète du DOM).

javascript
import * as cheerio from "cheerio";

const $ = cheerio.load(html);
$("div.post a").each((i, el) => {
  console.log($(el).text(), $(el).attr("href"));
});

jsdom

javascript
import { JSDOM } from "jsdom";

const dom = new JSDOM(html);
dom.window.document.querySelectorAll("div.post a")
  .forEach(a => console.log(a.textContent, a.href));

Java

Le standard de facto est jsoup, très pratique pour le HTML avec des sélecteurs CSS.

java
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;

Document doc = Jsoup.parse(html);
for (Element link : doc.select("div.post a")) {
    System.out.println(link.text() + " " + link.attr("href"));
}

Pour du XML strict, le JDK fournit JAXP en standard (javax.xml.parsers.DocumentBuilder) — un parser DOM prêt à l'emploi, ainsi que SAX/StAX pour l'analyse en flux.

PHP

La classe intégrée DOMDocument avec prise en charge de XPath

php
$dom = new DOMDocument();
@$dom->loadHTML($html);          // @ supprime les avertissements liés au HTML « sale »
$xpath = new DOMXPath($dom);

foreach ($xpath->query('//div[@class="post"]/a') as $node) {
    echo $node->textContent . ' ' . $node->getAttribute('href') . PHP_EOL;
}

Une alternative avec sélecteurs CSS — Symfony DomCrawler

C# / .NET

HtmlAgilityPack — un classique, fonctionne via XPath

c#
var doc = new HtmlAgilityPack.HtmlDocument();
doc.LoadHtml(html);

foreach (var link in doc.DocumentNode.SelectNodes("//div[@class='post']/a"))
{
    Console.WriteLine(link.InnerText + " " + link.GetAttributeValue("href", ""));
}

AngleSharp — plus moderne, avec un parsing conforme à la spécification et des sélecteurs CSS

c#
var context = AngleSharp.BrowsingContext.New(AngleSharp.Configuration.Default);
var doc = await context.OpenAsync(req => req.Content(html));

foreach (var a in doc.QuerySelectorAll("div.post a"))
    Console.WriteLine(a.TextContent + " " + a.GetAttribute("href"));

Go

goquery offre une API de type jQuery

go
import (
    "fmt"
    "strings"
    "github.com/PuerkitoBio/goquery"
)

doc, _ := goquery.NewDocumentFromReader(strings.NewReader(html))
doc.Find("div.post a").Each(func(i int, s *goquery.Selection) {
    href, _ := s.Attr("href")
    fmt.Println(s.Text(), href)
})

Sous le capot, goquery s'appuie sur le paquet officiel de bas niveau golang.org/x/net/html, que l'on peut aussi utiliser directement.

Ruby

Nokogiri — prend en charge à la fois CSS et XPath

ruby
require "nokogiri"

doc = Nokogiri::HTML(html)
doc.css("div.post a").each do |link|
  puts "#{link.text} #{link['href']}"
end

Tableau récapitulatif des bibliothèques

Langage Bibliothèque Sélecteurs Particularités
Python BeautifulSoup CSS Simple, tolère les erreurs de balisage
Python lxml CSS + XPath Rapide (backend C libxml2)
JS (navigateur) DOMParser CSS Intégré au navigateur
Node.js Cheerio CSS Légère, de type jQuery
Node.js jsdom CSS Émulation complète du DOM
Java jsoup CSS Standard de facto pour le HTML
PHP DOMDocument XPath Intégrée à PHP
C# HtmlAgilityPack XPath Classique de l'écosystème .NET
C# AngleSharp CSS Moderne, parsing conforme à la spécification
Go goquery CSS De type jQuery
Ruby Nokogiri CSS + XPath Rapide (backend C)

Remarques pratiques

Le HTML est « sale » par nature. Le balisage réel contient souvent des balises non fermées et des erreurs d'imbrication. Les parseurs orientés HTML (BeautifulSoup, jsoup, AngleSharp, Nokogiri en mode HTML) les tolèrent et les corrigent. Les parseurs XML stricts échoueront sur une telle entrée — le XML exige un balisage valide.

Un parser n'est pas un navigateur. Les bibliothèques listées analysent uniquement le HTML source renvoyé par le serveur. Si le contenu est chargé par JavaScript dans le navigateur, un simple parser DOM ne le verra pas. Pour ces sites, il faut des navigateurs headless :

Ils exécutent d'abord les scripts, puis renvoient le DOM final.

Performance. Les parseurs écrits en C (lxml, backend libxml2 de Nokogiri) sont d'un ordre de grandeur plus rapides que les implémentations en pur Python/Ruby. Pour les XML volumineux, préférez le SAX/StAX en flux au DOM afin de ne pas saturer la mémoire.

Choix des sélecteurs. Si CSS suffit, utilisez CSS, c'est plus lisible. XPath est nécessaire pour naviguer vers un parent/ancêtre, sélectionner par texte ou exprimer des conditions complexes.