那我们应该选哪个方法呢?别犹豫,选 etree.HTML(),即使你的 HTML 内容来自文件。这是为何?
首先要说的一点是,HTML 也是 XML 的一种,而 XML 的标准规定,其必须拥有一个根标签,否则,这段 XML 就黑白法的。而我们加载进来的 HTML 内容,可能本身就不是完整的,只是个片段,且没有根标签;或是加载进来的 HTML 重新到脚看起来都是完整的,但是中间的节点,有的缺少竣事标签,这些环境,其实都黑白法的 XML。那么,在用 parse() 或 formstring() 加载这种缺胳膊少腿的 HTML 的时候,就会报错;而用 etree.HTML() 则不会。
这是由于 etree.HTML() 加载方式,有很好的 HTML 兼容性,它会补全缺胳膊少腿的 HTML,把它变成一个完整的、合法的 HTML。
下面是一个从文件加载 HTML 的例子:
from lxml import etree
with open('test.html', 'r') as f:
html = etree.HTML(f.read())
print(html, type(html))
复制代码
打印出来的效果是: ,加载进来的 HTML 字符串,已经变成了 Element 对象。
后面我们通过 xpath 找 HTML 节点,全都是在这个 Element 对象上操纵的。
找到你需要的 HTML 节点
下面是我想要找的 HTML 节点
在这个 table 表格中,第一个 tbody 是表头,第二个 tbody 是表内容,我们要如何定位到第二个 tbody ?
我们通常是调用上面获得的 Element 对象的 xpath() 方法,通过传入的 xpath 路径查找的。而路径有两种写法:一种是 / 开头,从 html 根标签,沿着子节点一个个找下来;另一种是 // 开头,即岂论我们要找的节点在什么位置,找到就算,这种方式是最常用的。
比如,我们如今要找的 tbody 节点,它在 table 节点下,我们就可以这样写:html.xpath('//table/tbody')。这里的 html 是上面获得的 Element 对象,然后去找 HTML 内容中的、不管在任何位置的所有的 table,找到后再继续找它们下面的直接子节点 tbody,于是就匹配出来了。
可是这里有 2 个 tbody,我需要的是第二个,我们可以在 [] 中写条件表达式:html.xpath('//table/tbody[2]'),留意这里的序号是从 1 开始的。
强大的属性选择器
观察一下上面的 HTML 结构,table 表格的最外层有一个 div,它还有个 class 属性:table-responsive,假设这个 div 的 class 属性是整个 HTML 里独一无二的,那么我们就可以很放心的去查找 div.table-responsive 下的 table,进而准确定位我们想要的元素。
那么,要怎样写 class = "table-responsive" 这个条件呢?看看上面写条件表达式的 [],那里面除了可以写数字来指定位置以外,也可以写其它各式各样的条件,比如:
html.xpath('//div[@]/table/tbody[2]'),这里我们就把 class = "table-responsive" 这个条件写进去了,从而定位到想要的元素。留意,在 xpath 中,所有的 HTML 属性匹配都是以 @ 打头的,比如有这样一个 Click Me 元素,我们想要通过 id 定位它,可以这样写://a[@id="show_me"],是不是很简单。
假设很遗憾,我们这里的 table-responsive 不是唯一的,可能还有其它地方的 div 的 class = "table-responsive",这该怎么办?没关系,我们可以找其它具有唯一 class 值的元素,比如:最外层 div 下的 table.result_list 这个元素,这个是唯一的。好了,下面开始写定位代码:html.xpath('//table[@]/tbody[2]'),但是运行后,发现找不到元素,这是为什么?
其实仔细观察一下就能发现,这个元素的 class 里不只有 result_list,它还包括其它一长串的内容:class = "table result_list table-striped table-hover",所以匹配失败了。那要如何指定 class 包含某个属性呢?其实可以在条件表达式中,用 contains() 函数,无需准确匹配,而是模糊匹配,只要包含指定的字符串就可以了。比如:html.xpath('//table[contains(@class, "result_list")]/tbody[2]') 这样就可以实现了。
需要提一点的是,xpath 定位到的元素,不管是不是全局唯一的,它的返回值都是一个列表,需要通过下标获取此中的元素。
相对定位