Jak analizować XML w Pythonie?

Mam wiele wierszy w bazie danych, która zawiera xml i próbuję napisać skrypt Pythona, który przejdzie przez te wiersze i policzy, ile instancji danego atrybutu węzła się pojawi. Na przykład Moje drzewo wygląda tak:

<foo>
   <bar>
      <type foobar="1"/>
      <type foobar="2"/>
   </bar>
</foo>

Jak mogę uzyskać dostęp do atrybutów 1 i 2 w XML używając Pythona?

 776
Author: Martin Thoma, 2009-12-16

13 answers

Proponuję ElementTree. Istnieją inne kompatybilne implementacje tego samego API, takie jak lxml, i cElementTree w samej bibliotece standardowej Pythona; ale w tym kontekście to, co dodają, to jeszcze większa szybkość - łatwość programowania zależy od API, które definiuje ElementTree.

Po zbudowaniu instancji elementu e z XML, np. za pomocą funkcji XML , lub przez parsowanie pliku za pomocą czegoś w rodzaju

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

Lub któregokolwiek z wielu inne sposoby pokazane na ElementTree, po prostu zrób coś takiego:

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

I podobne, zazwyczaj dość proste, wzorce kodu.

 593
Author: Alex Martelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-10 20:11:15

minidom jest najszybsza i całkiem prosta:

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)

Wyjście

4
item1
item1
item2
item3
item4
 381
Author: Ryan Christensen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-23 10:22:41

Możesz użyć 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'
 208
Author: YOU,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-08 16:16:50

Istnieje wiele opcji. cElementTree wygląda doskonale, jeśli szybkość i zużycie pamięci są problemem. Ma bardzo mało narzutu w porównaniu do zwykłego czytania w pliku za pomocą readlines.

Odpowiednie metryki można znaleźć w poniższej tabeli, skopiowanej ze strony internetowej 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   

Jak zauważył @jfs, cElementTree jest w pakiecie z Pythonem:

  • Python 2: from xml.etree import cElementTree as ElementTree.
  • Python 3: from xml.etree import ElementTree (używana jest przyspieszona Wersja C automatycznie).
 78
Author: Cyrus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-31 05:44:43

Lxml.uprzedmiotowienie jest naprawdę proste.

Pobranie przykładowego tekstu:

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)

Wyjście:

{'1': 1, '2': 1}
 36
Author: Ryan Ginstrom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-07 08:15:54

Proponuję xmltodict dla uproszczenia.

Parsuje Twój xml do 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')])])])
 31
Author: myildirim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-13 07:02:11

Python posiada interfejs do expat XML parser.

xml.parsers.expat

To nie walidujący parser, więc zły xml nie zostanie przechwycony. Ale jeśli wiesz, że Twój plik jest poprawny, to jest to całkiem dobre i prawdopodobnie uzyskasz dokładne informacje, które chcesz, a resztę możesz odrzucić w locie.

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
 18
Author: Tor Valamo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-16 08:47:08

Tutaj bardzo prosty, ale skuteczny kod z użyciem 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")

Źródło:

http://www.snip2code.com/Snippet/991/python-xml-parse?fromPage=1

 8
Author: Jan Kohila,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-09 20:09:34

Aby dodać kolejną możliwość, możesz użyć untangle, ponieważ jest to prosta biblioteka obiektów XML-to-python. Oto przykład:

Instalacja

pip install untangle

Użycie

Twój plik xml (trochę zmieniony):

<foo>
   <bar name="bar_name">
      <type foobar="1"/>
   </bar>
</foo>

Dostęp do atrybutów za pomocą untangle :

import untangle

obj = untangle.parse('/path_to_xml_file/file.xml')

print obj.foo.bar['name']
print obj.foo.bar.type['foobar']

Wyjście będzie:

bar_name
1

Więcej informacji o untangle można znaleźć tutaj .
również (jeśli jesteś ciekawy), można znaleźć lista narzędzi do pracy z XML i Pythonem tutaj (zobaczysz również, że najczęstsze były wymienione w poprzednich odpowiedziach).

 8
Author: jessag,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-17 09:23:13

Proponuję declxml .

Pełne ujawnienie: napisałem tę bibliotekę, ponieważ szukałem sposobu na konwersję między strukturami danych XML i Python bez konieczności pisania dziesiątek linii imperatywnego kodu parsującego / serializującego za pomocą ElementTree.

W declxml, używasz procesorów do deklaratywnego zdefiniowania struktury dokumentu XML i sposobu mapowania struktur danych XML i Pythona. Procesory służą zarówno do serializacji jak i parsowanie, jak również dla podstawowego poziomu walidacji.

Parsowanie do struktur danych Pythona jest proste:

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)

Który wytwarza wyjście:

{'bar': {'foobar': [1, 2]}}

Możesz również użyć tego samego procesora do serializacji danych do XML

data = {'bar': {
    'foobar': [7, 3, 21, 16, 11]
}}

xml.serialize_to_string(processor, data, indent='    ')

, który wytwarza następujące wyjście

<?xml version="1.0" ?>
<foo>
    <bar>
        <type foobar="7"/>
        <type foobar="3"/>
        <type foobar="21"/>
        <type foobar="16"/>
        <type foobar="11"/>
    </bar>
</foo>

Jeśli chcesz pracować z obiektami zamiast ze słownikami, możesz zdefiniować procesory do przekształcania danych do i z obiektów.

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)

, który wytwarza następujące wyjście

{'bar': Bar(foobars=[1, 2])}
 8
Author: gatkin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-04 17:40:26

Znajduję Python xml.dom i xml.dom.minidom całkiem proste. Należy pamiętać, że DOM nie jest dobry dla dużych ilości XML, ale jeśli Dane wejściowe są dość małe, to będzie działać dobrze.

 6
Author: EMP,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-16 05:28:55
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')

Wyświetli wartość atrybutu foobar.

 5
Author: Souvik Dey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-20 15:56:58

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

Wyjście:

foo
1
2
 2
Author: Ahito,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-09 09:02:14