Comment puis-je analyser XML en Python?

j'ai beaucoup de lignes dans une base de données qui contient xml et j'essaye d'écrire un script Python qui va passer par ces lignes et compter combien d'instances d'un attribut de noeud particulier 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 du XML en utilisant Python?

776
demandé sur Martin Thoma 2009-12-16 08:09:24

13 réponses

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 elle-même; mais, dans ce contexte, ce qu'ils ajoutent principalement c'est encore plus de vitesse -- la facilité de programmation de la partie dépend de l'API, que ElementTree définit.

après la construction D'une instance D'élément e du XML, p.ex. avec le Fonction XML , ou en analysant un fichier avec quelque chose comme

import xml.etree.ElementTree
e = xml.etree.ElementTree.parse('thefile.xml').getroot()

ou de l'une des nombreuses autres façons indiquées à ElementTree , vous faites quelque chose comme:

for atype in e.findall('type'):
    print(atype.get('foobar'))

et similaires, généralement assez simples, motifs de code.

593
répondu Alex Martelli 2017-12-10 20:11:15

minidom est le moyen le plus rapide et assez 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)

OUTPUT

4
item1
item1
item2
item3
item4
381
répondu Ryan Christensen 2015-02-23 10:22:41

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'
208
répondu YOU 2015-04-08 16:16:50

il y a beaucoup d'options là-bas. cElementTree semble excellent si la vitesse et l'utilisation de la mémoire sont un problème. Il a très peu de frais généraux par rapport à la simple lecture dans le fichier en utilisant readlines .

les mesures pertinentes peuvent être trouvées dans le tableau ci-dessous, copié du cElementTree site web:

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 le souligne @jfs , cElementTree est livré en paquets 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).
78
répondu Cyrus 2018-01-31 05:44:43

lxml.objectify est vraiment simple.

prise de votre texte échantillon:

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}
36
répondu Ryan Ginstrom 2013-06-07 08:15:54

je suggère xmltodict pour la simplicité.

il parse 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')])])])
31
répondu myildirim 2014-06-13 07:02:11

Python a une interface avec l'analyseur expat xml.

xml.parsers.expat

c'est un analyseur de non-validation, donc un mauvais xml ne sera pas attrapé. Mais si vous savez que votre fichier est correct, alors c'est assez bon, et vous aurez probablement obtenir l'information exacte que vous voulez et vous pouvez jeter 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
18
répondu Tor Valamo 2009-12-16 08:47:08

ici un code très simple mais efficace en 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")

Source:

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

8
répondu Jan Kohila 2013-07-09 20:09:34

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

Installation

pip install untangle

Utilisation

Votre fichier xml (un peu modifié):

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

accéder aux attributs avec démêler :

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 untangle 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 communs ont été mentionnés par les réponses précédentes).

8
répondu jessag 2017-03-17 09:23:13

je propose declxml .

Full disclosure: j'ai écrit cette bibliothèque parce que je cherchais un moyen de convertir entre les structures de données XML et Python sans avoir besoin d'écrire des dizaines de lignes de parsing/serialization impérative code avec ElementTree.

avec declxml, vous utilisez processeurs pour définir de manière déclarative la structure de votre document XML et comment faire la correspondance entre XML et Python structures de données. Les processeurs sont utilisés à la fois pour la sérialisation et le parsing ainsi que pour un niveau de base de validation.

L'analyse des structures de données Python 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 les données en XML

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

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

qui produit la production 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 travailler avec des objets au lieu de dictionnaires, vous pouvez définir des processeurs pour transformer des 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 production suivante

{'bar': Bar(foobars=[1, 2])}
8
répondu gatkin 2017-09-04 17:40:26

je trouve le python xml.dom et xml.Dom.minidom assez facile. Gardez à l'esprit que DOM n'est pas bon pour de grandes quantités de XML, mais si votre entrée est assez petite, alors cela fonctionnera très bien.

6
répondu EMP 2009-12-16 05:28:55
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')

indique la valeur de l'attribut foobar.

5
répondu Souvik Dey 2017-02-20 15:56:58

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
2
répondu Ahito 2018-07-09 09:02:14