Reading XML python : exemples concrets pour automatiser vos rapports

Le module xml.etree.ElementTree fait partie de la bibliothèque standard Python. Il représente un document XML sous forme d’arbre en mémoire, où chaque nœud correspond à un élément balisé. Cette structure permet de lire, filtrer et transformer des données XML en quelques lignes de code, sans installer de dépendance externe.

Charger un fichier XML avec ElementTree et extraire la racine

Avant de filtrer ou transformer quoi que ce soit, le fichier XML doit être chargé en mémoire sous forme d’arbre. La fonction parse() du module xml.etree.ElementTree prend en paramètre le chemin du fichier et renvoie un objet ElementTree. La méthode getroot() donne accès à l’élément racine, point d’entrée de toute navigation.

Voici le squelette minimal :

import xml.etree.ElementTree as ET
tree = ET.parse('rapport.xml')
root = tree.getroot()

À partir de root, chaque élément expose trois propriétés utiles : tag (le nom de la balise), attrib (un dictionnaire des attributs) et text (le contenu textuel entre les balises ouvrante et fermante). Toute la logique d’extraction repose sur ces trois accès.

Pour un fichier XML volumineux qui ne tient pas confortablement en mémoire, la méthode iterparse() traite le document nœud par nœud, en mode flux. Elle évite de charger l’arbre complet et réduit la consommation mémoire de façon significative.

Filtrer des éléments XML avec findall et XPath en Python

Développeuse analysant un script Python de lecture XML sur un MacBook dans un espace de coworking en briques apparentes

Une fois la racine obtenue, la méthode findall() permet de sélectionner tous les éléments correspondant à un chemin. Ce chemin suit une syntaxe XPath simplifiée propre à ElementTree.

Prenons un fichier XML de rapport contenant des sections <entry> imbriquées dans <report> :

entries = root.findall('.//entry')
for entry in entries:
    print(entry.find('title').text, entry.attrib.get('date'))

findall('.//entry') parcourt l’arbre en profondeur et renvoie une liste de tous les éléments entry, quel que soit leur niveau d’imbrication. find(), au singulier, renvoie le premier résultat ou None.

Les expressions XPath supportées par ElementTree restent basiques : chemins relatifs, wildcards (*), prédicats d’index ([1]). Pour des requêtes XPath complètes (axes, fonctions, prédicats complexes), la bibliothèque lxml est nécessaire. Elle expose la même API qu’ElementTree tout en acceptant la syntaxe XPath 1.0 intégrale.

Précaution sur les prédicats d’index avec ElementTree

Une vulnérabilité référencée sous CVE-2026-6879 concerne le composant xml.etree.ElementPath. Les requêtes find(), findall() ou iterfind() utilisant des prédicats d’index ([1], [last()], [last()-N]) peuvent provoquer une consommation CPU quadratique sur des documents comportant un grand nombre d’éléments frères avec le même tag. Des correctifs sont annoncés pour les versions Python 3.11.6 et 3.12.6.

Si le script lit des fichiers XML provenant de sources externes (flux API, fichiers uploadés), cette faille mérite attention. Limiter la taille des documents acceptés ou migrer les requêtes critiques vers lxml réduit le risque.

Générer un rapport CSV à partir de données XML avec Python

Lire du XML ne sert à rien sans transformation. Le cas d’usage le plus fréquent en automatisation de rapports consiste à extraire des données structurées d’un fichier XML pour les écrire dans un format tabulaire exploitable (CSV, base de données, DataFrame).

Voici un script complet qui lit un fichier XML contenant des entrées de rapport et produit un fichier CSV :

import xml.etree.ElementTree as ET
import csv
tree = ET.parse('rapport.xml')
root = tree.getroot()
with open('sortie.csv', 'w', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(['titre', 'date', 'valeur'])
    for entry in root.findall('.//entry'):
        titre = entry.find('title').text
        date = entry.attrib.get('date', '')
        valeur = entry.find('value').text
        writer.writerow([titre, date, valeur])

Chaque entrée XML devient une ligne CSV exploitable dans un tableur ou un pipeline de données. Le script tient en une quinzaine de lignes et ne nécessite aucune bibliothèque tierce.

Vue aérienne d'un bureau avec rapport XML imprimé, notes Python manuscrites et graphique généré automatiquement sur tablette

Choisir entre ElementTree et lxml pour lire du XML en Python

Le choix de la bibliothèque dépend de trois critères opérationnels : la taille du fichier XML, la complexité des namespaces et le niveau de confiance dans la source.

  • ElementTree convient aux fichiers petits à moyens qui tiennent intégralement en mémoire, sans namespaces complexes. Il fait partie de la bibliothèque standard, donc aucune installation supplémentaire n’est requise. C’est le choix par défaut pour des fichiers de configuration ou des rapports internes.
  • lxml est préférable pour des fichiers de taille moyenne à grande, avec des namespaces imbriqués ou des besoins intensifs en XPath. Il accepte la validation par schéma XSD et offre des performances supérieures sur les documents volumineux grâce à son binding C (libxml2).
  • iterparse (ElementTree ou lxml) s’impose quand le fichier XML dépasse la mémoire disponible. Le traitement se fait nœud par nœud, ce qui permet de lire des fichiers de plusieurs centaines de mégaoctets sans saturer la RAM.

Pour un pipeline d’automatisation de rapports qui lit des fichiers XML générés en interne, ElementTree suffit dans la majorité des cas. Dès que le XML provient d’une API externe avec des namespaces ou que la validation XSD est requise, lxml devient le choix naturel.

Gérer les namespaces XML dans un script Python

Les namespaces sont la source d’erreur la plus fréquente quand un script Python lit du XML externe. Un élément qui semble s’appeler entry dans le fichier porte en réalité un nom qualifié comme {http://example.com/ns}entry. Sans prise en compte du namespace, findall('entry') ne renvoie rien.

Avec ElementTree, la solution consiste à passer un dictionnaire de namespaces :

ns = {'rpt': 'http://example.com/ns'}
entries = root.findall('.//rpt:entry', ns)

Le préfixe utilisé dans le dictionnaire Python n’a pas besoin de correspondre au préfixe du fichier XML. Seule l’URI du namespace doit être identique. Cette distinction évite des heures de débogage sur des scripts qui fonctionnent avec un fichier local mais échouent silencieusement sur un flux API.

Avec lxml, la gestion reste similaire, mais la méthode nsmap de chaque élément permet de récupérer automatiquement les namespaces déclarés dans le document, ce qui simplifie l’écriture de scripts génériques.

L’automatisation de rapports XML en Python repose sur un nombre réduit de mécanismes : charger l’arbre, naviguer avec find/findall, extraire tag, attrib et text, puis écrire le résultat. La difficulté réelle se situe rarement dans le code, mais dans la structure du XML source, ses namespaces et la confiance que l’on accorde à son contenu.