lxml.etree
作为一个强大的 xml 处理模块,该有的功能她都是有的。
若操作一个 XML ,首先要能读写,能将字符串或流读进来解析成 ElementTree ,又能将 ElementTree 转换为字符串或流。在这方面 lxml 提供了几个可用的函数:
- etree.fromstring( string )
将 string 解析为 Element 或者 ElementTree 。
- etree.parse( file )
将文件或者是 file_like 对象解析为 ElementTree (not an Element object),因为 parse() 一般解析整篇文档,字符串解析函数一般只解析片段。其中 file 还可以是 HTTP/FTP URL ,也就是说,file 应该是一个 Bytes 流。不过 libxml2 中的 HTTP/FTP client 比较简单,所以可以考虑使用第三方库。
- XML( text ) / HTML( text )
行为比较像 fromstring() ,比较直接的对 XML 和 HTML 文档进行特定解析,可以修改解析器 parser 参数,不同的解析器设置区别还是蛮大的。其中 parser 可以由相应的 XMLParser() / HTMLParser() 函数生成,可设置项有很多不仅限于 encoding 、recover 、remove_blank_text 、remove_comments ,但是默认值是存在不同的。比如 recover 在 XML 中是 False ,而在 HTML 中是 True 。在 HTML 中会使用 libxml2 的 recover 算法对不盒饭的标签进行修复。
- etree.tostring( elt )
将一个 Element 或者 ElementTree 转换为 string 形式。这里面有几个可选参数:pretty_print=False 表示是否格式化提高可读性;method=”xml” 选择输出后的文档格式,不同的选择,做的修改也不相同,可选参数有 xml 、html 、text (文本内容不包括标签,就是纯文本内容,tail也是) 、c14n (规范化 xml );encoding=None 表示以什么编码的字符串输出,在无 xml 文档声明情况下默认是 ASCⅡ ,可通过 encoding=None 进行修改,但是如果所改编码不是 utf-8 兼容,那么将会启用默认声明。
- ElementTree.write( file )
这个是 ElementTree 特有的方法,是将 ElementTree 写到 a file, a file-like object, or a URL (via FTP PUT or HTTP POST) 。可选参数和 tostring() 差不多,也有不同。
剩下就是操作了,增删查改一样也不少,而查找是剩下三个的基础。
etree 对于每种元素都有一个创建函数,比如 Element() 、ElementTree() 、SubElement() 以及 Comment() 。基本所有的函数都是基于这些实例对象的,lxml 的 API 是比较 Pythonic 的,Element 的行为和 list 比较像可以使用 len() 函数切片等,Element 的 attrib 就是一个 dist ;其中 Element 实例拥有 41 个属性和方法,包含了所有:
增append() insert()
删clear() remove()
查find*() get*() iter*() xpath()
改set() 以及属性 .tag .attrib .text .tail等
的操作
ElementTree 实例拥有的方法较少,常用的大概有 7 种,主要是查找find*() get*() xpath()
。
Element 的属性介绍:
- .tag
元素的名称
- .attrib
一个包含元素属性的字典,key 是属性名,value 是对应的值。
- .text
Element 的文本均为直接子文本,不包含子元素中的文本,这其中又包含两部分 .text 和 .tail 。.text 是第一个子元素标签之前的,如果没有则为 None 。
- .tail
.tail 为 Element 的关闭标签之后的文本,并且是在下一个兄弟子标签之前的部分。没有则为 None 。
Element 的方法介绍:
- append( child )
添加一个新的子节点(可以是 Element 、Comment)到当前 Element 中。
- insert( index, elt)
将子元素 elt 插入到指定位置,这个 index 的随意性比较大,如果是正数但是超过了最大值,那么 lxml 会直接将这个元素插到末尾,如果是负数,但是这个负数所指位置不存在,那么就插到末尾,这个负数的位置计算规则和列表的那个不太一样,不知道正确的规律是什么,但是经过测试,
-n
所插的位置,后面有 n (以变化前计算)个元素。如果对 Element 中的子元素执行 insert() 操作,那么子元素位置会按 index 指定的变换。 - clear()
调用该函数,将移除所有内容 .attrib 将被移除 .text 和 .tail 将被设置为 None 所有的子节点将被删除。
- remove( child )
将子节点 child 从Element 中移除 ,如果child 不是 Element 的子节点,将会引发 ValueError 异常。
- find( path )
从 Element 的子元素及后代元素中查找第一个符合 path 的 subelement 。如果没有返回 None 。
ElementPath 是 ElementTree 自带的一个 XPath-like 的路径语言,和 XPath 差不太多,主要区别是 ElementPath 能用
{namespace}tag
,但是 ElementPath 不能使用值比较和函数。 - findall( path )
返回一个匹配的 Element 的列表。
- findtext( path, default=None )
返回第一个匹配元素的 .text 内容,如果存在匹配,但是没有 .text 内容,那么将返回一个空字符串,如果没有一个匹配的元素,那么将会返回一个 None ,但是有 default 参数,返回 default 所指定的。
- get( key, default=None )
返回字符串形式的 属性 key 的值,没有返回 None 或者 default 指定的。
- getchildren()
返回一个包含 Element 子元素的列表。
- getiterator( tag=None, *tags )
返回元素的一个生成器,返回元素类别取决于参数 tag ,生成顺序是
in document order (depth first pre-order)
深度优先的先根遍历。如果没有参数的话,则第一个就是元素本身。如果想使用一个高效的生成器,可以使用 .iter() 。 - getroottree()
返回该元素的 ElementTree 。
- iter( tag=None, *tags )
过滤特定标签,生成迭代器。默认情况下,iter() 迭代所有的节点,包括PI(处理指令) 、Comment(注释) 等,如果只想迭代标签元素,可以使用 Element factory 做参数
e.iter(tag = etree.Element)
。 - iterfind( path )
迭代所有匹配 ElementPath 的 Element 。
- itertext( tag=None, *tags, with_tail=True )
迭代 Element 元素的文本内容,with_tail 参数决定是否迭代子元素的 tail 。Element 的tail 不会进行迭代。
- iterancestors( tag=None )
如果忽略参数,那么将会迭代所有先辈,加标签名可以只迭代该标签先辈。
- iterchildren( reversed=False, tag=None )
通过设置 reversed=True 可以以反向的顺序迭代子元素。
- itersiblings( preceding=False )
迭代 Element 之后的兄弟元素,可以通过设置 preceding=True 仅迭代 Element 之前的兄弟元素。
- iterdescendants( tag=None )
同 iterancestors()
- items()
返回由元素属性的键值所构成的( name, value)元组的列表。
- keys()
返回一个没有特定顺序的元素属性名的列表。
- set(A, V)
创建或者改变属性 A 的值为 V。
- xpath()
ElementTree 方法介绍:
没有介绍的方法,是前面的类似,或者已经介绍过。
- find( path )
- findall( path )
- findtext( path )
- getiterator( tag=None, *tags )
- getroot()
获得 ElementTree 的 root 元素,一般情况下是树的根节点的 Element 的实例,但是 ElementTree 是经没有任何东西创建出来的,将返回 None 。
- xpath()
- write()
XPath
XPath 是一门在 XML 文档中查找信息的路径语言,W3C 文档:XML Path Language (XPath), Version 1.0。
所有的查找都是基于参考定位的,依靠两者之间存在的某种关系,或主从,或平级,这样一步一步的查到。
在 XPath 中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档节点(或称为根节点)。
XPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。
下面列出了最有用的路径表达式:
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有子节点。 |
/ | 从根节点选取。 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 |
. | 选取当前节点。 |
.. | 选取当前节点的父节点。 |
@ | 选取属性。 |
XPath 通配符可用来选取未知的 XML 元素。
通配符 | 描述 |
---|---|
* | 匹配任何元素节点。 |
@* | 匹配任何属性节点。 |
node() | 匹配任何类型的节点。 |
通过在路径表达式中使用“|”运算符,可以选取若干个路径,“|”两边的表达式是互相独立的,并非是一个表达式下。
除了上面介绍的还有两个概念需要知道:轴(Axes) 、谓语
通过以上介绍我们基本可以进行选取节点了,但是有可能选的节点有点多,那么这时候就需要用谓语了。谓语就是用来做过滤的,过滤条件均写在[]
中。
这个过滤条件可写内容有很多:运算符表达式、数字、函数以及文字( Literal ,W3C 的英文介绍没仔细看,没太懂 Literal 说的是什么)。
w3school 给的一个例子,比较有代表性吧。
路径表达式 | 结果 |
---|---|
/bookstore/book[1] | 选取属于 bookstore 子元素的第一个 book 元素。 |
/bookstore/book[last()] | 选取属于 bookstore 子元素的最后一个 book 元素。 |
/bookstore/book[last()-1] | 选取属于 bookstore 子元素的倒数第二个 book 元素。 |
/bookstore/book[position()<3] | 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。 |
//title[@lang] | 选取所有拥有名为 lang 的属性的 title 元素。 |
//title[@lang=’eng’] | 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。 |
/bookstore/book[price>35.00] | 选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。 |
/bookstore/book[price>35.00]/title | 选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。 |
Xpath 的轴定于的是相对于当前节点具有某种相同特征的节点集。
轴名称 | 结果 |
---|---|
ancestor | 选取当前节点的所有先辈(父、祖父等)。 |
ancestor-or-self | 选取当前节点的所有先辈(父、祖父等)以及当前节点本身。 |
attribute | 选取当前节点的所有属性。 |
child | 选取当前节点的所有子元素。 |
descendant | 选取当前节点的所有后代元素(子、孙等)。 |
descendant-or-self | 选取当前节点的所有后代元素(子、孙等)以及当前节点本身。 |
following | 选取文档中当前节点的结束标签之后的所有节点。 |
namespace | 选取当前节点的所有命名空间节点。 |
parent | 选取当前节点的父节点。 |
preceding | 选取文档中当前节点的开始标签之前的所有节点。 |
preceding-sibling | 选取当前节点之前的所有同级节点。 |
self | 选取当前节点。 |
每一个step
都是由轴名称::NodeTest[谓语]
构成。比如:child::*/child::price
中child::*
和child::price
都是一个step
。
lxml.html
Since version 2.0, lxml comes with a dedicated Python package for dealing with HTML:
lxml.html
. It is based on lxml’s HTML parser, but provides a special Element API for HTML elements, as well as a number of utilities for common HTML processing tasks.The main API is based on the lxml.etree API, and thus, on the ElementTree API.
lxml.html 是一个专门用来处理 HTML 文档的,虽然他依旧是基于 HTML parser 的,但是她提供了很多有针对性的 API 。这里只作一个简单介绍,深入了解,请查阅官方文档。
首先在解析 HTML 文本上增加了几个方法:
- document_fromstring(string)
- fragment_fromstring(string, create_parent=False)
- fragments_fromstring(string)
其次对于 HTML 元素,除了之前的方法之外又加了一些:
- .drop_tree()
删除该元素及其所有子元素,就像
el.getparent().remove(el)
,但是不会删除该元素的 .tail ,.tail 将会与前一个元素合并。 - .drop_tag()
删除该标签,但是会保留该元素的子元素与文本。
- .find_class(class_name)
返回一个含有 class_name CSS 的元素列表。
- .find_rel_links(rel)
返回一个
<a rel="{rel}">
的元素列表 - .get_element_by_id(id, default=None)
通过 id 查找元素,如果没有返回默认,假如文档不合规出现多个同 id 的元素,仅返回第一个。
- .text_content()
返回元素的文本内容,包括其子代的文本内容。
- .cssselect(expr)
通过 CSS 选择器选择元素。
- .set(key, value=None)
设置元素属性,如果 value 没有给或者是 None ,那么将创建一个 boolean attribute (属性只要出现即表示真,无论 value 是什么,哪怕是个空串) E.g.
<div checked></div>
。
正如文章开头说的那个, lxml.html.soupparser 可以引入 BeautifulSoup ,甚至可以仅使用 BeautifulSoup 的编码检测模块 UnicodeDammit 。