您的位置:首页 > 编程语言 > Python开发

python中解析xml

2012-12-18 22:39 344 查看
转自:http://jordy.easymorse.com/?p=442

通过MiniDom库解析xml文件

通过ElementTree库解析xml文件

MiniDom方式解析xml

xml文件以data.xml为例,具体操作如下:

data.xml:

<?xml version="1.0" encoding="utf-8"?>
<info>
<intro>保存用户的信息</intro>
<list id='001'>
<head>auto_userone</head>
<name>Jordy</name>
<number>12345678</number>
<age>20</age>
<sex>男</sex>
<hobby>上网</hobby>
</list>

<list id='002'>
<head>auto_usertwo</head>
<name>功夫</name>
<number>34443678</number>
<age>18</age>
<sex>男</sex>
<hobby>功夫</hobby>
</list>

</info>



1. 得到DOM对象

DOM是Document Object Model的简称,它是以对象树来表示一个XML。

import xml.dom.minidom

#得到dom对象

dom = xml.dom.minidom.parse("data.xml")

2. 得到文档元素对象

#得到文档元素对象

root = dom.documentElement #这里得到的是根节点info

#打印根节点的 名字 节点的值 节点类型

print root.nodeName,root.nodeValue,root.nodeType

节点的属性:

每一个节点都有它的nodeName,nodeValue,nodeType属性。nodeName为节点名字。

nodeValue是节点的值,只对文本节点有效。nodeType是节点的类型,现在有以下几种:

‘ATTRIBUTE_NODE’
‘CDATA_SECTION_NODE’
‘COMMENT_NODE’
‘DOCUMENT_FRAGMENT_NODE’
‘DOCUMENT_NODE’
‘DOCUMENT_TYPE_NODE’
‘ELEMENT_NODE’
‘ENTITY_NODE’
‘ENTITY_REFERENCE_NODE’
‘NOTATION_NODE’
‘PROCESSING_INSTRUCTION_NODE’
‘TEXT_NODE’


3.子元素、子节点的访问

对于已经知道元素名字的子元素,可以通过使用getElementsByTagName()方法访问,例:

root.getElementsByTagName("intro") #读取intro子元素

返回的结果是一个列表。

如果要得到某元素下的所有子节点,可以使用childNodes属性:

root.childNodes

getElementsByTagName()可以搜索当前元素的所有子元素,包括所有层次的子元素。childNodes只保存了当前元素的第一层子结点。

比如:我们想要得到intro元素下的值“保存用户的信息”,实现如下代码:

node = root.getElementsByTagName("intro")[0]

for node in node.childNodes:

if node.nodeType in ( node.TEXT_NODE, node.CDATA_SECTION_NODE):

print node.data

这种方式在获取元素的文本时,需要先判断才行,所以使用起来感觉不是太方便。

ElementTree库解析xml文件

ElementTree属于python标准库的一部分,ElementTree的parse()方法是这个库的主要入口,它使用文件名或流对象作为参数,parse()方法会立即解析完整个文档,它返回的对象是整个文档的对象,而不是根元素,如果要获取根元素,可以调用getroot()方法。

使用上述的data.xml文档,通过ElementTree库解析的代码:

import xml.etree.ElementTree as ET

#读取xml文件
def load_xml_file(fileName):
root = ET.parse(fileName).getroot()

#获取文件描述
intro = root.find('intro').text
print intro

#获取所有list节点
all_users = root.findall('list')
#遍历list节点的子元素
for user in all_users:
#得到head节点的文本
head = user.find('head').text
#得到name节点的文本
name = user.find('name').text
#得到sex节点的文本
sex = user.find('sex').text
print head,name,sex

if __name__ == '__main__':

load_xml_file('data.xml')


通过这种方式解析xml文件比起使用minidom库解析xml文件的,操作要方便的多。

还有第三种方式,更高效的解析xml文件:

转自:/article/8003111.html

Python之lxml

作者:Shane

出处:http://bluescorpio.cnblogs.com

lxml takes all the pain out of XML.

Stephan Richter

lxml是Python语言里和XML以及HTML工作的功能最丰富和最容易使用的库。lxml是为libxml2和libxslt库的一个Python化的绑定。它与众不同的地方是它兼顾了这些库的速度和功能完整性,以及纯Python API的简洁性,大部分与熟知的ElementTree API兼容但比之更优越。

安装lxml:

要求:需要Python2.3或更后的版本

使用easy_install工具,以超级用户或管理员的角色run下面的命令:

easy_install lxml

在windows下,最好指定版本号:easy_install lxml==2.2.6

使用lxml进行开发

lxml.etree指南

通常使用lxml.etree的方式

>>> from lxml import etree

Element类,一个Element是ElementTree API的主要容器类,大部分的XML tree功能都是通过这个类来访问的。Elements可以非常容易地通过Element工厂方法来创建。

>>> root = etree.Element("root")

元素的XML tag名字是通过tag属性来访问的

>>> print root.tag # root

Elements是在XML树状结构中组织的,为创建子元素并将它们加到父元素上,可以使用append()方法。

>>> root.append( etree.Element("child1") )

我们还有更高效的方法:SubElement工厂方法,它使用和Element工厂方法相同的参数,不过额外需要父节点作第一个参数:

>>> child2 = etree.SubElement(root, "child2")

>>> child3 = etree.SubElement(root, "child3")

可以使用tostring()方法来看得到的XML

>>> print etree.tostring(root, pretty_print=True)

<root>

<child1/>

<child2/>

<child3/>

</root>

元素是列表

>>> child = root[0]

>>> print child.tag

child1

>>> print len(root)

3

>>> root.index(root[1]) # lxml.etree only!

1

打印所有子节点:

>>> children = list(root)

>>> for child in root:

... print(child.tag)

child1

child2

child3

可以使用insert()方法插入新的子节点:

>>> root.insert(0, etree.Element("child0"))

删除子节点:

>>> root[0] = root[-1] # this moves the element!

>>> for child in root:

... print(child.tag)

child3

child1

child2

如果想把一个元素拷贝到不同的地方,需要创建一个独立的deep copy。

>>> from copy import deepcopy

>>> element = etree.Element("neu")

>>> element.append( deepcopy(root[1]) )

>>> print(element[0].tag)

child1

>>> print([ c.tag for c in root ])

[’child3’, ’child1’, ’child2’]

getparent()返回父节点:

>>> root is root[0].getparent() # lxml.etree only!

True

元素的兄弟或邻居节点是通过next和previous属性来访问的

The siblings (or neighbours) of an element are accessed as next and previous elements:

>>> root[0] is root[1].getprevious() # lxml.etree only!

True

>>> root[1] is root[0].getnext() # lxml.etree only!

True

带属性的元素

XML元素支持属性,可以用Element工厂方法直接创建。

>>> root = etree.Element("root", interesting="totally")

>>> etree.tostring(root)

b’<root interesting="totally"/>’

可以使用set和get方法访问这些属性:

>>> print root.get("interesting")

totally

>>> root.set("interesting", "somewhat")

>>> print root.get("interesting")

somewhat

也可以使用attrib性质的字典接口

>>> attributes = root.attrib

>>> print(attributes["interesting"])

somewhat

>>> print(attributes.get("hello"))

None

>>> attributes["hello"] = "Guten Tag"

>>> print(attributes.get("hello"))

Guten Tag

>>> print(root.get("hello"))

Guten Tag

元素可以包含文字:

>>> root = etree.Element("root")

>>> root.text = "TEXT"

>>> print(root.text)

TEXT

>>> etree.tostring(root)

’<root>TEXT</root>’

如果XML用在(X)HTML中,文本也可以在不同的元素中显示:

<html><body>Hello<br/>World</body></html>

元素有tail属性,它包含XML 树中元素直接跟的,直到下个元素的文本。

>>> html = etree.Element("html")

>>> body = etree.SubElement(html, "body")

>>> body.text = "TEXT"

>>> etree.tostring(html)

b’<html><body>TEXT</body></html>’

>>> br = etree.SubElement(body, "br")

>>> etree.tostring(html)

b’<html><body>TEXT<br/></body></html>’

>>> br.tail = "TAIL"

>>> etree.tostring(html)

b’<html><body>TEXT<br/>TAIL</body></html>’

使用XPath查找文本

另一个抽取XML树的文本内容是XPath,

>>> print(html.xpath("string()")) # lxml.etree only!

TEXTTAIL

>>> print(html.xpath("//text()")) # lxml.etree only!

[’TEXT’, ’TAIL’]

如果经常使用,可以包装成一个方法:

>>> build_text_list = etree.XPath("//text()") # lxml.etree only!

>>> print(build_text_list(html))

[’TEXT’, ’TAIL’]

也可以通过getparent方法得到父节点

>>> texts = build_text_list(html)

>>> print(texts[0])

TEXT

>>> parent = texts[0].getparent()

>>> print(parent.tag)

body

>>> print(texts[1])

TAIL

>>> print(texts[1].getparent().tag)

br

You can also find out if it’s normal text content or tail text:

>>> print(texts[0].is_text)

True

>>> print(texts[1].is_text)

False

>>> print(texts[1].is_tail)

True

树的迭代:

Elements提供一个树的迭代器可以迭代访问树的元素。

>>> root = etree.Element("root")

>>> etree.SubElement(root, "child").text = "Child 1"

>>> etree.SubElement(root, "child").text = "Child 2"

>>> etree.SubElement(root, "another").text = "Child 3"

>>> print(etree.tostring(root, pretty_print=True))

<root>

<child>Child 1</child>

<child>Child 2</child>

<another>Child 3</another>

</root>

>>> for element in root.iter():

... print("%s - %s" % (element.tag, element.text))

root – None

child - Child 1

child - Child 2

another - Child 3

如果知道感兴趣的tag,可以把tag的名字传给iter方法,起到过滤作用。

>>> for element in root.iter("child"):

... print("%s - %s" % (element.tag, element.text))

child - Child 1

child - Child 2

默认情况下,迭代器得到一个树的所有节点,包括ProcessingInstructions, Comments and Entity的实例。如果想确认只有Elements对象返回,可以把Element factory作为参数传入。

>>> root.append(etree.Entity("#234"))

>>> root.append(etree.Comment("some comment"))

>>> for element in root.iter():

... if isinstance(element.tag, basestring):

... print("%s - %s" % (element.tag, element.text))

... else:

... print("SPECIAL: %s - %s" % (element, element.text))

root - None

child - Child 1

child - Child 2

another - Child 3

SPECIAL: ê - ê

SPECIAL: <!--some comment--> - some comment

>>> for element in root.iter(tag=etree.Element):

... print("%s - %s" % (element.tag, element.text))

root - None

child - Child 1

child - Child 2

another - Child 3

>>> for element in root.iter(tag=etree.Entity):

... print(element.text)

序列化:

序列化通常使用tostring()方法来返回一个字符串,或者ElementTree.write()方法来写入一个文件,一个类文件的对象,或者一个URL(通过FTP的PUT或者HTTP的POST)。二者都使用相同的关键字参数比如pretty_print来格式化输出或者encoding来选择一个特定的输出编码而不是简单的ASCII。

>>> root = etree.XML("<root><a><b/></a></root>")

>>> etree.tostring(root)

’<root><a><b/></a></root>’

>>> print etree.tostring(root, xml_declaration=True)

<?xml version=’1.0’ encoding=’ASCII’?>

<root><a><b/></a></root>

>>> print etree.tostring(root, encoding="iso-8859-1")

<?xml version=’1.0’ encoding=’iso-8859-1’?>

<root><a><b/></a></root>

>>> print etree.tostring(root, pretty_print=True)

<root>

<a>

<b/>

</a>

</root>

Note that pretty printing appends a newline at the end.

注意pretty打印在末尾添加一个新行。

从lxml2.0起,serialisation可以做的不止XML序列化,可以序列化到HTML或者通过传递函数关键字来提取文本内容。

>>> root = etree.XML("<html><head/><body><p>Hello<br/>World</p></body></html>")

>>> etree.tostring(root) # default: method = ’xml’

’<html><head/><body><p>Hello<br/>World</p></body></html>’

>>> etree.tostring(root, method="xml") # same as above

’<html><head/><body><p>Hello<br/>World</p></body></html>’

>>> etree.tostring(root, method="html")

’<html><head></head><body><p>Hello<br>World</p></body></html>’

>>> print etree.tostring(root, method="html", pretty_print=True)

<html>

<head></head>

<body><p>Hello<br>World</p></body>

</html>

>>> etree.tostring(root, method="text")

b’HelloWorld’

对XML序列化而言,默认的文本编码是ASCII

>>> br = root.find(".//br")

>>> br.tail = u"W/xf6rld"

>>> etree.tostring(root, method="text") # doctest: +ELLIPSIS

Traceback (most recent call last):

...

UnicodeEncodeError: ’ascii’ codec can’t encode character u’/xf6’ ...

>>>etree.tostring(root, method="text", encoding="UTF-8")

b’HelloW/xc3/xb6rld’

>>> etree.tostring(root, encoding=unicode, method="text")

u’HelloW/xf6rld’

ElementTree类:

一个ElementTree主要是围绕在一个有根节点的树的文档包装类。它提供了很多方法来解析,序列化以及一般的文档处理。一个最大的区别是它作为一个整体文档来序列化。与之相对的是序列化成单个的元素。

>>> tree = etree.parse(StringIO("""/

<?xml version="1.0"?>

<!DOCTYPE root SYSTEM "test" [ <!ENTITY tasty "eggs"> ]>

<root>

<a>&tasty;</a>

</root>

"""))

>>> print(tree.docinfo.doctype)

<!DOCTYPE root SYSTEM "test">

>>> # lxml 1.3.4 and later

>>> print(etree.tostring(tree))

<!DOCTYPE root SYSTEM "test" [

<!ENTITY tasty "eggs">

]>

<root>

<a>eggs</a>

</root>

>>> # lxml 1.3.4 and later

>>> print(etree.tostring(etree.ElementTree(tree.getroot())))

<!DOCTYPE root SYSTEM "test" [

<!ENTITY tasty "eggs">

]>

<root>

<a>eggs</a>

</root>

>>> # ElementTree and lxml <= 1.3.3

>>> print(etree.tostring(tree.getroot()))

<root>

<a>eggs</a>

</root>

从字符串和文件中解析:

fromstring()是解析字符串最容易的方法

>>> some_xml_data = "<root>data</root>"

>>> root = etree.fromstring(some_xml_data)

>>> print root.tag

root

>>> etree.tostring(root)

’<root>data</root>’

XML()方法和fromstring()方法类似,但它主要用来把XML文字写入源文件。

>>> root = etree.XML("<root>data</root>")

>>> print root.tag

root

>>> etree.tostring(root)

’<root>data</root>’

parse()方法用来从文件或者类文件对象中解析

>>> some_file_like = StringIO.StringIO("<root>data</root>")

>>> tree = etree.parse(some_file_like)

>>> etree.tostring(tree)

’<root>data</root>’

注意parse()返回的是一个ElementTree对象,而不是字符串解析方法的Element对象。

>>> root = tree.getroot()

>>> print root.tag

root

>>> etree.tostring(root)

’<root>data</root>’

解析器对象:lxml.etree在默认情况下使用带默认配置的标准解析器,如果想配置解析器,可以创建自己的实例。

>>> parser = etree.XMLParser(remove_blank_text=True) # lxml.etree only!

本例在解析的时候创建了一个移除tags之间的空的文本的解析器,这可以减少tree的大小以及避免不定的tail,如果你知道空白内容对你来说是没有任何意义的话。

>>> root = etree.XML("<root> <a/> <b> </b> </root>", parser)

>>> etree.tostring(root)

b’<root><a/><b> </b></root>’

>>> for element in root.iter("*"):

... if element.text is not None and not element.text.strip():

... element.text = None

>>> etree.tostring(root)

b’<root><a/><b/></root>’

递增解析:

lxml.etree提供了两种方法来实现递增的逐步的解析。一个方法是通过类文件对象,它重复调用read() 方法。

>>> class DataSource:

... data = [ b"<roo", b"t><", b"a/", b"><", b"/root>" ]

... def read(self, requested_size):

... try:

... return self.data.pop(0)

... except IndexError:

... return b’’

>>> tree = etree.parse(DataSource())

>>> etree.tostring(tree)

b’<root><a/></root>’

第二个方法是通过feed解析器接口,由feed(data) 和 close() 方法提供

>>> parser = etree.XMLParser()

>>> parser.feed("<roo")

>>> parser.feed("t><")

>>> parser.feed("a/")

>>> parser.feed("><")

>>> parser.feed("/root>")

>>> root = parser.close()

>>> etree.tostring(root)

’<root><a/></root>’

在调用close() 方法(或者当有exception发生的时候),可以通过调用feed() 方法重新使用parser:

>>> parser.feed("<root/>")

>>> root = parser.close()

>>> etree.tostring(root)

b’<root/>’


用lxml来解析大型xml文件+命令行中的python

转自:http://jiangzhixiang123.blog.163.com/blog/static/2780206220118711303042/

上周被布置了一个任务,要解析一个大约有600MB左右的xml文件,从中提取所需的信息然后输出成一个csv文件。从来没有做过类似东西 的我,加上不太熟悉的python、linux和vim,这样一个简单的东西花了一天半才解决,不过解决的还算比较完美吧,用lxml这个库,原本以为要 至少10几分钟的解析过程其实只用了1分钟左右,说起来,还是c比较强大啊,lxml的底层使用c实现的,换成python恐怕就够呛了。好了废话不多 说,讲讲lxml的用法和我在做这个任务里碰到的几个问题吧,权当复习和备份。


用lxml载入xml文件

lxml是c中的libxml的python实现,在保证效率的情况下,为程序员免去了内存管理方面的麻烦,具体介绍大家还是移步它的官网吧。

首先在python里import lxml的etree模块,然后用etree里的parse函数从文件中解析xml,解析得到的是一个ElementTree的实例,用这个实例的 getroot函数就能得到xml中的root。root是对象Element的一个实例,对这个root可以做indexing,即用过root
可以得到root下相应的子节点,这些子节点同样也是Element的实例,所以通过root
[m]就可遍历各个节点。

>>>from lxml import etree

>>>tree = etree.parse(open(“file_name”,“rb”))

>>>root = tree.getroot()

另外,对一个Element还可以进行iterate操作,iterate会依次遍历Element下的所有子节点、子节点的子节点,然后按照顺序,返回一个所有节点的序列。

如果我们有这样一个xml:

<root>

<child>

<grandson1/>

<grandson2>name1</grandson2>

</child>

<child>

<grandson1/>

<grandson2>name2</grandson2>

</child>

</root>

那么:

>>>root[0] #返回第一个child节点的Element实例

>>>root[0][0] #返回第一个child中grandson1

>>>root.iter() #按顺序返回root中所有节点

那么如何得到各个节点中的信息呢?其实也很方便,用element.text、element.tag可以得到节点的内容和节点的名字。另外用element.get(“attribute_name”)还可以得到节点中attribute的值。

如:

>>>for child in root:

>>> for son in child.iter():

>>> print son.tag, “:”, son.text

这样一段代码就可以遍历每个上面那个xml中每一个child中的grandchild的名字了。

另外,lxml还提供了丰富的写xml的功能,和读写html的功能,可以说是一场强大,有兴趣的童鞋可以自行研究~


像运行*nix命令一样用python模块

我做的这个解析功能是给同事用的,所以自己加了点代码好让同事在命令行中利用这个代码。

我想实现的功能是,当同事在bash中运行:

$:python parser.py –f some_xml_file.xml

便可以直接跑我的代码来解析xml了。当同事运行:

$:python parser.py

的时候,会把这个模块的用法打印出来。具体代码如下,各个行的作用我用注释标注了:

if __name__ == “__main__”:

#引用OptionParse模块:

from optparse import OptionParser

#初始化一个Parser,这时可以用usage参数写下模块说明:

parser = OptionParser(usage=”%prog [options] xml_filename\n”

“available option: -f indicates the xml file name”)


#添加一个option,缩写为-f,全称是–file:

parser.add_option(‘-f’, ‘–file’)

#从命令行中得到option和arguments:

options, args = parser.parse_args()

#如果file不为空,则运行模块:

if options.file:

sys.exit(parse_feed(options.file))


#若file是空,则打印出模块用法:

else:

parser.print_usage()

sys.exit(1)


这样,一个可以让人方便使用的模块就写好了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: