Как обрабатывать XML в Python?

У меня много строк в базе данных, содержащей xml, и я пытаюсь написать Python script, который будет проходить через эти строки и подсчитывать количество экземпляров определенного атрибута node. Например, мое дерево выглядит следующим образом:

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

Как я могу получить доступ к атрибутам 1 и 2 в XML с помощью Python?

Ответы

Ответ 1

Я предлагаю ElementTree. В самой стандартной библиотеке Python есть другие совместимые реализации того же API, такие как lxml и cElementTree; но в этом контексте они в основном добавляют еще большую скорость - легкость программирования зависит от API, который определяет ElementTree.

Сначала создайте root экземпляра Element из XML, например, с помощью функции XML, или проанализируйте файл с помощью чего-то вроде:

import xml.etree.ElementTree as ET
root = ET.parse('thefile.xml').getroot()

Или любым другим способом, показанным на ElementTree. Затем сделайте что-то вроде:

for type_tag in root.findall('bar/type'):
    value = type_tag.get('foobar')
    print(value)

И похожие, обычно довольно простые, шаблоны кода.

Ответ 2

minidom является самым быстрым и довольно прямым:

XML:

<data>
    <items>
        <item name="item1"></item>
        <item name="item2"></item>
        <item name="item3"></item>
        <item name="item4"></item>
    </items>
</data>

ПИТОН:

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)

ВЫХОД

4
item1
item1
item2
item3
item4

Ответ 3

Вы можете использовать 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'

Ответ 4

Есть много вариантов. cElementTree отлично смотрится, если проблема и скорость использования памяти. Он имеет очень мало накладных расходов по сравнению с простым чтением в файле с использованием readlines.

Соответствующие показатели можно найти в таблице ниже, скопированной с веб-сайта 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   

Как указано @jfs, cElementTree поставляется в комплекте с Python:

  • Python 2: from xml.etree import cElementTree as ElementTree.
  • Python 3: from xml.etree import ElementTree (ускоренная версия C используется автоматически).

Ответ 5

Я предлагаю xmltodict для простоты.

Он анализирует ваш xml на 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')])])])

Ответ 6

lxml.objectify действительно прост.

Взяв ваш образец текста:

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)

Вывод:

{'1': 1, '2': 1}

Ответ 7

Python имеет интерфейс с парсером expat xml.

xml.parsers.expat

Это недействительный синтаксический анализатор, поэтому плохой xml не будет пойман. Но если вы знаете, что ваш файл верен, то это очень хорошо, и вы, вероятно, получите точную информацию, которую хотите, и можете отбросить остальное на лету.

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

Ответ 8

Я все еще новичок Python, но, как мне кажется, ElementTree - это современное состояние в Python Разбор XML и обработка.

Марк Пилигрим хороший раздел по анализу XML с ElementTree в своей книге Погружение в Python 3.

Ответ 9

Чтобы добавить еще одну возможность, вы можете использовать unangle, так как это простая библиотека объектов xml-to-python. Здесь у вас есть пример:

Установка

pip install untangle

Использование

Ваш xml файл (немного изменился):

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

доступ к атрибутам с unangle:

import untangle

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

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

вывод будет:

bar_name
1

Более подробную информацию о unangle можно найти здесь. Также (если вам интересно), вы можете найти список инструментов для работая с XML и Python здесь (вы также увидите, что наиболее распространенные из них были упомянуты в предыдущих ответах).

Ответ 10

Я мог бы предложить declxml.

Полное раскрытие: я написал эту библиотеку, потому что я искал способ конвертировать между структурами данных XML и Python без необходимости писать десятки строк императивного кода синтаксического анализа/сериализации с помощью ElementTree.

С помощью declxml вы используете процессоры для декларативного определения структуры вашего XML-документа и способа сопоставления между структурами данных XML и Python. Процессоры используются как для сериализации, так и для синтаксического анализа, а также для базового уровня проверки.

Анализ в структурах данных Python прост:

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)

Что дает результат:

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

Вы также можете использовать один и тот же процессор для сериализации данных в XML

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

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

Которая производит следующий выход

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

Если вы хотите работать с объектами вместо словарей, вы можете определить процессоры для преобразования данных в объекты и из них.

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)

Которая производит следующий выход

{'bar': Bar(foobars=[1, 2])}

Ответ 11

Здесь очень простой, но эффективный код с использованием 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")

Источник:

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

Ответ 12

Я нахожу Python xml.dom и xml.dom.minidom довольно просто. Имейте в виду, что DOM не подходит для больших объемов XML, но если ваш вход довольно мал, это будет работать нормально.

Ответ 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')

Это будет печатать значение атрибута foobar.

Ответ 14

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

ВЫВОД:

foo
1
2

Ответ 15

Вы серьезно?

Как насчет проблем безопасности? Используйте defusedxml.

Это также рекомендуется Два сокета Django.

Сравнение файлов defusedxml и других библиотек

Lxml защищен от миллиардов атак смеха и не делает сетевой поиск по умолчанию.

libxml2 и lxml не являются непосредственно уязвимыми для декомпрессии gzip бомбы, но они не защищают вас от них.

xml.etree не расширяет сущности и вызывает ParserError, когда объект.

minidom не расширяет сущности и просто возвращает нерасширенный сущность дословно.

genshi.input of genshi 0.6 не поддерживает сущность расширения и повышает ParserError, когда происходит сущность.

Библиотека имеет (ограниченную) поддержку XInclude, но требует дополнительного шага для включения процесса.

Ответ 16

xml.etree.ElementTree против lxml

Вот некоторые плюсы двух наиболее часто используемых библиотек, которые мне было бы полезно узнать, прежде чем выбирать между ними.

xml.etree.ElementTree:

  1. Из стандартной библиотеки: нет необходимости устанавливать какой-либо модуль

LXML

  1. Легко написать декларацию XML: например, вам нужно добавить standalone="no"?
  2. Хорошая печать: у вас может быть хороший отступ XML без лишнего кода.
  3. Функциональность Objectify: позволяет использовать XML так, как если бы вы имели дело с обычной иерархией объектов Python .node.

Ответ 17

rec.xml: -

<?xml version="1.0"?>
<nodes>
    <node name="Car" child="Engine"></node>
    <node name="Engine" child="Piston"></node>
    <node name="Engine" child="Carb"></node>
    <node name="Car" child="Wheel"></node>
    <node name="Wheel" child="Hubcaps"></node>
    <node name="Truck" child="Engine"></node>
    <node name="Truck" child="Loading Bin"></node>
    <node name="Piston" child="Loa"></node>
    <node name="Spare Wheel" child=""></node>
</nodes>

par.py: -

import xml.etree.ElementTree as ET
tree = ET.parse('rec.xml')
root = tree.getroot()

for nodes in root.findall('node'):
    parent = nodes.attrib.get('name')
    child = nodes.attrib.get('child')
    print parent,child