提问人:randombits 提问时间:12/16/2009 最后编辑:Mateen Ulhaqrandombits 更新时间:5/21/2023 访问量:1555740
如何解析XML并获取特定节点属性的实例?
How to parse XML and get instances of a particular node attribute?
问:
我在XML中有很多行,我正在尝试获取特定节点属性的实例。
<foo>
<bar>
<type foobar="1"/>
<type foobar="2"/>
</bar>
</foo>
如何访问属性的值?在此示例中,我想要 和 。foobar
"1"
"2"
答:
您可以使用 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'
评论
BeautifulStoneSoup
BeautifulSoup(source_xml, features="xml")
ElementTree
BeautifulSoup
我建议ElementTree
。同一 API 还有其他兼容的实现,例如 lxml
,以及 Python 标准库本身;但是,在这种情况下,他们主要增加的是更高的速度——编程的难易程度部分取决于 API,它定义了 API。cElementTree
ElementTree
首先从 XML 构建一个 Element 实例,例如使用 XML 函数,或者使用类似的东西解析文件:root
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)
输出:
1
2
评论
lxml
增加的不仅仅是速度。它提供了对父节点、XML 源中的行号等信息的轻松访问,这些信息在多种情况下非常有用。
Warning The xml.etree.ElementTree module is not secure against maliciously constructed data. If you need to parse untrusted or unauthenticated data see XML vulnerabilities.
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
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
dom = minidom.parse('items.xml')
elements = dom.getElementsByTagName('item')
print(f"There are {len(elements)} items:")
for element in elements:
print(element.attributes['name'].value)
输出:
There are 4 items:
item1
item2
item3
item4
评论
item
data->items
data->secondSetOfItems
item
item
for s in itemlist: print(s.attributes['name'].value)
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}
评论
count
使用默认键将每个项目的计数存储在字典中,因此您不必检查成员资格。您也可以尝试查看 。collections.Counter
这里有一个非常简单但有效的代码,使用 .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 parse”。
那里有很多选择。如果速度和内存使用率是一个问题,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 所指出的,它与 Python 捆绑在一起:cElementTree
- Python 2:.
from xml.etree import cElementTree as ElementTree
- Python 3:(自动使用加速 C 版本)。
from xml.etree import ElementTree
评论
from xml.etree import cElementTree as ElementTree
from xml.etree import ElementTree
ElementTree
minidom
为了简单起见,我建议使用 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')])])])
评论
result["foo"]["bar"]["type"]
<type>
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
为了增加另一种可能性,您可以使用 untangle,因为它是一个简单的 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
有关解开的更多信息,请参见“解开”。
此外,如果您好奇,可以在“Python 和 XML”中找到用于处理 XML 和 Python 的工具列表。您还将看到之前的答案中提到了最常见的问题。
评论
我可能会建议 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])}
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
xml.etree.ElementTree 与 lxml
这些是两个最常用的库的一些优点,在它们之间进行选择之前,我会很高兴了解它们。
xml.etree.ElementTree:
- 来自标准库:无需安装任何模块
lxml 格式
- 轻松编写XML声明:例如,您需要添加吗?
standalone="no"
- 漂亮的打印:你可以有一个漂亮的缩进XML,没有额外的代码。
- 客观化功能:它允许您像处理普通的 Python 对象层次结构一样使用 XML。
.node
sourceline
允许轻松获取您正在使用的 XML 元素的行。- 还可以使用内置的 XSD 架构检查器。
如果您使用 .只需从 XML 初始化一个新实例并轻松管理它,因为它是一个子类。python-benedict
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'])
它支持多种格式的 I/O 操作并规范化:、、、、和 。Base64
CSV
JSON
TOML
XML
YAML
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'}'''
评论
如果源是 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'}
如果您不想使用任何外部库或第三方工具,请尝试以下代码。
- 这将解析为 python
xml
dictionary
- 这也将解析 xml 属性
- 这也将解析空标签,例如 和仅具有
<tag/>
<tag var=val/>
法典
import re
def getdict(content):
res=re.findall("<(?P<var>\S*)(?P<attr>[^/>]*)(?:(?:>(?P<val>.*?)</(?P=var)>)|(?:/>))",content)
if len(res)>=1:
attreg="(?P<avr>\S+?)(?:(?:=(?P<quote>['\"])(?P<avl>.*?)(?P=quote))|(?:=(?P<avl1>.*?)(?:\s|$))|(?P<avl2>[\s]+)|$)"
if len(res)>1:
return [{i[0]:[{"@attributes":[{j[0]:(j[2] or j[3] or j[4])} for j in re.findall(attreg,i[1].strip())]},{"$values":getdict(i[2])}]} for i in res]
else:
return {res[0]:[{"@attributes":[{j[0]:(j[2] or j[3] or j[4])} for j in re.findall(attreg,res[1].strip())]},{"$values":getdict(res[2])}]}
else:
return content
with open("test.xml","r") as f:
print(getdict(f.read().replace('\n','')))
示例输入
<details class="4b" count=1 boy>
<name type="firstname">John</name>
<age>13</age>
<hobby>Coin collection</hobby>
<hobby>Stamp collection</hobby>
<address>
<country>USA</country>
<state>CA</state>
</address>
</details>
<details empty="True"/>
<details/>
<details class="4a" count=2 girl>
<name type="firstname">Samantha</name>
<age>13</age>
<hobby>Fishing</hobby>
<hobby>Chess</hobby>
<address current="no">
<country>Australia</country>
<state>NSW</state>
</address>
</details>
输出(美化)
[
{
"details": [
{
"@attributes": [
{
"class": "4b"
},
{
"count": "1"
},
{
"boy": ""
}
]
},
{
"$values": [
{
"name": [
{
"@attributes": [
{
"type": "firstname"
}
]
},
{
"$values": "John"
}
]
},
{
"age": [
{
"@attributes": []
},
{
"$values": "13"
}
]
},
{
"hobby": [
{
"@attributes": []
},
{
"$values": "Coin collection"
}
]
},
{
"hobby": [
{
"@attributes": []
},
{
"$values": "Stamp collection"
}
]
},
{
"address": [
{
"@attributes": []
},
{
"$values": [
{
"country": [
{
"@attributes": []
},
{
"$values": "USA"
}
]
},
{
"state": [
{
"@attributes": []
},
{
"$values": "CA"
}
]
}
]
}
]
}
]
}
]
},
{
"details": [
{
"@attributes": [
{
"empty": "True"
}
]
},
{
"$values": ""
}
]
},
{
"details": [
{
"@attributes": []
},
{
"$values": ""
}
]
},
{
"details": [
{
"@attributes": [
{
"class": "4a"
},
{
"count": "2"
},
{
"girl": ""
}
]
},
{
"$values": [
{
"name": [
{
"@attributes": [
{
"type": "firstname"
}
]
},
{
"$values": "Samantha"
}
]
},
{
"age": [
{
"@attributes": []
},
{
"$values": "13"
}
]
},
{
"hobby": [
{
"@attributes": []
},
{
"$values": "Fishing"
}
]
},
{
"hobby": [
{
"@attributes": []
},
{
"$values": "Chess"
}
]
},
{
"address": [
{
"@attributes": [
{
"current": "no"
}
]
},
{
"$values": [
{
"country": [
{
"@attributes": []
},
{
"$values": "Australia"
}
]
},
{
"state": [
{
"@attributes": []
},
{
"$values": "NSW"
}
]
}
]
}
]
}
]
}
]
}
]
评论
simplified_scrapy
:一个新的库,我用完就爱上了它。我推荐给你。
from simplified_scrapy import SimplifiedDoc
xml = '''
<foo>
<bar>
<type foobar="1"/>
<type foobar="2"/>
</bar>
</foo>
'''
doc = SimplifiedDoc(xml)
types = doc.selects('bar>type')
print (len(types)) # 2
print (types.foobar) # ['1', '2']
print (doc.selects('bar>type>foobar()')) # ['1', '2']
这里有更多例子。这个库很容易使用。
我是伤员,没有人建议熊猫。Pandas 有一个函数,非常适合这种扁平的 xml 结构。read_xml()
import pandas as pd
xml = """<foo>
<bar>
<type foobar="1"/>
<type foobar="2"/>
</bar>
</foo>"""
df = pd.read_xml(xml, xpath=".//type")
print(df)
输出:
foobar
0 1
1 2
使用 iterparse(),您可以捕获标签属性字典值:
import xml.etree.ElementTree as ET
from io import StringIO
xml = """<foo>
<bar>
<type foobar="1"/>
<type foobar="2"/>
</bar>
</foo>
"""
file = StringIO(xml)
for event, elem in ET.iterparse(file, ("end",)):
if event == "end" and elem.tag == "type":
print(elem.attrib["foobar"])
评论