Question Comment analyser XML en Python?


J'ai beaucoup de lignes dans une base de données qui contient xml et j'essaye d'écrire un manuscrit de Python qui passera par ces rangées et compte combien d'occurrences d'un attribut particulier de noeud 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?


760
2017-12-16 05:09


origine


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 est encore plus de rapidité - la facilité de programmation dépend de l'API, qui ElementTree définit.

Après avoir construit une instance Element e à partir du XML, par ex. avec le XML fonction, ou en analysant un fichier avec quelque chose comme

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

ou l'un des nombreux autres moyens montrés à ElementTree, vous faites juste quelque chose comme:

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

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


582
2017-12-16 05:21



minidom est le plus rapide et plutôt 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

372
2017-12-16 05:30



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'

201
2017-12-16 05:12



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 par rapport à simplement lire dans le fichier en utilisant readlines.

Les métriques pertinentes peuvent être trouvées dans le tableau ci-dessous, copiées cElementTree site Internet:

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 accélérée C est utilisée automatiquement).

76
2017-10-10 17:44



lxml.objectify est vraiment simple.

Prenant 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}

36
2017-12-16 10:42



je suggère xmltodict pour la simplicité.

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')])])])

30
2018-06-12 11:57



Python a une interface avec l'analyseur expat xml.

xml.parsers.expat

C'est un analyseur non-validant, donc le xml ne sera pas détecté. Mais si vous savez que votre fichier est correct, alors c'est très bien, 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

17
2017-12-16 05:28



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


8
2017-07-08 20:35



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

Installation

pip install untangle

Usage

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


8
2018-03-17 09:10



Je pourrais suggérer 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 code impératif d'analyse / sérialisation avec ElementTree.

Avec declxml, vous utilisez processeurs définir de manière déclarative la structure de votre document XML et comment mapper les structures de données XML et Python. Les processeurs sont utilisés à la fois pour la sérialisation et l'analyse ainsi que pour un niveau basique 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 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 au lieu de dictionnaires, vous pouvez également définir des processeurs pour transformer des données vers et depuis des 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])}

7
2017-09-04 17:40



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 contribution est assez faible, cela fonctionnera bien.


6
2017-12-16 05:28