Как обрабатывать 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:
- Из стандартной библиотеки: нет необходимости устанавливать какой-либо модуль
LXML
- Легко написать декларацию XML: например, вам нужно добавить
standalone="no"
? - Хорошая печать: у вас может быть хороший отступ XML без лишнего кода.
- Функциональность 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