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

Мое дерево выглядит так:

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

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

1110
randombits 16 Дек 2009 в 08:09

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)

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

753
Mig B 13 Дек 2019 в 00:39

Если источником является файл 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'}
-1
Siraj 20 Фев 2020 в 12:46

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}
37
sandy 7 Июн 2013 в 08:15

Просто чтобы добавить еще одну возможность, вы можете использовать распутать , так как это простая библиотека 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 ". Вы также увидите, что самые распространенные из них были упомянуты в предыдущих ответах.

13
the Tin Man 12 Дек 2019 в 23:26

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
6
the Tin Man 12 Дек 2019 в 23:58

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

10
the Tin Man 12 Дек 2019 в 23:28

Вы можете использовать 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'
234
the Tin Man 12 Дек 2019 в 23:18

Нет необходимости использовать 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.

2
Fabio Caccamo 16 Янв 2020 в 09:22
#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'}'''
0
Siraj 20 Фев 2020 в 12:56

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
423
the Tin Man 12 Дек 2019 в 23:18

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

5
EMP 16 Дек 2009 в 05:28

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
20
the Tin Man 12 Дек 2019 в 23:20

Есть много вариантов там. 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 используется автоматически).
97
Stevoisiak 31 Янв 2018 в 05:44
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.

5
the Tin Man 12 Дек 2019 в 23:57

Я предлагаю 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')])])])
43
the Tin Man 12 Дек 2019 в 23:19

Я мог бы предложить 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])}
13
gatkin 4 Сен 2017 в 17:40