您的位置:首页 > 其它

lxml.etree 入门

2016-06-24 17:33 686 查看
来源 http://lxml.de/tutorial.html

作者: Stefan Behnel

这个教学覆盖lxml 处理的主要几个方面,其中的一些功能也许能使你的码农生涯好过一点。

完整的API 请看 http://lxml.de/api/index.html

通常像下面这样导入 lxml.etree 模块:

from lxml import etree


Element 类

这个一个主要的类,大部分函数都通过它来进行。使用Element工厂函数很容易建立起一个xml元素。

访问标签名,用tag属性。

root = etree.Element("root")
print(root.tag)  #root


添加子节点的方法之一,append方法:

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


更有效的方法是SubElement工厂函数,使用如下:

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


这个方法,建立元素,添加子节点一步完成。

使用tostring 方法,可以看到刚才建立的 xml文件全貌。

>>>print(etree.tostring(root, pretty_print=True))
<root>
<child1/>
<child2/>
<child3/>
</root>


节点本身就是列表

节点在努力模拟Python中list的样子:

可以使用索引来获取元素

对于上面建立的xml,

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

root.insert(0, etree.Element("child0"))
start = root[:1]
end   = root[-1:]

print(start[0].tag)
#child0
print(end[0].tag)
#child3


在ElementTree 1.3 and lxml 2.0 以前的版本中,可以使用下面的代码来判断,一个节点是否有子节点。

if root:   # this no longer works!
print("The root element has children")


但这个已经不再支持了。因为有些人认为,节点也是“某种东西”,所以对节点判断,本来就应该是True。即使这个节点没有子节点。 代替方案是用 len(element) 。

print(etree.iselement(root))  # test if it's some kind of Element
True
if len(root):                 # test if it has children
print("The root element has children")
#The root element has children


在lxml和Python原生的list之间,还要一点不同。请看代码

>>> for child in root:
...     print(child.tag)
child0
child1
child2
child3
>>> root[0] = root[-1]  # this moves the element in lxml.etree!
>>> for child in root:
...     print(child.tag)
child3
child1
child2


它把最后一个元素移动到第一个位置了。

>>> l = [0, 1, 2, 3]
>>> l[0] = l[-1]
>>> l
[3, 1, 2, 3]


可是在原生的list中,只是把最后一个对象的引用拷贝到第一个位置 。也就是说同一个对象可以同时出现在不同的地方。这一点在lxml中不一样,他用的是移动,而不是拷贝。

注意在原生的ElementTree中,节点就像list一样可以复制在许多不同的地方,可这样也有一个明显的缺点,就是改变这个节点后,所有引用这个节点的地方都会一个改变,这可能不是你想要的。

Element 总是有一个确切的父节点,可以通过getparent() 方法来查询,这在原生ElementTree中并不支持。

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


既然,在lxml中的节点无法复制都是移动的形式,如果你真的是想复制,那怎么办呢?

请使用copy库的deepcopy方法。

>>> 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']


上面这个例子展示的是,用deepcopy复制了root[1]这个元素后,并没有移动它。所有还是能在原来的root中打印出来的。

相邻元素之间的访问

>>> 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"/>'


属性即是无序的键值对,所以用字典可以方便的处理。

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

>>> print(root.get("hello"))
None
>>> root.set("hello", "Huhu")
>>> print(root.get("hello"))
Huhu

>>> etree.tostring(root)
b'<root interesting="totally" hello="Huhu"/>'

>>> sorted(root.keys())
['hello', 'interesting']

>>> for name, value in sorted(root.items()):
...     print('%s = %r' % (name, value))
hello = 'Huhu'
interesting = 'totally'


如果你想获取字典这样的结构,就用attrib属性获取。

>>> attributes = root.attrib

>>> print(attributes["interesting"])
totally
>>> print(attributes.get("no-such-attribute"))
None

>>> attributes["hello"] = "Guten Tag"
>>> print(attributes["hello"])
Guten Tag
>>> print(root.get("hello"))
Guten Tag


attrib 是Element中支持类似字典的对象, 这意味着任何对节点的改变会反映到attrib属性,反之亦然。

只要节点的一个属性在使用中,XML就在内存中活跃着。为了得到一个独立的属性的快照,不要依赖xml tree,用copy复制一个。

>>> d = dict(root.attrib)
>>> sorted(d.items())
[('hello', 'Guten Tag'), ('interesting', 'totally')]


在节点中包含文本

在节点中可以携带文字,

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

>>> print(root.text)
TEXT

>>> etree.tostring(root)
b'<root>TEXT</root>'


在许多XML文档中,这是文本能存放的唯一地方,文本被包裹在节点的标签中。

然而,像XHTML这样的,文本也可能出现在不同的节点之间,或者在节点的右边。

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


像这个例子,
<br>
这个标签被文本包围。 节点为了支持这个特性,使用了tail属性。 它包含的文本直接跟在节点后面,并且在下一个节点之前。下面是例子:

>>> 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>'


灵活使用 .text 和 .tail 这两个属性,就可以满足任何文本插入的需要。这样,API就不需要专门的文本节点了。

然而,当你序列化一个xml时,可能并不想要tail的文本,这时,你只需要在用tostring方法时, 注意指定with_tail的值为False即可,下面是示例代码:

>>> etree.tostring(br)
b'<br/>TAIL'
>>> etree.tostring(br, with_tail=False) # lxml.etree only!
b'<br/>'


下面有个强大的功能,如果你只想要xml中间的文本,而不要任何的标签。这个有个方法可以快速搞定。

还是tostring方法,传入method关键字为 text 即可。

>>> etree.tostring(html, method="text")
b'TEXTTAIL'


比如要把word中的文本读出来,又不需要大堆的标签时。这个就派上用场了。

另一个提取文本的方法是 XPath ,并且还能把提取的文本,放入一个list中间。

>>> 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']


通过XPath返回的对象有些聪明,它能够知道自己的来源。你可以通过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


你还可以知道,这个文本是普通文本,还是tail文本。

>>> print(texts[0].is_text)
True
>>> print(texts[1].is_text)
False
>>> print(texts[1].is_tail)
True


While this works for the results of the text() function, lxml will not tell you the origin of a string value that was constructed by the XPath functions string() or concat():

当使用text()函数来获取文本时, 是可以获取parent的关系的。但是如果是用string()和concat方法,就不能获取这样的特性了。示例代码

#!/usr/local/python2.7/bin/python
#encoding=UTF-8

from lxml import etree

html = etree.Element("html")
html.text= 'abc'
html.tail= 'xyz'

ch1 =etree.SubElement(html, "child1")
ch1.text="child1"

print etree.tostring(html,pretty_print=True)

#string方法,无法获取getparent的信息
t = html.xpath("string()") # lxml.etree only!
print t.getparent()

#下面可以获取getparent的信息
t2 = html.xpath("//text()") # lxml.etree only!
print t2
for a in t2:
print a.getparent().tag


未完
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: