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?
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.
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
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'
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).
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}
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')])])])
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
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
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).
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])}
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.
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.
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