XML
XML et les technologies qui gravitent autour
1. Définition de XML, terminologie associée
XML est le sigle de eXtensible Markup Language, soit en français le langage de balisage générique extensible. Il a été conçu pour permettre le stockage d’informations de toute nature dans des fichiers structurés en arborescence via l’utilisation de balises au nom devant être représentatif et devant elles-mêmes suivre des règles inhérentes au format XML et d’autres décrites dans des schémas associés au fichier XML afin de permettre leur validation.
Par nature, un fichier XML contient des balises qui portent un nom et doivent être bien fermées (</balise></balise>, <balise />) et bien parenthésées. Par exemple, <a><b></b></a> est valide, mais pas <a><b></a></b>. Chaque balise de son début à sa fin forme ainsi un nœud.
C’est ce principe qui permet de construire l’arborescence et les relations entre nœuds. Par exemple dans <a><b></b><c></c></a>, on peut dire que les nœuds b et c sont frères, qu’elles sont les fils de a et que a est le père de b et de c. On peut également dire que b est le frère gauche de c et que c est le frère droit de b. On peut par ailleurs dire que b et c sont des feuilles, car il s’agit de nœuds ne contenant pas d’autres nœuds.
Une balise peut également contenir un ou plusieurs attributs : <balise attr="valeur" />.
Au-delà de ces règles communes à tous les fichiers XML et en formant le socle, le format est dit « extensible » parce que le vocabulaire et la grammaire du langage XML ne sont pas figés, mais sont définis pour chaque document XML par une référence à ce schéma qui est un élément central.
Un tel schéma indique quelle balise peut être contenue dans quelle autre et également combien, quels sont les attributs que peuvent utiliser telle ou telle balise et quelle est la nature des données d’un attribut ou d’une feuille.
Ceci donne une très grande liberté à celui qui doit stocker des données tout...
Valider un document XML
1. Document XML
Pour toute cette partie et pour la suite, nous avons besoin de la bibliothèque lxml qu’il est possible d’installer ainsi au sein du projet :
$ apt install python3-lxml
Ou, au sein d’un projet, selon l’environnement :
$ pip install lxml
$ poetry add lxml
Voici comment importer la brique de base :
from lxml import etree
Pour valider un document XML, il faut commencer par le charger. Pour ce faire, il y a plusieurs méthodes. La méthode suivante pose un problème lorsqu’est présente la première ligne des fichiers XML contenant un encoding, car Python considère que cette information peut être fausse. L’erreur peut être mise en évidence ainsi :
>>> with open('document.xml') as f:
... f.encoding
... f.read()
... f.seek(0)
... etree.XML(f.read())
...
'UTF-8'
'<?xml version="1.0" encoding="UTF-8"?>\n'
0
Traceback (most recent call last):
File "<stdin>", line 5, in <module>
File "lxml.etree.pyx", line 2723, in lxml.etree.XML
(src/lxml/lxml.etree.c:52448)
File "parser.pxi", line 1564, in lxml.etree._parseMemoryDocument
(src/lxml/lxml.etree.c:79843)
ValueError: Unicode strings with encoding declaration are not supported.
Le fichier est déclaré comme étant en UTF-8 dans la toute première ligne du fichier et l’est...
DOM
1. Lecture
On a vu la manière de parser un fichier pour le valider. En réalité, il s’agit d’un arbre DOM que l’on valide. Cet arbre est extrêmement important pour beaucoup de technologies, comme par exemple la technologie JavaScript qui utilise l’arbre DOM du document HTML comme base de travail (on peut le voir grâce à firebug). Le code le plus court est rappelé ici :
>>> with open('document.xml') as f:
... t = etree.parse(f)
...
>>> root = t.getroot()
Ainsi, il est possible de réaliser à peu près toutes les opérations que l’on souhaite. Voici un pot-pourri :
>>> root.getchildren()
[<Element personne at 0x2cce910>, <Element personne at 0x2d750f0>,
<Element personne at 0x2dc5320>]
>>> for c in root.iterchildren():
... c.getchildren(), c.text, c.values(), c.items()
...
([], None, ['1', 'Paul Personne'])
([], None, ['2', 'Joe Satriani'])
([], None, ['3'])
>>> root.getchildren()[0].keys()
['id', 'nom']
>>> root.getchildren()[0].items()
[('id', '1'), ('nom', 'Paul Personne')]
>>> root.getchildren()[0].get('id')
'1'
Voici un exemple...
SAX
1. Support de SAX dans lxml
La bibliothèque lxml est, avant toute chose, faite pour parser des fichiers XML et construire des arbres DOM. Cependant, elle dispose des outils nécessaires pour appliquer les principes de SAX non pas directement sur le fichier XML, mais sur l’arbre DOM.
Du coup, on perd l’avantage de SAX qui est de n’utiliser que peu de mémoire, puisqu’il ne charge que la balise courante, mais on profite de la méthodologie SAX, le tout étant vu comme de la programmation événementielle.
Le principe est de définir un handler qui offre des hooks permettant au développeur de déclencher une action lorsqu’un certain événement se produit. Cet événement peut être le début ou la fin du document, l’entrée ou la sortie d’une balise, etc. La difficulté pour le développeur est de gérer correctement l’événement en s’assurant qu’il se trouve bien là où il pense être (surtout lorsqu’une balise de même nom peut se trouver à plusieurs niveaux de profondeur), et à gérer correctement le fait que l’arborescence est lue à plat et non pas en profondeur.
Ce handler s’appuie sur l’API complète de SAX et se définit ainsi :
>>> from xml.sax.handler import ContentHandler ...
XPath
XPath est un langage permettant une interrogation simplifiée d’un document XML.
Toute la puissance de XPath se révèle lorsque le développeur maîtrise bien la formulation de ses interrogations et recense tous les cas possibles décrits par son expression.
Pour le reste, la partie Python est triviale. Pour commencer, Python permet à l’objet document XML issu de etree.parse de donner le chemin d’un de ses nœuds :
>>> xml.getpath(xml.getroot().getchildren()[1])
'/liste/personne[2]'
Voici un court rappel de la manière dont la variable xml a été générée :
>>> with open('document.xml') as f:
... f.readline()# suppression de la première ligne
... xml = etree.parse(StringIO(f.read()))
...
Voici un exemple basique d’interrogation et d’utilisation de la réponse :
>>> element = xml.xpath('/liste/personne')
>>> len(element)
3
>>> element[0].tag
'personne'
Un exemple des fonctions utilisables dans les expressions XPath :
>>> xml.xpath('count(/*/personne)')
3.0
Un exemple d’itération à travers des nœuds (qui commencent à 1, et non à 0) :
>>> for i in range(5):
... expr = '/*/personne[%s]'...
XSLT
XSLT est l’acronyme de eXtensible Stylesheet Language Transformations et, comme son nom le laisse supposer, il s’agit d’un langage XML qui permet d’opérer des transformations de style afin de traduire un document XML respectant un certain schéma en un autre document XML respectant un autre schéma.
Le document XML d’entrée ou de sortie peut être des utilisations particulières de langages, comme (X)HTML ou SVG, mais dans tous les cas ces documents doivent être bien formés, sans quoi la transformation est impossible.
Bien que cela ne soit pas prévu par la recommandation XSLT, il est envisageable que le format de sortie ne soit pas un format XML, ce qui permet de mettre en place des mécanismes de transformation vers un fichier texte, par exemple.
Pour commencer, il faut charger le document XSLT qui est du XML :
>>> with open('document.xslt', 'r') as f:
... xslt = etree.XSLT(etree.parse(f))
...
Puis, il suffit de se servir du résultat pour transformer notre document XML :
>>> xml2 = xslt(xml, **{'date': '20110901'})
Le résultat de l’opération peut être affiché en console :
>>> etree.tostring(xml2, pretty_print=True)
b'<root name="liste de personnes" date="20110901">\n <liste>\n ...
Cas spécifique des fichiers HTML
1. Problématique
L’histoire de l’Internet, du web et de l’évolution du langage HTML est pavée de rebondissements et même s’il est maintenant normé (HTML 4, XHTML, XML5), il est rare que cette norme soit réellement suivie.
Pour pallier cette difficulté et pour permettre un traitement professionnel et efficace des flux XML, le langage XHTML a été conçu, rapprochant le HTML du XML et permettant d’en faire un fichier respectant parfaitement les normes XML et donc pouvant être parsé sans difficultés, se débarrassant au passage de résidus indésirables.
Il reste que les fichiers HTML mal formés ne peuvent être parsés à l’aide des parsers XML classiques. BeautifulSoup et le module html sont deux réponses.
Il est à noter que le nouveau standard HTML5 est une nouvelle norme supprimant la plupart des résidus de HTML4, mais en gardant quelques-uns, rajoutant de nouvelles balises destinées à améliorer la sémantique (web sémantique) et surtout, définissant des heuristiques permettant d’interpréter de manière déterministe le flux de données quelles que soient ses malformations.
2. Parser un fichier HTML à la façon DOM
La bibliothèque BeautifulSoup est portée sous Python 3, voici comment l’installer pour le système :
$ sudo apt install python3-bs4
Au sein d’un projet avec son propre environnement, on installera ainsi :
$ sudo pip install beautifulsoup4
$ sudo poetry add beautifulsoup4
Parser un fichier HTML reste trivial :
>>> from bs4 import BeautifulSoup
>>> with open('document.html') as f:
... soup = BeautifulSoup(f.read()) ...