Article original : How to Parse XML in Python Without Using External Libraries
Dans le développement de logiciels, vous rencontrerez du XML (Extensible Markup Language) lors de l'utilisation de fichiers de configuration, de réponses d'API, d'exports de données, et plus encore. Bien qu'il existe de puissantes bibliothèques tierces pour analyser le XML, la bibliothèque standard de Python inclut déjà tout ce dont vous avez besoin.
Dans ce tutoriel, vous apprendrez à parser du XML en utilisant le module intégré de Python xml.etree.ElementTree. Aucune installation via pip n'est requise.
🔗 Vous pouvez trouver le code sur GitHub.
Prérequis
Pour suivre ce tutoriel, vous devez disposer de :
Python 3.7 ou une version ultérieure installée sur votre système
Une compréhension de base de la syntaxe et des structures de données Python
Une familiarité avec les concepts de programmation de base tels que les boucles et les conditions
Un éditeur de texte ou un IDE pour écrire du code Python
Aucune bibliothèque externe n'est requise car nous utiliserons le module intégré xml.etree.ElementTree de Python.
Table des matières
Comment lire une chaîne XML
Commençons simplement. Nous allons parser du XML directement à partir d'une chaîne de caractères pour comprendre les concepts fondamentaux.
import xml.etree.ElementTree as ET
xml_string = """
<catalog>
<product id="101">
<name>Wireless Keyboard</name>
<price currency="USD">29.99</price>
</product>
</catalog>
"""
root = ET.fromstring(xml_string)
print(f"Root tag: {root.tag}")
print(f"Root attributes: {root.attrib}")
Comment cela fonctionne :
Nous importons
xml.etree.ElementTreeet lui donnons l'aliasET(c'est la convention)ET.fromstring()analyse la chaîne XML et renvoie l'élément racine (root)Chaque élément possède une propriété
.tag(le nom de l'élément) et un dictionnaire.attrib(ses attributs)L'objet
rootreprésente l'élément<catalog>dans notre XML
Pour l'exemple ci-dessus, vous verrez la sortie suivante :
Root tag: catalog
Root attributes: {}
Ici, root.attrib est vide car l'élément racine <catalog> dans la chaîne xml_string fournie n'a aucun attribut défini. Les attributs sont des paires clé-valeur à l'intérieur de la balise d'ouverture d'un élément XML, comme id="101" ou currency="USD" dans les éléments <product> et <price>. Comme <catalog> n'a qu'une balise et aucune information supplémentaire dans sa balise d'ouverture, son dictionnaire d'attributs est vide.
Comment lire un fichier XML
Dans les applications réelles, vous lirez généralement le XML à partir de fichiers. Supposons que vous ayez un fichier products.xml. Voici comment vous pouvez lire à partir du fichier XML :
# Analyser un fichier XML
tree = ET.parse('products.xml')
root = tree.getroot()
print(f"Root element: {root.tag}")
Avant de passer à l'exécution et à la vérification de la sortie, notons les différences entre la lecture de chaînes XML et de fichiers :
ET.parse()lit à partir d'un fichier et renvoie un objetElementTreeNous appelons
.getroot()pour obtenir l'élément racineUtilisez
ET.parse()pour les fichiers,ET.fromstring()pour les chaînes
L'exécution du code ci-dessus devrait vous donner :
Root element: catalog
Comment trouver des éléments dans un arbre XML
ElementTree vous offre trois manières principales de rechercher des éléments. Il est important de comprendre quand utiliser chacune d'elles.
import xml.etree.ElementTree as ET
xml_data = """
<catalog>
<product id="101">
<name>Wireless Keyboard</name>
<categories>
<category>Electronics</category>
<category>Accessories</category>
</categories>
</product>
<product id="102">
<name>USB Mouse</name>
<categories>
<category>Electronics</category>
</categories>
</product>
</catalog>
"""
root = ET.fromstring(xml_data)
# Méthode 1 : find() - renvoie le PREMIER élément correspondant
first_product = root.find('product')
print(f"First product ID: {first_product.get('id')}")
# Méthode 2 : findall() - renvoie TOUS les enfants directs qui correspondent
all_products = root.findall('product')
print(f"Total products: {len(all_products)}")
# Méthode 3 : iter() - trouve de manière récursive TOUS les éléments correspondants
all_categories = root.iter('category')
category_list = [cat.text for cat in all_categories]
print(f"All categories: {category_list}")
Voyons maintenant comment fonctionnent ces trois méthodes :
find()s'arrête à la première correspondance. Utilisez-la lorsque vous n'avez besoin que d'un seul élément.findall()ne recherche que parmi les enfants directs (un seul niveau de profondeur). Utilisez-la pour les éléments enfants immédiats.iter()effectue une recherche récursive dans tout l'arbre. Utilisez-la lorsque les éléments peuvent être imbriqués n'importe où.
C'est important : findall('category') sur la racine ne trouvera rien car <category> n'est pas un enfant direct de <catalog>. Mais iter('category') trouvera toutes les catégories, peu importe leur niveau d'imbrication. Ainsi, lorsque vous exécutez le code ci-dessus, vous obtiendrez :
First product ID: 101
Total products: 2
All categories: ['Electronics', 'Accessories', 'Electronics']
Comment extraire du texte et des attributs du XML
Maintenant, extrayons les données réelles de notre XML. C'est ici que vous transformez le XML structuré en données Python exploitables.
xml_data = """
<catalog>
<product id="101">
<name>Wireless Keyboard</name>
<price currency="USD">29.99</price>
<stock>45</stock>
</product>
</catalog>
"""
root = ET.fromstring(xml_data)
product = root.find('product')
# Obtenir le contenu textuel de l'élément
product_name = product.find('name').text
price_text = product.find('price').text
stock_text = product.find('stock').text
# Obtenir les attributs (deux manières)
product_id = product.get('id') # Méthode 1 : .get()
product_id_alt = product.attrib['id'] # Méthode 2 : dictionnaire .attrib
# Obtenir des attributs imbriqués
price_element = product.find('price')
currency = price_element.get('currency')
print(f"Product: {product_name}")
print(f"ID: {product_id}")
print(f"Price: {currency} {price_text}")
print(f"Stock: {stock_text}")
Ceci affiche :
Product: Wireless Keyboard
ID: 101
Price: USD 29.99
Stock: 45
Ce qui se passe ici :
.textrécupère le contenu textuel entre les balises d'ouverture et de fermeture.get('nom_attribut')récupère un attribut de manière sécurisée (renvoieNones'il est manquant).attrib['nom_attribut']accède directement au dictionnaire d'attributs (lève uneKeyErrors'il est manquant)Utilisez
.get()lorsqu'un attribut peut être optionnel, utilisez.attrib[]lorsqu'il est requis
Comment construire un parseur XML simple
Mettons tout cela en pratique avec un exemple concret. Nous allons parser le catalogue complet de produits et le convertir en une liste Python de dictionnaires.
def parse_product_catalog(xml_file):
"""Analyse un catalogue de produits XML et renvoie une liste de dictionnaires de produits."""
tree = ET.parse(xml_file)
root = tree.getroot()
products = []
for product_element in root.findall('product'):
# Extraire les données du produit
product = {
'id': product_element.get('id'),
'name': product_element.find('name').text,
'price': float(product_element.find('price').text),
'currency': product_element.find('price').get('currency'),
'stock': int(product_element.find('stock').text),
'categories': []
}
# Extraire les catégories (éléments imbriqués)
categories_element = product_element.find('categories')
if categories_element is not None:
for category in categories_element.findall('category'):
product['categories'].append(category.text)
products.append(product)
return products
Analyse de ce parseur :
Nous itérons sur tous les éléments
<product>en utilisantfindall()Pour chaque produit, nous extrayons le texte et les attributs dans un dictionnaire. Nous convertissons les chaînes numériques en types appropriés (
floatpour le prix,intpour le stock)Pour les catégories imbriquées, nous vérifions d'abord si l'élément
<categories>existe. Ensuite, nous itérons sur les éléments enfants<category>et collectons leur texte
Le résultat est une structure de données Python propre que vous pouvez facilement manipuler. Vous pouvez maintenant utiliser le parseur ainsi :
products = parse_product_catalog('products.xml')
for product in products:
print(f"\nProduct: {product['name']}")
print(f" ID: {product['id']}")
print(f" Price: {product['currency']} {product['price']}")
print(f" Stock: {product['stock']}")
print(f" Categories: {', '.join(product['categories'])}")
Sortie :
Product: Wireless Keyboard
ID: 101
Price: USD 29.99
Stock: 45
Categories: Electronics, Accessories
Product: USB Mouse
ID: 102
Price: USD 15.99
Stock: 120
Categories: Electronics
Comment gérer les données manquantes
Le XML en conditions réelles est souvent désordonné (pas de surprise ici !). Des éléments peuvent être manquants, le texte peut être vide ou des attributs peuvent ne pas exister. Voici comment gérer cela proprement.
xml_data = """
<catalog>
<product id="101">
<name>Wireless Keyboard</name>
<price currency="USD">29.99</price>
</product>
<product id="102">
<name>USB Mouse</name>
<!-- Élément price manquant -->
</product>
</catalog>
"""
root = ET.fromstring(xml_data)
for product in root.findall('product'):
name = product.find('name').text
# Manière sécurisée de gérer des éléments potentiellement manquants
price_element = product.find('price')
if price_element is not None:
price = float(price_element.text)
currency = price_element.get('currency', 'USD') # Valeur par défaut
print(f"{name}: {currency} {price}")
else:
print(f"{name}: Price not available")
Ici, nous gérons les données manquantes potentielles en :
Utilisant
product.find('price')pour rechercher l'élément<price>dans l'élément<product>actuel.Vérifiant si le résultat de
find()estNone. Si un élément n'est pas trouvé,find()renvoieNone.Utilisant une condition
if price_element is not None:pour ne tenter d'accéder au texte(price_element.text)et aux attributs(price_element.get('currency', 'USD'))de l'élément<price>que s'il a réellement été trouvé.Ajoutant un bloc
elsepour gérer le cas où l'élément<price>est manquant, en affichant "Price not available".
Cette approche évite les erreurs qui se produiraient si vous essayiez d'accéder à .text ou .get() sur un objet None. Pour l'extrait de code ci-dessus, vous obtiendrez :
Wireless Keyboard: USD 29.99
USB Mouse: Price not available
Voici quelques stratégies supplémentaires de gestion d'erreurs :
Vérifiez toujours si
find()renvoieNoneavant d'accéder à.textou.get()Utilisez
.get('attr', 'default')pour fournir des valeurs par défaut aux attributs manquantsEnvisagez d'envelopper l'analyse dans des blocs try-except pour le code en production
Validez vos données après l'analyse plutôt que de supposer que la structure XML est correcte
Conclusion
Vous savez maintenant comment parser du XML en Python sans installer de bibliothèques externes. Vous avez appris :
Comment lire du XML à partir de chaînes et de fichiers
La différence entre
find(),findall()etiter()Comment extraire le contenu textuel et les attributs en toute sécurité
Comment gérer les éléments imbriqués et les données manquantes
Le module xml.etree.ElementTree fonctionne suffisamment bien pour la plupart des besoins d'analyse XML, et il est toujours disponible dans la bibliothèque standard de Python.
Pour une navigation et une sélection XML plus avancées, vous pouvez explorer les expressions XPath. XPath fonctionne bien pour sélectionner des nœuds dans un document XML et peut être très utile pour les structures complexes. Nous aborderons cela dans un autre tutoriel.
D'ici là, bon parsing !