web-dev-qa-db-fra.com

Comment analyser XML en Python?

Une base de données contenant de nombreuses lignes contient xml et j'essaie d'écrire un script Python qui parcourt ces lignes et compte le nombre d'instances d'un attribut de noeud particulier qui apparaissent. Par exemple, mon arbre ressemble à:

<foo>
   <bar>
      <type foobar="1"/>
      <type foobar="2"/>
   </bar>
</foo>

Comment puis-je accéder aux attributs 1 et 2 dans le XML en utilisant Python?

907
randombits

Je suggère ElementTree . Il existe d'autres implémentations compatibles de la même API, telles que lxml , et cElementTree dans la bibliothèque standard Python; mais, dans ce contexte, ce qu’ils ajoutent principalement, c’est encore plus de rapidité - la partie relative à la facilité de programmation dépend de l’API définie par ElementTree.

Commencez par créer une instance d'élément root à partir du XML, par exemple. avec la fonction XML , ou en analysant un fichier avec quelque chose comme:

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

Ou l’un des nombreux autres moyens présentés à ElementTree . Ensuite, faites quelque chose comme:

for type_tag in root.findall('bar/type'):
    value = type_tag.get('foobar')
    print(value)

Et des modèles de code similaires, généralement simples.

700
Alex Martelli

minidom est le plus rapide et le plus simple:

XML:

<data>
    <items>
        <item name="item1"></item>
        <item name="item2"></item>
        <item name="item3"></item>
        <item name="item4"></item>
    </items>
</data>

PYTHON:

from xml.dom import minidom
xmldoc = minidom.parse('items.xml')
itemlist = xmldoc.getElementsByTagName('item')
print(len(itemlist))
print(itemlist[0].attributes['name'].value)
for s in itemlist:
    print(s.attributes['name'].value)

SORTIE

4
item1
item1
item2
item3
item4
412
Ryan Christensen

Vous pouvez utiliser BeautifulSoup

from bs4 import BeautifulSoup

x="""<foo>
   <bar>
      <type foobar="1"/>
      <type foobar="2"/>
   </bar>
</foo>"""

y=BeautifulSoup(x)
>>> y.foo.bar.type["foobar"]
u'1'

>>> y.foo.bar.findAll("type")
[<type foobar="1"></type>, <type foobar="2"></type>]

>>> y.foo.bar.findAll("type")[0]["foobar"]
u'1'
>>> y.foo.bar.findAll("type")[1]["foobar"]
u'2'
226
YOU

Il y a beaucoup d'options sur le marché. cElementTree semble excellent si la vitesse et l'utilisation de la mémoire posent problème. Il y a très peu de temps système par rapport à une simple lecture dans le fichier avec readlines.

Les mesures pertinentes se trouvent dans le tableau ci-dessous, copié du site Web cElementTree :

library                         time    space
xml.dom.minidom (Python 2.1)    6.3 s   80000K
gnosis.objectify                2.0 s   22000k
xml.dom.minidom (Python 2.4)    1.4 s   53000k
ElementTree 1.2                 1.6 s   14500k  
ElementTree 1.2.4/1.3           1.1 s   14500k  
cDomlette (C extension)         0.540 s 20500k
PyRXPU (C extension)            0.175 s 10850k
libxml2 (C extension)           0.098 s 16000k
readlines (read as utf-8)       0.093 s 8850k
cElementTree (C extension)  --> 0.047 s 4900K <--
readlines (read as ascii)       0.032 s 5050k   

Comme indiqué par @ jfs , cElementTree est livré avec Python:

  • Python 2: from xml.etree import cElementTree as ElementTree.
  • Python 3: from xml.etree import ElementTree (la version C accélérée est utilisée automatiquement).
89
Cyrus

Je suggère xmltodict pour simplifier.

Il analyse votre XML à un OrderedDict;

>>> e = '<foo>
             <bar>
                 <type foobar="1"/>
                 <type foobar="2"/>
             </bar>
        </foo> '

>>> import xmltodict
>>> result = xmltodict.parse(e)
>>> result

OrderedDict([(u'foo', OrderedDict([(u'bar', OrderedDict([(u'type', [OrderedDict([(u'@foobar', u'1')]), OrderedDict([(u'@foobar', u'2')])])]))]))])

>>> result['foo']

OrderedDict([(u'bar', OrderedDict([(u'type', [OrderedDict([(u'@foobar', u'1')]), OrderedDict([(u'@foobar', u'2')])])]))])

>>> result['foo']['bar']

OrderedDict([(u'type', [OrderedDict([(u'@foobar', u'1')]), OrderedDict([(u'@foobar', u'2')])])])
41
myildirim

lxml.objectify est vraiment simple.

Prendre votre exemple de texte:

from lxml import objectify
from collections import defaultdict

count = defaultdict(int)

root = objectify.fromstring(text)

for item in root.bar.type:
    count[item.attrib.get("foobar")] += 1

print dict(count)

Sortie:

{'1': 1, '2': 1}
37
Ryan Ginstrom

Python a une interface avec l'analyseur Expat xml.

xml.parsers.expat

C'est un analyseur non-validant, donc le mauvais fichier XML ne sera pas attrapé. Mais si vous savez que votre fichier est correct, alors c'est plutôt bon et vous aurez probablement obtenez les informations exactes que vous voulez et vous pourrez supprimer le reste à la volée.

stringofxml = """<foo>
    <bar>
        <type arg="value" />
        <type arg="value" />
        <type arg="value" />
    </bar>
    <bar>
        <type arg="value" />
    </bar>
</foo>"""
count = 0
def start(name, attr):
    global count
    if name == 'type':
        count += 1

p = expat.ParserCreate()
p.StartElementHandler = start
p.Parse(stringofxml)

print count # prints 4
19
Tor Valamo

Juste pour ajouter une autre possibilité, vous pouvez utiliser untangle , car il s'agit d'une simple bibliothèque xml-to-python-object. Ici vous avez un exemple:

Installation

pip install untangle

Utilisation

Votre fichier xml (un peu changé):

<foo>
   <bar name="bar_name">
      <type foobar="1"/>
   </bar>
</foo>

accéder aux attributs avec untangle :

import untangle

obj = untangle.parse('/path_to_xml_file/file.xml')

print obj.foo.bar['name']
print obj.foo.bar.type['foobar']

la sortie sera:

bar_name
1

Plus d'informations sur le débrouillage peuvent être trouvées ici .
Aussi (si vous êtes curieux), vous pouvez trouver une liste d’outils pour travailler avec XML et Python ici (vous verrez aussi que les plus courants ont été mentionnés dans les réponses précédentes).

13
jchanger

Je pourrais suggérer declxml .

Divulgation complète: j'ai écrit cette bibliothèque parce que je cherchais un moyen de convertir des structures de données XML et Python sans avoir à écrire des dizaines de lignes de code d'analyse/de sérialisation impératif avec ElementTree.

Avec declxml, vous utilisez des processeurs pour définir de manière déclarative la structure de votre document XML et le mappage entre XML et les structures de données Python. Les processeurs sont habitués à la sérialisation et à l'analyse ainsi qu'à un niveau de base de validation.

L'analyse dans Python structures de données est simple:

import declxml as xml

xml_string = """
<foo>
   <bar>
      <type foobar="1"/>
      <type foobar="2"/>
   </bar>
</foo>
"""

processor = xml.dictionary('foo', [
    xml.dictionary('bar', [
        xml.array(xml.integer('type', attribute='foobar'))
    ])
])

xml.parse_from_string(processor, xml_string)

Qui produit la sortie:

{'bar': {'foobar': [1, 2]}}

Vous pouvez également utiliser le même processeur pour sérialiser des données au format XML.

data = {'bar': {
    'foobar': [7, 3, 21, 16, 11]
}}

xml.serialize_to_string(processor, data, indent='    ')

Qui produit la sortie suivante

<?xml version="1.0" ?>
<foo>
    <bar>
        <type foobar="7"/>
        <type foobar="3"/>
        <type foobar="21"/>
        <type foobar="16"/>
        <type foobar="11"/>
    </bar>
</foo>

Si vous souhaitez utiliser des objets plutôt que des dictionnaires, vous pouvez également définir des processeurs pour transformer les données vers et à partir d'objets.

import declxml as xml

class Bar:

    def __init__(self):
        self.foobars = []

    def __repr__(self):
        return 'Bar(foobars={})'.format(self.foobars)


xml_string = """
<foo>
   <bar>
      <type foobar="1"/>
      <type foobar="2"/>
   </bar>
</foo>
"""

processor = xml.dictionary('foo', [
    xml.user_object('bar', Bar, [
        xml.array(xml.integer('type', attribute='foobar'), alias='foobars')
    ])
])

xml.parse_from_string(processor, xml_string)

Qui produit la sortie suivante

{'bar': Bar(foobars=[1, 2])}
11
gatkin

Voici un code très simple mais efficace utilisant cElementTree.

try:
    import cElementTree as ET
except ImportError:
  try:
    # Python 2.5 need to import a different module
    import xml.etree.cElementTree as ET
  except ImportError:
    exit_err("Failed to import cElementTree from any known place")      

def find_in_tree(tree, node):
    found = tree.find(node)
    if found == None:
        print "No %s in file" % node
        found = []
    return found  

# Parse a xml file (specify the path)
def_file = "xml_file_name.xml"
try:
    dom = ET.parse(open(def_file, "r"))
    root = dom.getroot()
except:
    exit_err("Unable to open and parse input definition file: " + def_file)

# Parse to find the child nodes list of node 'myNode'
fwdefs = find_in_tree(root,"myNode")

La source:

http://www.snip2code.com/Snippet/991/python-xml-parse?fromPage=1

10
Jan Kohila

Je trouve le Python xml.dom et xml.dom.minidom assez facile. Gardez à l'esprit que DOM n'est pas approprié pour de grandes quantités de XML, mais si votre entrée est assez petite, cela fonctionnera bien.

6
EMP
import xml.etree.ElementTree as ET
data = '''<foo>
           <bar>
               <type foobar="1"/>
               <type foobar="2"/>
          </bar>
       </foo>'''
tree = ET.fromstring(data)
lst = tree.findall('bar/type')
for item in lst:
    print item.get('foobar')

Cela affichera la valeur de l'attribut foobar.

5
Souvik Dey

XML

<foo>
   <bar>
      <type foobar="1"/>
      <type foobar="2"/>
   </bar>
</foo>

PYTHON_CODE

import xml.etree.cElementTree as ET

tree = ET.parse("foo.xml")
root = tree.getroot() 
root_tag = root.tag
print(root_tag) 

for form in root.findall("./bar/type"):
    x=(form.attrib)
    z=list(x)
    for i in z:
        print(x[i])

SORTIE:

foo
1
2
4
Ahito

xml.etree.ElementTree vs. lxml

Ce sont quelques-uns des avantages des deux bibliothèques les plus utilisées que je souhaiterais connaître avant de choisir entre elles.

xml.etree.ElementTree:

  1. De la bibliothèque standard: pas besoin d'installer un module

lxml

  1. Écrivez facilement déclaration XML: par exemple, avez-vous besoin d'ajouter standalone="no"?
  2. Jolie impression: vous pouvez avoir un Nice en retrait XML sans code supplémentaire.
  3. Objectify fonctionnalité: Il vous permet d'utiliser XML comme si vous utilisiez une hiérarchie d'objet Python normale.node.
2
G M