У меня есть много строк в базе данных, содержащей XML, и я пытаюсь написать скрипт Python для подсчета экземпляров определенного атрибута узла.
Мое дерево выглядит так:
<foo>
<bar>
<type foobar="1"/>
<type foobar="2"/>
</bar>
</foo>
Как я могу получить доступ к атрибутам "1"
и "2"
в XML с помощью Python?
16 ответов
Я предлагаю ElementTree
. Существуют другие совместимые реализации того же API, такие как lxml
и {{X2} } в самой стандартной библиотеке Python; но в этом контексте то, что они в основном добавляют, - это еще большая скорость - простота программирования зависит от API, который ElementTree
определяет.
Сначала создайте экземпляр элемента root
из 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)
И похожие, обычно довольно простые, шаблоны кода.
Если источником является файл XML, скажем, как этот пример
<pa:Process xmlns:pa="http://sssss">
<pa:firsttag>SAMPLE</pa:firsttag>
</pa:Process>
Вы можете попробовать следующий код
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)
Выход будет
{'FIRST_TAG': 'SAMPLE'}
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}
Просто чтобы добавить еще одну возможность, вы можете использовать распутать , так как это простая библиотека xml-to-python-object. Вот вам пример:
Установка:
pip install untangle
Применение:
Ваш XML-файл (немного изменен):
<foo>
<bar name="bar_name">
<type foobar="1"/>
</bar>
</foo>
Доступ к атрибутам с помощью untangle
:
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
Дополнительную информацию о распутывании можно найти в разделе распутать.
Кроме того, если вам интересно, вы можете найти список инструментов для работы с XML и Python в "Python и XML ". Вы также увидите, что самые распространенные из них были упомянуты в предыдущих ответах.
XML:
<foo>
<bar>
<type foobar="1"/>
<type foobar="2"/>
</bar>
</foo>
Код Python:
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
Здесь очень простой, но эффективный код, использующий 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")
Это из "анализа python xml".
Вы можете использовать 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'
Нет необходимости использовать API, специфичный для lib , если вы используете python-benedict
. Просто инициализируйте новый экземпляр из вашего XML и управляйте им легко, так как это подкласс dict
.
Установка проста: 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'])
Он поддерживает и нормализует операции ввода-вывода во многих форматах: Base64
, CSV
, JSON
, TOML
, XML
, { {X5}} и query-string
.
Он хорошо протестирован и имеет открытый исходный код на GitHub.
#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'}'''
minidom
- самый быстрый и довольно простой.
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)
Выход:
4
item1
item1
item2
item3
item4
Я нахожу Python xml.dom и xml .dom.minidom довольно просто. Имейте в виду, что DOM не подходит для больших объемов XML, но если ваш ввод довольно мал, это будет работать нормально.
Python имеет интерфейс для синтаксического анализатора 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
Есть много вариантов там. 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 используется автоматически).
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
.
Я предлагаю 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')])])])
Я мог бы предложить 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])}
Похожие вопросы
Новые вопросы
python
Python - это многопарадигмальный, динамически типизированный, многоцелевой язык программирования. Он разработан для быстрого изучения, понимания и использования, а также для обеспечения чистого и единообразного синтаксиса. Обратите внимание, что Python 2 официально не поддерживается с 01.01.2020. Тем не менее, для вопросов о Python, связанных с версией, добавьте тег [python-2.7] или [python-3.x]. При использовании варианта Python (например, Jython, PyPy) или библиотеки (например, Pandas и NumPy) включите его в теги.