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?
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.
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
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 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:
from xml.etree import cElementTree as ElementTree
.from xml.etree import ElementTree
(la version C accélérée est utilisée automatiquement).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')])])])
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}
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
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).
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])}
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
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.
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.
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
Ce sont quelques-uns des avantages des deux bibliothèques les plus utilisées que je souhaiterais connaître avant de choisir entre elles.
standalone="no"
?.node
.