如何在Python中parsingXML?
我在包含xml的数据库中有很多行,我试图编写一个Python脚本,它将通过这些行并计算出一个特定节点属性的实例数量。 例如,我的树看起来像:
<foo> <bar> <type foobar="1"/> <type foobar="2"/> </bar> </foo>
我如何使用Python访问XML中的属性1和2?
我build议ElementTree
。 还有其他兼容的API实现,比如Python标准库本身的lxml
和cElementTree
; 但在这种情况下,他们主要添加的是更快的速度 – 编程部分的轻松取决于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.dom和xml.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