Skip to content

Traitement XML en Python

XML (eXtensible Markup Language) est un langage de balisage conçu pour stocker et transporter des données dans un format à la fois lisible par l’homme et lisible par la machine. Il est largement utilisé dans les fichiers de configuration, l’échange de données et les formats de documents.

XML définit un ensemble de règles pour l’encodage des documents. Il utilise des balises (comme le HTML) pour définir des éléments et des attributs pour fournir des métadonnées sur ces éléments. Contrairement au HTML, XML ne prédéfinit pas les balises ; les utilisateurs définissent leurs propres balises pertinentes pour leurs données.

Caractéristiques Clés :

  • Extensible : Vous définissez vos propres balises.
  • Hiérarchique : Les données sont structurées sous forme d’arbre.
  • Basé sur du texte : Utilise des caractères Unicode.
  • Auto-descriptif : Les balises décrivent souvent les données qu’elles contiennent.
  • Indépendant de la plateforme/du langage : Un standard ouvert.

Bien que JSON soit devenu plus populaire pour les API web en raison de sa simplicité et de sa correspondance directe avec les objets JavaScript, XML reste important pour de nombreux systèmes d’entreprise, les standards de documents (comme DocBook, Office Open XML) et la configuration.

La bibliothèque standard de Python fournit plusieurs modules pour travailler avec XML, prenant en charge différentes stratégies d’analyse :

  • xml.etree.ElementTree : (Recommandé pour la plupart des tâches courantes) Fournit un moyen simple et “Pythonique” d’analyser et de créer du XML. Il charge l’intégralité du document dans une structure arborescente en mémoire (similaire au DOM) mais offre une API plus légère et souvent plus facile à utiliser.
  • xml.dom.minidom : Implémente l’API Document Object Model (DOM), un standard du W3C. Il analyse l’intégralité du document XML en un arbre d’objets en mémoire, permettant un accès et une modification complets. Peut être gourmand en mémoire pour de très gros fichiers.
  • xml.sax : Implémente le Simple API for XML (SAX), un analyseur basé sur les événements. Il lit le fichier XML séquentiellement, déclenchant des fonctions de rappel (handlers) pour des événements comme les balises de début, les balises de fin et les données de caractères. Il est efficace en mémoire car il ne charge pas le document entier, ce qui le rend adapté aux très grands fichiers ou au traitement en flux, mais peut être plus complexe à utiliser pour un accès aléatoire.

Choisir une API :

  • Pour l’analyse et la création XML générales, commencez par ElementTree.
  • Si vous avez besoin d’une conformité DOM stricte ou de modifications complexes de l’arbre, utilisez minidom.
  • Pour traiter de très gros fichiers XML avec une mémoire limitée, utilisez SAX.

Nous utiliserons le fichier XML suivant pour les exemples :

<?xml version="1.0" encoding="UTF-8"?>
<collection shelf="New Arrivals">
<movie title="Enemy Behind">
<type>War, Thriller</type>
<format>DVD</format>
<year>2003</year>
<rating>PG</rating>
<stars>10</stars>
<description>Talk about a US-Japan war</description>
</movie>
<movie title="Transformers">
<type>Anime, Science Fiction</type>
<format>DVD</format>
<year>1989</year>
<rating>R</rating>
<stars>8</stars>
<description>A scientific fiction</description>
</movie>
<movie title="Trigun">
<type>Anime, Action</type>
<format>DVD</format>
<episodes>4</episodes>
<rating>PG</rating>
<stars>10</stars>
<description>Vash the Stampede!</description>
</movie>
<movie title="Ishtar">
<type>Comedy</type>
<format>VHS</format>
<rating>PG</rating>
<stars>2</stars>
<description>Viewable boredom</description>
</movie>
</collection>

Analyse XML avec ElementTree (Recommandé)

Section titled “Analyse XML avec ElementTree (Recommandé)”

ElementTree représente le document XML comme un arbre d’objets Element.

#!/usr/bin/env python3
import xml.etree.ElementTree as ET
xml_file = 'movies.xml'
try:
tree = ET.parse(xml_file)
root = tree.getroot() # Obtenir l'élément racine ('collection')
print(f"Root element: {root.tag}, Shelf: {root.get('shelf')}")
# Iterate through 'movie' elements under the root
for movie in root.findall('movie'): # findall trouve les enfants directs
title = movie.get('title') # Obtenir la valeur de l'attribut
year_element = movie.find('year') # find obtient le premier enfant correspondant
type_element = movie.find('type')
# Le contenu textuel de l'élément est dans l'attribut .text
movie_type = type_element.text if type_element is not None else 'N/A'
year = year_element.text if year_element is not None else 'N/A'
print(f"\nMovie Title: {title}")
print(f" Type: {movie_type}")
print(f" Year: {year}")
# Accéder aux éléments potentiellement manquants en toute sécurité
episodes_element = movie.find('episodes')
if episodes_element is not None:
print(f" Episodes: {episodes_element.text}")
except ET.ParseError as e:
print(f"Erreur d'analyse XML : {e}")
except FileNotFoundError:
print(f"Erreur : Le fichier '{xml_file}' est introuvable.")

Sortie :

Root element: collection, Shelf: New Arrivals
Movie Title: Enemy Behind
Type: War, Thriller
Year: 2003
Movie Title: Transformers
Type: Anime, Science Fiction
Year: 1989
Movie Title: Trigun
Type: Anime, Action
Year: N/A
Episodes: 4
Movie Title: Ishtar
Type: Comedy
Year: N/A

Concepts Clés d’ElementTree :

  • ET.parse(source) : Analyse le XML à partir d’un chemin de fichier ou d’un objet de type fichier.
  • ET.fromstring(text) : Analyse le XML à partir d’une chaîne de caractères.
  • tree.getroot() : Renvoie l’élément racine de l’arbre analysé.
  • element.tag : Le nom de la balise de l’élément (chaîne de caractères).
  • element.attrib : Un dictionnaire des attributs de l’élément.
  • element.text : Le contenu textuel directement à l’intérieur de l’élément.
  • element.get(attr_name) : Obtient la valeur d’un attribut.
  • element.find(match) : Trouve le premier élément enfant direct correspondant au nom de balise ou au chemin.
  • element.findall(match) : Trouve tous les éléments enfants directs correspondant au nom de balise ou au chemin.
  • element.iter(tag) : Crée un itérateur sur tous les sous-éléments (directs et imbriqués) correspondant à la balise.

minidom analyse le XML en une structure d’arbre DOM standard.

#!/usr/bin/env python3
from xml.dom import minidom
import xml.parsers.expat # Souvent nécessaire pour attraper des erreurs d'analyse spécifiques
xml_file = 'movies.xml'
try:
# Analyser le fichier XML en un arbre DOM
dom_tree = minidom.parse(xml_file)
# Obtenir l'élément collection (racine)
collection = dom_tree.documentElement # L'élément racine <collection>
if collection.hasAttribute("shelf"):
print(f"Root element: <{collection.tagName}>, Shelf: {collection.getAttribute('shelf')}")
# Obtenir tous les éléments 'movie'
movies = collection.getElementsByTagName("movie")
# Afficher les détails de chaque film
for movie in movies:
print("\n***** Film *****")
if movie.hasAttribute("title"):
print(f"Title: {movie.getAttribute('title')}")
# Fonction utilitaire pour obtenir le texte du premier élément enfant
def get_element_text(parent, tag_name):
elements = parent.getElementsByTagName(tag_name)
if elements and elements[0].firstChild:
# Vérifier si firstChild existe et est un nœud de texte
if elements[0].firstChild.nodeType == elements[0].TEXT_NODE:
return elements[0].firstChild.data.strip()
return 'N/A'
print(f"Type: {get_element_text(movie, 'type')}")
print(f"Format: {get_element_text(movie, 'format')}")
print(f"Rating: {get_element_text(movie, 'rating')}")
print(f"Description: {get_element_text(movie, 'description')}")
except xml.parsers.expat.ExpatError as e:
print(f"Erreur d'analyse XML : {e}")
except FileNotFoundError:
print(f"Erreur : Le fichier '{xml_file}' est introuvable.")
except Exception as e:
print(f"Une erreur inattendue est survenue : {e}")

Sortie (similaire à ElementTree, démontre l’accès DOM) :

Root element: <collection>, Shelf: New Arrivals
***** Film *****
Title: Enemy Behind
Type: War, Thriller
Format: DVD
Rating: PG
Description: Talk about a US-Japan war
***** Film *****
Title: Transformers
Type: Anime, Science Fiction
Format: DVD
Rating: R
Description: A scientific fiction
...

Concepts Clés du DOM :

  • minidom.parse(source) : Analyse le XML.
  • dom.documentElement : Le nœud d’élément racine.
  • element.tagName : Nom de la balise de l’élément.
  • element.getAttribute(name) : Obtient la valeur de l’attribut.
  • element.hasAttribute(name) : Vérifie si l’attribut existe.
  • element.getElementsByTagName(name) : Obtient une liste de tous les éléments descendants avec le nom de balise donné.
  • node.firstChild, node.childNodes, node.parentNode : Navigation à travers l’arbre.
  • node.nodeType : Type de nœud (ELEMENT_NODE, TEXT_NODE, etc.).
  • node.data : Contenu textuel d’un TEXT_NODE.

L’analyse SAX nécessite la création d’une classe ‘Content Handler’ qui définit les méthodes à appeler lorsque l’analyseur rencontre des structures XML spécifiques (balises de début/fin, données de caractères).

#!/usr/bin/env python3
import xml.sax
# Définir la classe Content Handler
class MovieHandler(xml.sax.ContentHandler):
def __init__(self):
super().__init__()
self.current_data = "" # Stocke le nom de la balise en cours de traitement
self.type = ""
self.format = ""
self.year = ""
self.rating = ""
self.stars = ""
self.description = ""
self.episodes = "" # Ajouté pour Trigun
self.current_movie_title = ""
# Appelé lorsqu'un élément commence
def startElement(self, tag, attributes):
self.current_data = tag
if tag == "movie":
print("\n***** Film *****")
self.current_movie_title = attributes.get("title", "N/A")
print(f"Title: {self.current_movie_title}")
# Réinitialiser les champs spécifiques au film
self.type = self.format = self.year = self.rating = ""
self.stars = self.description = self.episodes = ""
# Appelé lorsqu'un élément se termine
def endElement(self, tag):
if tag == "type":
print(f" Type: {self.type}")
elif tag == "format":
print(f" Format: {self.format}")
elif tag == "year":
print(f" Year: {self.year}")
elif tag == "rating":
print(f" Rating: {self.rating}")
elif tag == "stars":
print(f" Stars: {self.stars}")
elif tag == "description":
print(f" Description: {self.description}")
elif tag == "episodes":
print(f" Episodes: {self.episodes}")
# Réinitialiser le suivi de la balise actuelle
self.current_data = ""
# Appelé avec les données de caractères entre les balises
def characters(self, content):
# Ne stocker le contenu que si c'est à l'intérieur d'une balise pertinente
if self.current_data == "type":
self.type += content.strip()
elif self.current_data == "format":
self.format += content.strip()
elif self.current_data == "year":
self.year += content.strip()
elif self.current_data == "rating":
self.rating += content.strip()
elif self.current_data == "stars":
self.stars += content.strip()
elif self.current_data == "description":
self.description += content.strip()
elif self.current_data == "episodes":
self.episodes += content.strip()
# --- Exécution principale ---
xml_file = 'movies.xml'
# Créer un analyseur SAX
parser = xml.sax.make_parser()
# Désactiver les espaces de noms (optionnel, simplifie l'exemple)
parser.setFeature(xml.sax.handler.feature_namespaces, 0)
# Créer une instance de notre gestionnaire
handler = MovieHandler()
# Définir le gestionnaire pour l'analyseur
parser.setContentHandler(handler)
print("--- Début de l'analyse SAX ---")
try:
parser.parse(xml_file)
print("\n--- Analyse SAX Terminée ---")
except xml.sax.SAXParseException as e:
print(f"Erreur d'analyse SAX : {e}")
except FileNotFoundError:
print(f"Erreur : Le fichier '{xml_file}' est introuvable.")

Sortie (contenu similaire, flux de traitement différent) :

--- Début de l'analyse SAX ---
***** Film *****
Title: Enemy Behind
Type: War, Thriller
Format: DVD
Year: 2003
Rating: PG
Stars: 10
Description: Talk about a US-Japan war
***** Film *****
Title: Transformers
...

Concepts Clés de SAX :

  • xml.sax.make_parser() : Crée un objet analyseur SAX.
  • xml.sax.ContentHandler : Classe de base pour la gestion des événements d’analyse.
  • startElement(tag, attributes) : Appelé pour chaque balise ouvrante.
  • endElement(tag) : Appelé pour chaque balise fermante.
  • characters(content) : Appelé avec des fragments de données de caractères entre les balises.
  • parser.setContentHandler(handler) : Associe votre gestionnaire à l’analyseur.
  • parser.parse(source) : Démarre le processus d’analyse.

ElementTree est également pratique pour créer des documents XML.

import xml.etree.ElementTree as ET
# Créer l'élément racine
root = ET.Element("data")
# Créer des éléments enfants
item1 = ET.SubElement(root, "item")
item1.set("name", "item1_name") # Définir l'attribut
item1.text = "This is the first item."
item2 = ET.SubElement(root, "item", attrib={"name": "item2_name", "type": "testing"})
item2.text = "Second item content."
# Créer un arbre et écrire dans un fichier
tree = ET.ElementTree(root)
# Formatage joli (optionnel, ajoute l'indentation)
ET.indent(tree, space=" ", level=0)
try:
tree.write("output.xml", encoding="utf-8", xml_declaration=True)
print("Fichier output.xml généré")
except IOError as e:
print(f"Erreur lors de l'écriture du fichier XML : {e}")