python之lxml快速上手_Element(一)

来源:互联网 发布:淘宝大学电商总裁班 编辑:程序博客网 时间:2024/06/12 01:41

The Element class

对于使用任何一种编程语言的开发人员来说,xml的处理总是不可避免,甚至是非常常见的。而lxml则是在python语言中,功能最丰富、最易于使用,同时性能也相当不错的xml、html处理库。虽然网上也有许多介绍lxml用法的文章,但是,学习任意一个第三方库(框架、新技术),官方文档无疑是不可多得的第一手好材料。于是,为了让其他有需要的同学也能快速上手,针对手册中The lxml.etree Tutorial部分,进行了部分翻译。同时,本人英语水平有限,或者理解上有偏差,望不吝指正。

ElementElementTree API中是主要的容器对象。大部分XML tree函数都是通过它访问的。要创建它也是很简单,只需使用Element工厂函数:

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

xml节点的标签名可通过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")

为了更加直观地了解到刚才创建的就是xml,你可以使用序列化:

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

Elements are lists

为了更加简单、直接地访问(上面所创建的)子节点,elements尽可能地模仿常规python List的行为:

>>> 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)child1child2child3>>> 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之前,你可以检查一个Element的真假值,来判定它是否有孩子节点。如果孩子节点组成的list是空的:

if root:   # 在后续版本中将不再起作用    print("The root element has children")

上面的条件测试将不再起作用。因此,许多用户可能会惊奇地发现,任意一个节点像上面一样进行条件测试,结果都是False。作为替代的做法,使用len(element)语意上更加明确, 也意味着更少的错误倾向。

>>> print(etree.iselement(root))  # 测试root是否是某种类型的ElementTrue>>> if len(root):                 # 测试root是否有孩子节点...     print("The root element has children")The root element has children

还有一个需要说明的情况,lxml中Elements的行为 (在2.0及之后的版本中)与常规python List、原始的ElementTree会有偏差。

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

在上面的例子中,最后一个节点被移动到不同的位置(第一个),而不是被拷贝到另一个位置。当它被移至另一个不同的位置,它从它原有的位置被移除。在一般的list中,对象可以在同一时间点出现在不同位置,而像上面的情况,在list中仅会拷贝最后一个节点的引用到第一个位置,因此list中包含两个同样的对象:

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

注意,在原始的ElementTree中,一个Element对象可以位于任意xml树对象的任意一个位置,它允许像list一样执行同样的拷贝操作。很明显的一个缺点,对节点的任意改变都将应用到所有它在tree中出现的地方。而这却不一定是你想要的。
上面这种特殊的处理方式,有一个好处就是:在lxml.etree中的任意一个Element都有唯一一个父节点,可通过getparent()获取,而在原始ElementTree中是不支持的。

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

如果你想在lxml.etree中拷贝一个节点到另外一个位置,可以考虑使用独立的标准库模块copydeepcopy()

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

如果你想访问一个节点的兄弟节点,可以:

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

Elements carry attributes as a dict

XML节点支持属性,你可以直接在Element工厂函数中构建它们:

>>> root = etree.Element("root", interesting="totally")>>> etree.tostring(root)b'<root interesting="totally"/>'

属性仅仅是无序的键值(key-value)对,因此,处理它们的一个简便方式就是通过Elements的类字典接口:

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

有时,你只是想做item的查找,或者因为一些其他的原因,想要获得“真实”的类字典对象,并把它传递给周边,你可以使用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本身支持的类字典对象。这也意味者对Element所做的改动会反射到attrib上,同样,XML tree将一直保持“激活”状态,只要它的任意节点的attrib还在使用中。为了获取一个独立的,不依赖于XML tree的attrib快照,可以把它拷贝到一个字典:

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

Elements contain text

Elements可以包含文本:

>>> root = etree.Element("root")>>> root.text = "TEXT">>> print(root.text)TEXT>>> etree.tostring(root)b'<root>TEXT</root>'

对于许多xml文档(以数据为主),这是唯一能找到文本的地方。(文本)通常被tree底部的叶子节点包裹者。
然而,如果xml被用作标签文本例如HTML,文本也可以出现在不同的节点之间:

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

在这里,
标签被文本包围。这在document-stylemixed-content类型的xml中经常被提及。 Elements使用通过tail属性来支持这一点的。它包含紧跟该节点的文本,直到XML tree中的下一个节点:

>>> 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已经足够呈现XML文档中的任意文本。
然而,当你序列化一个Element时,你并总是希望tail text出现在结果中。为了达成这个目的,tostring()函数接受一个关键字参数with_tail:

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

如果你仅仅需要文本text,不需要中间的节点,你必须以正确的顺序,递归连接所有的texttail text属性。再一次,tostring()方法大显身手,这一次使用method关键字参数:

>>> etree.tostring(html, method="text")b'TEXTTAIL'
0 0
原创粉丝点击