Wiki Python Fr   CodeXML UserPreferences
 
HelpContents FindPage Diffs Info Edit Subscribe XML Print View

Cette page tente de donner des exemples de traitement de fichiers XML à l'aide de python et des modules ad-hoc.

  1. Généralités
  2. L'API DOM
    1. Qu'est ce que c'est ?
    2. Exemples de codes
      1. Créer un document XML
      2. Extraire des données d'un élément XML
      3. Lire un flux RSS 2.0 et en extraire les titres
  3. L'API SAX
    1. Qu'est ce que c'est ?
    2. Comment écrire un programme python utilisant SAX ?
    3. Exemples de codes
      1. Créer un document XML
      2. Transformer un fichier *.xml en *.sql
  4. Format OpenDocument
  5. Liens utiles

1. Généralités

Courte présentation de XML. Ils existents deux API qui correspondent à deux façons de traiter des documents : DOM et SAX

2. L'API DOM

2.1. Qu'est ce que c'est ?

DOM signifie "Document Object Model". C'est une API qui associe à un fichier XML un arbre (ou un bosquet) dans lequel nous allons pouvoir naviguer pour accéder à différentes informations. Les avantages de cette API sont :

Les inconvénients :

2.2. Exemples de codes

2.2.1. Créer un document XML

  1 
  2 
  3 
  4 
  5 
  6 
  7 
  8 
  9 
 10 
 11 
 12 
 13 
 14 
 15 
from xml.dom.minidom import Document

doc = Document()

racine = doc.createElement("element_racine")
doc.appendChild(racine)

element = doc.createElement("sous_element")
element.setAttribute("un_attribut", "la valeur")
racine.appendChild(element)

texte = doc.createTextNode('Coucou :-)')
element.appendChild(texte)

print doc.toprettyxml()

Ce qui nous donne le résultat suivant :

<?xml version="1.0" ?>
<element_racine>
        <sous_element un_attribut="la valeur">
                Coucou :-)
        </sous_element>
</element_racine>

2.2.2. Extraire des données d'un élément XML

  1 
  2 
  3 
  4 
  5 
from xml.dom import minidom
dom = minidom.parse('out.xml')

section = dom.getElementsByTagName('section')
print section[0].toxml()

2.2.3. Lire un flux RSS 2.0 et en extraire les titres

  1 
  2 
  3 
  4 
  5 
  6 
import urllib, sys, xml.dom.minidom
adress = 'http://www.sebsauvage.net/rss/updates.xml'
document = xml.dom.minidom.parse(urllib.urlopen(adress))
for item in document.getElementsByTagName('item'):
    titre = item.getElementsByTagName('title')[0].firstChild.data
    print "Titre:", titre.encode('latin-1','replace')

3. L'API SAX

3.1. Qu'est ce que c'est ?

L'API SAX est née fin 1998 sur la liste xml-dev face à la multitude de parseurs présents à l'époque. SAX signifie "Simple API for XML" et n'est pas normalisée par le W3C contrairement à DOM. Cette API (SAX2 en fait) est inclue dans python depuis la version 2. Les avantages de l'API SAX sont :

3.2. Comment écrire un programme python utilisant SAX ?

Il faut suivre les étapes suivantes :

  1 
  2 
  3 
  4 
  5 
  6 
  7 
  8 
  9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
from xml.sax import ContentHandler, make_parser
class MonHandler(ContentHandler):
     "Document Handler personnel"
     def __init__(self):
     "initialisation"
          pass
     def startDocument(self):
     "fonction appelée lorsque le parser rencontre le premier élement"
          pass
     def startElement(self, name, attrs):
     "fonction appelée lorsque le parser rencontre une balise ouvrante"
          pass
     def endElement(self, name):
     "fonction appelée lorsque le parser rencontre une balise fermante"
          pass
     def characters(self, chrs, offset, length):
     "fonction appelée lorsque le parser rencontre des données dans un élement"
          pass
     def endDocument(self):
     "fonction appelée lorsque le parser rencontre le dernier élement"
          pass

doc = MonHandler()
saxparser = make_parser()
saxparser.setContentHandler(doc)

datasource = open("out.xml","r")
saxparser.parse(datasource)

Il est courant d'utiliser des variables d'instances (liste ou dict) chargées de collecter des données spécifiques. Si le traitement nécessite de conserver une trace des balises déjà rencontrées, il suffit d'utliser une liste comme pile: on empile les balise dans startElement(), on les dépile dans endElement().

3.3. Exemples de codes

3.3.1. Créer un document XML

La bibliothèque standard de Python propose un handler SAX standard pour écrire dans un descripteur de fichier : xml.sax.saxutils.XMLGenerator. On peut l'instancier en lui passant un objet de fichier ouvert. Par défaut, il écrit sur la sortie standard.

  1 
  2 
  3 
  4 
  5 
  6 
  7 
  8 
  9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
from xml.sax.saxutils import XMLGenerator
from xml.sax.xmlreader import AttributesImpl

hd = XMLGenerator() # Création du handler

hd.startDocument()
attr = AttributesImpl({ "attribut1": "valeur", "attribut2": "0" })
hd.startElement("élément_racine", attr)

hd.startElement("toto", AttributesImpl({"foo": "bar", "titi": "tata"}))
# Les caractères génants seront échapés comme il faut
hd.characters("<plop> & <!--")
hd.endElement("toto")

# Utilisation des espaces de nommages
uri = "http://www.quelquepart.org/namespace/"
hd.startPrefixMapping("monNS", uri)
hd.startElementNS(
        (uri, "foo"),
        "foo",
        AttributesImpl({ (uri, "att1"): "false", (uri, "att2"): "coucou" })
        )
hd.characters("Bla bla bli bla...")
hd.endElementNS((uri, "foo"), "mon_ns")

hd.endElement("élément_racine")
hd.endDocument()

Cette méthode est intéressante pour générer de gros fichiers pour lesquels DOM serait trop gourmand en mémoire.

3.3.2. Transformer un fichier *.xml en *.sql

Le fichier *.xml ressemble à

<?xml version="1.0" encoding="ISO-8859-1"?>
<document>
<introduction>Texte intro</introduction>
<section id="1" name="section 1" description="Texte introductif à la section 1">
<sousSection id="1" rubrik="1" thema="subsection 1.1">
<texte>Texte de la soussection 1.1</texte>
</sousSection>
<sousSection id="2" rubrik="1" thema="subsection 1.2">
<texte>Texte de la soussection 1.2</texte>
</sousSection>
</section>
<section id="2" name="section 2" description="Texte introductif à la section 2">
<sousSection id="1" rubrik="2" thema="soussection 2.1">
<texte>Texte de la soussection 2.1</texte>
</sousSection>
</section>
</document>
L'utilisation de l'API SAX va le convertir en fichier de requêtes SQL. Voici le code python :

4. Format OpenDocument

Ne voulant pas lancer openoffice systématiquement, ni être dépendant d'une plateforme, j'ai essayé ooopy http://ooopy.sourceforge.net/ mais le projet ne semble pas évoluer et ça ne fonctionne pas bien avec odt, ça fonctionne plutôt avec sxw (ancien format openoffice). Par contre le principe reste intéressant, il dézippe le document et l'ouvre avec elementtree. J'ai donc utilisé cette technique.

Comme me l'a suggéré florent manens, j'utilise la notion de "variables" d'openoffice plutôt que des balise textes $$xxxx, ça évite les problèmes au cas où la balise texte serait dissociée en interne. (genre $$xx<b>xx</b>).

  1 
  2 
  3 
  4 
  5 
  6 
  7 
  8 
  9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
from zipfile import ZipFile, ZIP_DEFLATED
from cStringIO import StringIO
from cElementTree import ElementTree, fromstring

def replace(infile, outfile, replace):
    inzip = ZipFile(infile, 'r', ZIP_DEFLATED)
    outzip = ZipFile(outfile, 'w', ZIP_DEFLATED)
    content = ElementTree(fromstring(inzip.read('content.xml')))

    for p in content.findall(".//{urn:oasis:names:tc:opendocument:xmlns:text:1.0}variable-set"):
        name = p.get("{urn:oasis:names:tc:opendocument:xmlns:text:1.0}name")
        if name in replace:
            p.text = replace[name]

    for f in inzip.infolist():
        if f.filename == 'content.xml':
            s = StringIO()
            content.write(s)
            outzip.writestr('content.xml', s.getvalue())
        else:
            outzip.writestr(f, inzip.read(f.filename))
    inzip.close()
    outzip.close()

-- William Dodé

5. Liens utiles

Les liens ci-dessous étaient valides le 18 avril 2005 :

Page en cours de construction... Vos contributions sont les bienvenues ! ;-) Rémi Boulle.

PythonPowered