Le parsing XML en contexte de web scraping ne se résume pas à charger un fichier bien formé avec xml.etree.ElementTree. Les documents récupérés en production (sitemaps, flux RSS, réponses d’API tierces) contiennent régulièrement des erreurs d’encodage, des entités non déclarées ou des balises mal fermées. Nous partons ici du cas réel, pas du cas académique.
Parsing XML en mode recover : gérer les documents invalides du web
ElementTree, inclus dans la bibliothèque standard Python, lève une exception dès qu’il rencontre un XML mal formé. Sur un sitemap généré par un CMS mal configuré ou un flux RSS encodé en Latin-1 alors que le header déclare UTF-8, le script plante sans produire aucune donnée.
La solution terrain est lxml avec l’option recover=True. Ce mode indique au parseur libxml2 sous-jacent de tenter de récupérer le maximum de contenu malgré les erreurs. En pratique, cela donne un arbre exploitable là où ElementTree abandonne.
L’initialisation ressemble à ceci : créer un objet etree.XMLParser(recover=True, encoding="utf-8"), puis passer le contenu brut de la réponse HTTP à etree.fromstring(). Les erreurs rencontrées sont stockées dans parser.error_log, ce qui permet un audit post-traitement sans interrompre l’extraction.
Beautiful Soup 4 constitue une alternative quand le document mélange XML et HTML (cas fréquent sur certains flux). En spécifiant le parseur lxml-xml, Beautiful Soup applique une tolérance similaire tout en offrant une API de navigation par méthodes find et find_all familière aux développeurs habitués au scraping HTML.

Extraction ciblée avec XPath sur des sitemaps volumineux
Un sitemap index peut référencer plusieurs dizaines de sitemaps enfants, chacun contenant des milliers d’URL. Parcourir cette arborescence sans filtrage revient à télécharger et parser des fichiers pour rien. Le filtrage par lastmod est plus fiable que priority ou changefreq, ces deux derniers champs étant largement ignorés par les moteurs de recherche et souvent remplis de valeurs par défaut.
Avec lxml, une expression XPath permet d’extraire directement les éléments pertinents. La difficulté vient des namespaces : le protocole sitemap déclare un namespace par défaut (http://www.sitemaps.org/schemas/sitemap/0.9). Sans le mapper explicitement dans un dictionnaire passé à la méthode xpath(), aucun nœud n’est retourné.
Nous recommandons cette séquence pour un crawl piloté par sitemap :
- Récupérer le
robots.txtdu domaine cible pour localiser les déclarationsSitemap:, plutôt que de deviner l’URL du sitemap - Parser le sitemap index, extraire les URL des sitemaps enfants, puis itérer récursivement (certains sites imbriquent deux ou trois niveaux d’index)
- Filtrer les éléments
<url>dont la valeur<lastmod>est postérieure à une date seuil, en ignorant<priority>et<changefreq> - Collecter les
<loc>restantes dans une liste d’URL à scraper, en dédupliquant au passage
Cette approche réduit considérablement le volume de requêtes et limite les risques de blocage par le serveur cible.
Namespaces XML et pièges courants en scraping Python
La majorité des erreurs de parsing XML en scraping proviennent d’une mauvaise gestion des namespaces. Quand un élément appartient à un namespace, son tag interne dans lxml prend la forme {http://...}loc. Chercher simplement "loc" avec findall() ne retourne rien.
Deux stratégies fonctionnent. La première : passer un dictionnaire de namespaces à chaque appel findall ou xpath. La seconde, plus expéditive : utiliser une recherche par wildcard {*}loc (syntaxe supportée depuis Python 3.8 dans ElementTree, et nativement dans lxml). La wildcard {*} simplifie le code mais masque les conflits de namespace quand le document en déclare plusieurs.
Un autre piège fréquent concerne les entités HTML dans du XML. Un flux RSS contenant ou — sans déclaration DTD provoque une erreur de parsing. Avec lxml en mode recover, ces entités sont silencieusement ignorées. Avec ElementTree, il faut pré-traiter le texte brut par un remplacement regex avant le parsing.

lxml, ElementTree ou Beautiful Soup : quel parseur XML pour quel usage
Le choix du parseur dépend du type de document et du niveau de tolérance aux erreurs requis.
| Critère | ElementTree | lxml | Beautiful Soup (lxml-xml) |
|---|---|---|---|
| Installation | Inclus (stdlib) | pip install lxml | pip install beautifulsoup4 lxml |
| XML invalide | Échec immédiat | recover=True | Tolérant par défaut |
| Support XPath | Sous-ensemble limité | XPath 1.0 complet | Non (find/select) |
| Performance gros fichiers | Correcte (iterparse) | Rapide (C binding) | Plus lent (surcouche) |
| Cas d’usage scraping | Prototypage rapide | Sitemaps, flux, API XML | Documents mixtes HTML/XML |
Pour du scraping en production, lxml couvre la grande majorité des besoins : il gère les gros fichiers via iterparse, supporte XPath complet et tolère les erreurs. ElementTree reste pertinent pour des scripts ponctuels sur des fichiers XML garantis valides. Beautiful Soup s’impose quand le contenu oscille entre HTML et XML, typiquement des pages qui embarquent des fragments RSS ou des données structurées en XML dans du HTML.
Bonnes pratiques pour un pipeline XML scraping robuste
Un pipeline de scraping XML fiable ne se limite pas au choix du parseur. Le transport HTTP conditionne la qualité du document reçu. Envoyer un header Accept: application/xml oriente certains serveurs vers une réponse XML plutôt que HTML. Lire l’encodage déclaré dans le header Content-Type de la réponse avant de décoder le body évite les erreurs de caractères.
- Toujours vérifier le code HTTP et le Content-Type avant de parser : un 200 renvoyant du HTML (page de captcha, redirection soft) est un cas courant
- Utiliser
response.content(bytes) plutôt queresponse.text(str) pour passer le flux brut au parseur, qui gère lui-même l’encodage - Implémenter un retry avec backoff exponentiel pour les sitemaps volumineux, car les serveurs peuvent couper la connexion à mi-transfert
Logger chaque erreur de parsing avec l’URL source permet de distinguer un problème de réseau d’un défaut structurel du XML distant. Sur un crawl de plusieurs milliers d’URL, cette traçabilité fait la différence entre un pipeline maintenable et une boîte noire.

