Hoe kan ik XML ontleden en instanties van een bepaald knooppuntattribuut tellen?

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 lxmlen cElementTreein de Python-standaardbibliotheek zelf; maar in deze context voegen ze vooral nog meer snelheid toe — het programmeergemak hangt af van de API, die ElementTreedefinieert.

Bouw eerst een Element-instantie rootop 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%

minidomis 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, cElementTreewordt 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:

  1. Van de standaardbibliotheek: u hoeft geen enkele module te installeren

lxml

  1. Gemakkelijk XML-declaratieschrijven: moet u bijvoorbeeld standalone="no"toevoegen?
  2. Mooi printen: je kunt een mooie ingesprongenXML hebben zonder extra code.
  3. Objectify-functionaliteit: hiermee kunt u XML gebruiken alsof u te maken hebt met een normale Python-objecthiërarchie.node.
  4. Met

  5. sourcelinekunt u eenvoudig de regel van het XML-element dat u gebruikt ophalen.
  6. u kunt ook een ingebouwde XSD-schemacontrole gebruiken.

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 foobarafgedrukt.


Antwoord 14

Het is niet nodig om een lib-specifieke API te gebruikenals je python-benedictgebruikt. Initialiseer gewoon een nieuwe instantie vanuit uw XML en beheer deze eenvoudig omdat het een dictsubklasse 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, YAMLen 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 xmlgeparseerd in python dictionary
  • 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'}

Other episodes