如何在Python中parsingXML?

我在包含xml的数据库中有很多行,我试图编写一个Python脚本,它将通过这些行并计算出一个特定节点属性的实例数量。 例如,我的树看起来像:

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

我如何使用Python访问XML中的属性1和2?

我build议ElementTree 。 还有其他兼容的API实现,比如Python标准库本身的lxmlcElementTree ; 但在这种情况下,他们主要添加的是更快的速度 – 编程部分的轻松取决于ElementTree定义的API。

在从XML构buildElement实例之后,例如使用XML函数,或者通过类似的方式parsing文件

 import xml.etree.ElementTree e = xml.etree.ElementTree.parse('thefile.xml').getroot() 

或者在ElementTree显示的其他任何方式,您只需执行以下操作:

 for atype in e.findall('type'): print(atype.get('foobar')) 

和类似的,通常很简单的代码模式。

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) 

OUTPUT

 4 item1 item1 item2 item3 item4 

你可以使用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' 

那里有很多select。 如果速度和内存使用率是一个问题, cElementTree看起来非常好。 与使用readlines简单读取文件相比,它的开销非常小。

相关指标可以从cElementTree网站复制下表中find:

 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 

正如@ jf-sebastian指出的那样, cElementTree绑定了python:“在Python 2上: from xml.etree import cElementTree as ElementTree 。在Python 3上: from xml.etree import ElementTree (加速的C版本自动使用)。

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} 

为了简单,我build议使用xmltodict 。

它parsing你的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')])])]) 

Python有一个expat xmlparsing器的接口。

 xml.parsers.expat 

这是一个非validation的parsing器,所以不好的xml不会被捕获。 但是,如果你知道你的文件是正确的,那么这是非常好的,你可能会得到你想要的确切信息,你可以放弃其余的dynamic。

 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 

我自己还是一个Python新手,但我的印象是, ElementTree是Python XMLparsing和处理的最新技术。

Mark Pilgrim在他的书“ 潜入Python 3”一书中有关于用ElementTreeparsingXML 的很好的章节 。

这里使用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

我发现python xml.domxml.dom.minidom很容易。 请记住,DOM不适合大量的XML,但如果您的input相当小,那么这将工作正常。

为了增加另一种可能性,可以使用unangle ,因为它是一个简单的xml-to-python-object库。 这里有一个例子:

安装

 pip install untangle 

用法

你的XML文件(有点改变):

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

访问属性与解决

 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 

更多关于解开信息可以在这里find。
另外(如果你很好奇),你可以在这里find一个用于处理XML和Python的工具列表(你也可以看到最常见的是以前的答案)。

 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属性的值。

你是认真的吗 ?

安全问题呢? 使用defusedxml 。

这也是Django的两个推荐。

关于defusedxml和其他库的比较

Lxml可以防止百万笑的攻击,默认情况下不会进行networking查找。

libxml2和lxml不会直接受到gzip解压缩炸弹的攻击,但它们也不能保护你免受它们的攻击。

xml.etree不会扩展实体,并在实体出现时引发ParserError。

minidom不扩展实体,只是简单地返回未扩展的实体。

genshi 0.6的genshi.input不支持实体扩展,并在实体发生时引发ParserError。

图书馆有(有限)XInclude支持,但需要额外的步骤来处理包含。

我可能会build议declxml 。

完全公开:我写了这个库,因为我正在寻找一种在XML和Python数据结构之间进行转换的方法,而无需使用ElementTree编写数十行命令式parsing/序列化代码。

使用declxml,您可以使用处理器声明性地定义XML文档的结构以及如何在XML和Python数据结构之间进行映射。 处理器用于序列化和parsing以及基本的validation级别。

parsingPython数据结构非常简单:

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

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