Python网络爬虫技术与实战
上QQ阅读APP看书,第一时间看更新

3.4.4 lxml和XPath的结合使用

使用lxml来解析HTML代码,如以下实例所示。

【例3-28】使用lxml解析HTML代码


# 使用lxml的etree库
from lxml import etree 
text = '''
<div>
    <ul>
        <li class="item-0"><a href="link1.html">first item</a></li>
        <li class="item-1"><a href="link2.html">second item</a></li>
        <li class="item-inactive"><a href="link3.html">third item</a></li>
        <li class="item-1"><a href="link4.html">fourth item</a></li>
        <li class="item-0"><a href="link5.html">fifth item</a> 
        # 注意,此处缺少一个 </li> 闭合标签
    </ul>
 </div>
'''
# 利用etree.HTML,将字符串解析为HTML文档
html = etree.HTML(text) 
# 按字符串序列化HTML文档
result = etree.tostring(html)
print(result)

这里体现了lxml的一个非常实用的功能,就是自动修正HTML代码,大家应该注意到了,最后一个li标签,其实是把尾标签删掉了,是不闭合的。不过,因为继承了libxml2的特性,所以lxml具有自动修正HTML代码的功能。

因此运行结果如下:


<html>
<body>
<div>
    <ul>
        <li class="class-0"><a href="http://www.baidu.com">first item</a></li>
        <li class="class-1"><a href="http://www.douban.com">second item</a></li>
        <li class="class-2"><a href="http://www.weibo.com"><span class="se">th
            ird item</span></a></li>
        <li class="class-3"><a href="http://www.google.com">fourth item</a></li>
        <li class="class-4"><a href="http://www.zhihu.com">fifth item</a></li>
    </ul>
 </div>
</body>
</html>

lxml不仅能直接读取字符串,还支持从文件读取内容。比如我们新建一个文件,叫作test.html,内容为:


<div>
    <ul>
        <li class="class-0"><a href="http://www.baidu.com">first item</a></li>
        <li class="class-1"><a href="http://www.douban.com">second item</a></li>
        <li class="class-2"><a href="http://www.weibo.com"><span class="bold">th
            ird item</span></a></li>
        <li class="class-3"><a href="http://www.google.com">fourth item</a></li>
        <li class="class-4"><a href="http://www.zhihu.com">fifth item</a></li>
    </ul>
 </div>

下面以lxml与XPath结合使用的10个实例来展示XPath的强大之处。

【例3-29】利用parse方法来读取文件


1  from lxml import etree
2  html = etree.parse('test.html')
3  result = etree.tostring(html, pretty_print=True)
4  print(result)

此方法也能得到相同的结果。

若test.html内容与例3-28中标签内容相同,那么例3-29同样也能得到相同的结果。

【例3-30】获取所有的<li>标签


1  from lxml import etree
2  html = etree.parse('test.html')
3  print (type(html))
4  result = html.xpath('//li')
5  print (result)
6  print (len(result))
7  print (type(result))
8  print (type(result[0]))

运行结果如下:


<class 'lxml.etree._ElementTree'>
[<Element li at 0x1f5809048c8>, <Element li at 0x1f580904988>, <Element li at 0
x1f5809049c8>, <Element li at 0x1f580904a08>, <Element li at 0x1f580904a48>]
5
<class 'list'>
<class 'lxml.etree._Element'>

由运行结果可知,etree.parse的类型是ElementTree,通过调用XPath(第4得到了一个列表(result是一个数组列表),其包含5个<li>元素,每个元素都是Element类型。

【例3-31】获取<li>标签的所有class


1  from lxml import etree
2  html = etree.parse('test.html')
3  print (type(html))
4  result = html.xpath('//li/@class')
5  print (result)

运行结果如下:


<class 'lxml.etree._ElementTree'>
['class-0', 'class-1', 'class-2', 'class-3', 'class-4']

【例3-32】获取<li>标签下href为http://www.baidu.com的<a>标签


1  from lxml import etree
2  html = etree.parse('test.html')
3  result = html.xpath('//li/a[@href="http://www.baidu.com"]')
4  print (result)

运行结果如下:


[<Element a at 0x25d9c8038c8>]

【例3-33】获取<li>标签下的所有<span>标签


1  from lxml import etree
2  html = etree.parse('test.html')
3  result = html.xpath('//li//span')
4  print (result)

注意,这里为了获取<span>标签,使用的是双斜杠“//”。

运行结果如下:


[<Element span at 0x11fd9132908>]

【例3-34】获取<li>标签下的所有class,不包括<li>


1  from lxml import etree
2  html = etree.parse('test.html')
3  result = html.xpath('//li/a//@class')
4  print (result)

运行结果如下:


['se']

【例3-35】获取第一个<li>的<a>的href


1  from lxml import etree
2  html = etree.parse('test.html')
3  result = html.xpath('//li[1]/a/@href')
4  print (result)

运行结果如下:


['http://www.baidu.com']

【例3-36】获取最后一个<li>的<a>的href


1  from lxml import etree
2  html = etree.parse('test.html')
3  result = html.xpath('//li[last()]/a/@href')
4  print (result)

运行结果如下:


['http://www.zhihu.com']

【例3-37】获取倒数第二个<li>的<a>的内容


1  from lxml import etree
2  html = etree.parse('test.html')
3  result = html.xpath('//li[last()-1]/a')
4  print (result[0].text)

运行结果如下:


fourth item

【例3-38】获取class为"class-2"的标签名


1  from lxml import etree
2  html = etree.parse('test.html')
3  result = html.xpath('//*[@class="class-2"]')
4  print (result[0].tag)

运行结果如下:


li