Ik heb veel rijen in een database die XML bevat en ik probeer een Python-script te schrijven om exemplaren van een bepaald knooppuntkenmerk te tellen.
Mijn boom ziet eruit als:
<foo>
<bar>
<type foobar="1"/>
<type foobar="2"/>
</bar>
</foo>
Hoe krijg ik met Python toegang tot de attributen "1"
en "2"
in de XML?
Antwoord 1, autoriteit 100%
Ik stel voor ElementTree
. Er zijn andere compatibele implementaties van dezelfde API, zoals lxml
en cElementTree
in de Python-standaardbibliotheek zelf; maar in deze context voegen ze vooral nog meer snelheid toe — het programmeergemak hangt af van de API, die ElementTree
definieert.
Bouw eerst een Element-instantie root
op basis van de XML, b.v. met de functie XMLof door een bestand ontleden met zoiets als:
import xml.etree.ElementTree as ET
root = ET.parse('thefile.xml').getroot()
Of een van de vele andere manieren die worden getoond op ElementTree
. Doe dan iets als:
for type_tag in root.findall('bar/type'):
value = type_tag.get('foobar')
print(value)
En soortgelijke, meestal vrij eenvoudige, codepatronen.
Antwoord 2, autoriteit 53%
minidom
is de snelste en vrij ongecompliceerd.
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)
Uitvoer:
4
item1
item1
item2
item3
item4
Antwoord 3, autoriteit 29%
U kunt BeautifulSoupgebruiken:
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'
Antwoord 4, autoriteit 12%
Er zijn veel opties. cElementTreeziet er uitstekend uit als snelheid en geheugengebruik een probleem zijn. Het heeft weinig overhead vergeleken met het eenvoudig inlezen van het bestand met behulp van readlines
.
De relevante statistieken zijn te vinden in de onderstaande tabel, gekopieerd van de cElementTree-website:
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
Zoals aangegeven door @jfs, cElementTree
wordt geleverd met Python:
- Python 2:
from xml.etree import cElementTree as ElementTree
. - Python 3:
from xml.etree import ElementTree
(de versnelde C-versie wordt automatisch gebruikt).
Antwoord 5, autoriteit 5%
Ik stel voor de eenvoud xmltodictvoor.
Het parseert uw XML naar een 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')])])])
Antwoord 6, autoriteit 5%
lxml.objectifyis heel eenvoudig.
Uw voorbeeldtekst nemen:
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)
Uitvoer:
{'1': 1, '2': 1}
Antwoord 7, autoriteit 2%
Python heeft een interface naar de expat XML-parser.
xml.parsers.expat
Het is een niet-validerende parser, dus slechte XML wordt niet opgevangen. Maar als je weet dat je bestand correct is, dan is dit best goed, en je krijgt waarschijnlijk de exacte informatie die je wilt en je kunt de rest meteen weggooien.
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
Antwoord 8, autoriteit 2%
Om nog een mogelijkheid toe te voegen, kun je untanglegebruiken, omdat het een eenvoudige xml-naar-python-objectbibliotheek is. Hier heb je een voorbeeld:
Installatie:
pip install untangle
Gebruik:
Uw XML-bestand (een beetje gewijzigd):
<foo>
<bar name="bar_name">
<type foobar="1"/>
</bar>
</foo>
Toegang tot de attributen met untangle
:
import untangle
obj = untangle.parse('/path_to_xml_file/file.xml')
print obj.foo.bar['name']
print obj.foo.bar.type['foobar']
De uitvoer zal zijn:
bar_name
1
Meer informatie over ontwarren is te vinden in “ontwarren“.
Als je nieuwsgierig bent, kun je een lijst met tools voor het werken met XML en Python vinden in “Python en XML“. Je zult ook zien dat de meest voorkomende werden genoemd in eerdere antwoorden.
Antwoord 9, autoriteit 2%
Ik zou declxmlkunnen voorstellen.
Volledige openbaarmaking: ik heb deze bibliotheek geschreven omdat ik op zoek was naar een manier om gegevensstructuren tussen XML en Python te converteren zonder dat ik tientallen regels imperatieve parsing-/serialisatiecode met ElementTree hoefde te schrijven.
Met declxml gebruikt u processorsom de structuur van uw XML-document declaratief te definiëren en hoe u de gegevensstructuren van XML en Python kunt koppelen. Processors worden gebruikt voor zowel serialisatie en parsing als voor een basisniveau van validatie.
Het ontleden in Python-gegevensstructuren is eenvoudig:
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)
Wat de uitvoer produceert:
{'bar': {'foobar': [1, 2]}}
U kunt dezelfde processor ook gebruiken om gegevens naar XML te serialiseren
data = {'bar': {
'foobar': [7, 3, 21, 16, 11]
}}
xml.serialize_to_string(processor, data, indent=' ')
Wat de volgende uitvoer oplevert
<?xml version="1.0" ?>
<foo>
<bar>
<type foobar="7"/>
<type foobar="3"/>
<type foobar="21"/>
<type foobar="16"/>
<type foobar="11"/>
</bar>
</foo>
Als u met objecten wilt werken in plaats van met woordenboeken, kunt u processors definiëren om ook gegevens van en naar objecten te transformeren.
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)
Wat de volgende uitvoer oplevert
{'bar': Bar(foobars=[1, 2])}
Antwoord 10
Hier een zeer eenvoudige maar effectieve code met behulp van 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")
Dit is van “python xml ontleden“.
Antwoord 11
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])
Uitvoer:
foo
1
2
Antwoord 12
xml.etree.ElementTree vs. lxml
Dit zijn enkele voordelen van de twee meest gebruikte bibliotheken die ik graag zou willen weten voordat ik ertussen kies.
xml.etree.ElementTree:
- Van de standaardbibliotheek: u hoeft geen enkele module te installeren
lxml
- Gemakkelijk XML-declaratieschrijven: moet u bijvoorbeeld
standalone="no"
toevoegen? - Mooi printen: je kunt een mooie ingesprongenXML hebben zonder extra code.
- Objectify-functionaliteit: hiermee kunt u XML gebruiken alsof u te maken hebt met een normale Python-objecthiërarchie
.node
. sourceline
kunt u eenvoudig de regel van het XML-element dat u gebruikt ophalen.- u kunt ook een ingebouwde XSD-schemacontrole gebruiken.
Met
Antwoord 13
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')
Hiermee wordt de waarde van het kenmerk foobar
afgedrukt.
Antwoord 14
Het is niet nodig om een lib-specifieke API te gebruikenals je python-benedict
gebruikt. Initialiseer gewoon een nieuwe instantie vanuit uw XML en beheer deze eenvoudig omdat het een dict
subklasse is.
Installatie is eenvoudig: pip install python-benedict
from benedict import benedict as bdict
# data-source can be an url, a filepath or data-string (as in this example)
data_source = """
<foo>
<bar>
<type foobar="1"/>
<type foobar="2"/>
</bar>
</foo>"""
data = bdict.from_xml(data_source)
t_list = data['foo.bar'] # yes, keypath supported
for t in t_list:
print(t['@foobar'])
Het ondersteunt en normaliseertI/O-bewerkingen met vele formaten: Base64
, CSV
, JSON
, TOML
, xml
, YAML
en query-string
.
Het is goed getest en open-source op GitHub. Openbaarmaking: ik ben de auteur.
Antwoord 15
Een nieuw lib, ik werd er verliefd op nadat ik het had gebruikt. Ik raad het je aan.
from simplified_scrapy import SimplifiedDoc
xml = '''
<foo>
<bar>
<type foobar="1"/>
<type foobar="2"/>
</bar>
</foo>
'''
doc = SimplifiedDoc(xml)
types = doc.selects('bar>type')
print (len(types)) # 2
print (types.foobar) # ['1', '2']
print (doc.selects('bar>type>foobar()')) # ['1', '2']
Hierzijn meer voorbeelden. Deze bibliotheek is gemakkelijk te gebruiken.
Antwoord 16
#If the xml is in the form of a string as shown below then
from lxml import etree, objectify
'''sample xml as a string with a name space {http://xmlns.abc.com}'''
message =b'<?xml version="1.0" encoding="UTF-8"?>\r\n<pa:Process xmlns:pa="http://xmlns.abc.com">\r\n\t<pa:firsttag>SAMPLE</pa:firsttag></pa:Process>\r\n' # this is a sample xml which is a string
print('************message coversion and parsing starts*************')
message=message.decode('utf-8')
message=message.replace('<?xml version="1.0" encoding="UTF-8"?>\r\n','') #replace is used to remove unwanted strings from the 'message'
message=message.replace('pa:Process>\r\n','pa:Process>')
print (message)
print ('******Parsing starts*************')
parser = etree.XMLParser(remove_blank_text=True) #the name space is removed here
root = etree.fromstring(message, parser) #parsing of xml happens here
print ('******Parsing completed************')
dict={}
for child in root: # parsed xml is iterated using a for loop and values are stored in a dictionary
print(child.tag,child.text)
print('****Derving from xml tree*****')
if child.tag =="{http://xmlns.abc.com}firsttag":
dict["FIRST_TAG"]=child.text
print(dict)
### output
'''************message coversion and parsing starts*************
<pa:Process xmlns:pa="http://xmlns.abc.com">
<pa:firsttag>SAMPLE</pa:firsttag></pa:Process>
******Parsing starts*************
******Parsing completed************
{http://xmlns.abc.com}firsttag SAMPLE
****Derving from xml tree*****
{'FIRST_TAG': 'SAMPLE'}'''
Antwoord 17
Als je geen externe bibliotheken of tools van derden wilt gebruiken, probeer dan onderstaande code.
- Hiermee wordt
xml
geparseerd in pythondictionary
- Hiermee worden ook xml-kenmerken geparseerd
- Hiermee worden ook lege tags zoals
<tag/>
en tags met alleen attributen zoals<tag var=val/>
geparseerd
Code
import re
def getdict(content):
res=re.findall("<(?P<var>\S*)(?P<attr>[^/>]*)(?:(?:>(?P<val>.*?)</(?P=var)>)|(?:/>))",content)
if len(res)>=1:
attreg="(?P<avr>\S+?)(?:(?:=(?P<quote>['\"])(?P<avl>.*?)(?P=quote))|(?:=(?P<avl1>.*?)(?:\s|$))|(?P<avl2>[\s]+)|$)"
if len(res)>1:
return [{i[0]:[{"@attributes":[{j[0]:(j[2] or j[3] or j[4])} for j in re.findall(attreg,i[1].strip())]},{"$values":getdict(i[2])}]} for i in res]
else:
return {res[0]:[{"@attributes":[{j[0]:(j[2] or j[3] or j[4])} for j in re.findall(attreg,res[1].strip())]},{"$values":getdict(res[2])}]}
else:
return content
with open("test.xml","r") as f:
print(getdict(f.read().replace('\n','')))
Voorbeeldinvoer
<details class="4b" count=1 boy>
<name type="firstname">John</name>
<age>13</age>
<hobby>Coin collection</hobby>
<hobby>Stamp collection</hobby>
<address>
<country>USA</country>
<state>CA</state>
</address>
</details>
<details empty="True"/>
<details/>
<details class="4a" count=2 girl>
<name type="firstname">Samantha</name>
<age>13</age>
<hobby>Fishing</hobby>
<hobby>Chess</hobby>
<address current="no">
<country>Australia</country>
<state>NSW</state>
</address>
</details>
Uitvoer(verfraaid)
[
{
"details": [
{
"@attributes": [
{
"class": "4b"
},
{
"count": "1"
},
{
"boy": ""
}
]
},
{
"$values": [
{
"name": [
{
"@attributes": [
{
"type": "firstname"
}
]
},
{
"$values": "John"
}
]
},
{
"age": [
{
"@attributes": []
},
{
"$values": "13"
}
]
},
{
"hobby": [
{
"@attributes": []
},
{
"$values": "Coin collection"
}
]
},
{
"hobby": [
{
"@attributes": []
},
{
"$values": "Stamp collection"
}
]
},
{
"address": [
{
"@attributes": []
},
{
"$values": [
{
"country": [
{
"@attributes": []
},
{
"$values": "USA"
}
]
},
{
"state": [
{
"@attributes": []
},
{
"$values": "CA"
}
]
}
]
}
]
}
]
}
]
},
{
"details": [
{
"@attributes": [
{
"empty": "True"
}
]
},
{
"$values": ""
}
]
},
{
"details": [
{
"@attributes": []
},
{
"$values": ""
}
]
},
{
"details": [
{
"@attributes": [
{
"class": "4a"
},
{
"count": "2"
},
{
"girl": ""
}
]
},
{
"$values": [
{
"name": [
{
"@attributes": [
{
"type": "firstname"
}
]
},
{
"$values": "Samantha"
}
]
},
{
"age": [
{
"@attributes": []
},
{
"$values": "13"
}
]
},
{
"hobby": [
{
"@attributes": []
},
{
"$values": "Fishing"
}
]
},
{
"hobby": [
{
"@attributes": []
},
{
"$values": "Chess"
}
]
},
{
"address": [
{
"@attributes": [
{
"current": "no"
}
]
},
{
"$values": [
{
"country": [
{
"@attributes": []
},
{
"$values": "Australia"
}
]
},
{
"state": [
{
"@attributes": []
},
{
"$values": "NSW"
}
]
}
]
}
]
}
]
}
]
}
]
Antwoord 18
Als de bron een xml-bestand is, zeg dan zoals dit voorbeeld
<pa:Process xmlns:pa="http://sssss">
<pa:firsttag>SAMPLE</pa:firsttag>
</pa:Process>
je kunt de volgende code proberen
from lxml import etree, objectify
metadata = 'C:\\Users\\PROCS.xml' # this is sample xml file the contents are shown above
parser = etree.XMLParser(remove_blank_text=True) # this line removes the name space from the xml in this sample the name space is --> http://sssss
tree = etree.parse(metadata, parser) # this line parses the xml file which is PROCS.xml
root = tree.getroot() # we get the root of xml which is process and iterate using a for loop
for elem in root.getiterator():
if not hasattr(elem.tag, 'find'): continue # (1)
i = elem.tag.find('}')
if i >= 0:
elem.tag = elem.tag[i+1:]
dict={} # a python dictionary is declared
for elem in tree.iter(): #iterating through the xml tree using a for loop
if elem.tag =="firsttag": # if the tag name matches the name that is equated then the text in the tag is stored into the dictionary
dict["FIRST_TAG"]=str(elem.text)
print(dict)
Uitvoer zou zijn
{'FIRST_TAG': 'SAMPLE'}