python之lxml快速上手_Element(一)
2017-04-16 13:42
381 查看
The Element class
对于使用任何一种编程语言的开发人员来说,xml的处理总是不可避免,甚至是非常常见的。而lxml则是在python语言中,功能最丰富、最易于使用,同时性能也相当不错的xml、html处理库。虽然网上也有许多介绍lxml用法的文章,但是,学习任意一个第三方库(框架、新技术),官方文档无疑是不可多得的第一手好材料。于是,为了让其他有需要的同学也能快速上手,针对手册中The lxml.etree Tutorial部分,进行了部分翻译。同时,本人英语水平有限,或者理解上有偏差,望不吝指正。Element在ElementTree 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) 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之前,你可以检查一个Element的真假值,来判定它是否有孩子节点。如果孩子节点组成的list是空的:
if root: # 在后续版本中将不再起作用 print("The root element has children")
上面的条件测试将不再起作用。因此,许多用户可能会惊奇地发现,任意一个节点像上面一样进行条件测试,结果都是False。作为替代的做法,使用len(element)语意上更加明确, 也意味着更少的错误倾向。
>>> print(etree.iselement(root)) # 测试root是否是某种类型的Element True >>> 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) 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
在上面的例子中,最后一个节点被移动到不同的位置(第一个),而不是被拷贝到另一个位置。当它被移至另一个不同的位置,它从它原有的位置被移除。在一般的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中拷贝一个节点到另外一个位置,可以考虑使用独立的标准库模块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']
如果你想访问一个节点的兄弟节点,可以:
>>> root[0] is root[1].getprevious() # lxml.etree only! True >>& bb5c gt; 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-style、mixed-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,不需要中间的节点,你必须以正确的顺序,递归连接所有的text、tail text属性。再一次,tostring()方法大显身手,这一次使用method关键字参数:
>>> etree.tostring(html, method="text") b'TEXTTAIL'
相关文章推荐
- python之lxml快速上手_Element(二)
- python logging 快速上手
- Python程序语言快速上手教程
- Python快速上手JSON指南
- python编程快速上手之第10章实践项目参考答案
- Python编程快速上手——让繁琐工作自动化学习笔记
- Python程序语言快速上手教程
- 【Python五篇慢慢弹】快速上手学python
- Python requests模块快速上手
- python编程快速上手之第5章实践项目参考答案
- Python 编程快速上手
- Python快速上手(三)
- Python快速上手(二)
- python3爬虫必学Xpath,快速使用lxml.etree
- python-requests快速上手
- Python快速上手(一)
- Python快速上手(一)
- Python 编程快速上手
- python编程快速上手之第6章实践项目参考答案
- Python萌新快速上手+老鸟案例