在网页节点中,可以定义id、class或其他属性。节点间有层次关系,网页中要通过XPath或CSS选择器定位一个或多个节点。在页面解析时,可利用XPath或CSS选择器提取某个节点,再调用相应方法获取它的正文内容或者属性,就可提取到想要的信息。在python中常用的解析库有lxml、Beautiful Soup、pyquery等。使用这些库可以很大程度上提高效率。
一 使用XPath解析库
XPath,全称XML Path Language,即XML路径语言,是一门在XML文档中查找信息的语言。适用于XML文档和HTML文档搜索。
XPath的选择功能十分强大,提供了非常简洁明了的路径选择表达式。另外,还提供了超过100个内建函数,用于字符串、数值、时间的匹配以及节点、序列的处理等。几乎所有想要定位的节点,都可以用XPath来选择。
XPath于1999年11月16日成为W3C标准,被设计为供XSLT、XPointer以及其他XML解析软件使用,更多的文档可以访问其官方网站:https://www.w3.org/TR/xpath/。
XPath对应的库是lxml库。
1、 XPath的常用规则
下表是XPath的几个常用规则
表4-1 XPath常用规则
表达式 描述
nodename 选取此节点的所有子节点
/ 从当前节点选取直接子节点
// 从当前节点选取子孙节点
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性
下面是XPath的匹配示例,如下所示:
//title[@lang='eng']
这个实例是XPath规则 ,代表选择所有名称为title,同时属性lang的值为eng的节点。
2、 第一个lxml库实例
下面用一个简单的代码了解下XPath对网页的解析过程,代码如下:
1 from lxml import etree 2 text = ''' 3 <div> 4 <ul> 5 <li class="item-0"><a href="link1.html">first item</a></li> 6 <li class="item-1"><a href="link2.html">second item</a></li> 7 <li class="item-inactive"><a href="link2.html">third item</a></li> 8 <li class="item-1"><a href="link4.html">fourth item</a></li> 9 <li class="item-0"><a href="link5.html">fifth item</a> 10 </ul> 11 </div> 12 ''' 13 html = etree.HTML(text) # 调用HTML类初始化,自动修正HTML文本 14 result = etree.tostring(html) # 输出修正后的HTML代码,是bytes类型 15 print(result.decode("utf-8"))
在上面代码中,首先导人lxml库的etree模块,然后声明了一段HTML文本,调用HTML类进行初始化,这样就成功构造了一个XPath解析对象。注意HTML文本中的最后一个li节点是没有闭合的,但是etree模块可以自动修正HTML文本。
调用tostring()方法即可输出修正后的HTML代码,结果是bytes类型。利用decode()方法将其转成str类型,在输出结果中,经过处理后,li节点的标签被补全,并且还自动添加了body、html节点。输出结果如下:
1 <html><body><div> 2 <ul> 3 <li class="item-0"><a href="link1.html">first item</a></li> 4 <li class="item-1"><a href="link2.html">second item</a></li> 5 <li class="item-inactive"><a href="link2.html">third item</a></li> 6 <li class="item-1"><a href="link4.html">fourth item</a></li> 7 <li class="item-0"><a href="link5.html">fifth item</a> 8 </li></ul> 9 </div> 10 </body></html>
还可以直接读取文本文件进行解析,示例如下:
from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = etree.tostring(html)
print(result.decode('utf-8'))
上面代码中test.html的内容是前面例子中的HTML代码。这次的输出结果中多了DOCTYPE的声明,对解析没有任何影响。输出如下所示:
1 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> 2 <html><body><div> 3 <ul> 4 <li class="item-0"><a href="link1.html">first item</a></li> 5 <li class="item-1"><a href="link2.html">second item</a></li> 6 <li class="item-inactive"><a href="link2.html">third item</a></li> 7 <li class="item-1"><a href="link4.html">fourth item</a></li> 8 <li class="item-0"><a href="link5.html">fifth item</a> 9 </li></ul> 10 </div></body></html>
3、 所有节点
可用 // 开头的XPath规则来选取所有符合要求的节点。用前面的HTML文本,如果要选取所有节点,可以这样实现:
1 from lxml import etree 2 html = etree.parse('./test.html', etree.HTMLParser()) 3 result = html.xpath('//*') # *表示选取所有节点 4 print(result) # 输出如下所示: 5 [<Element html at 0x2555e650208>, <Element body at 0x2555e650308>, 6 <Element div at 0x2555e650348>, <Element ul at 0x2555e650388>, 7 <Element li at 0x2555e6503c8>, <Element a at 0x2555e650448>, 8 <Element li at 0x2555e650488>, <Element a at 0x2555e6504c8>, 9 <Element li at 0x2555e650508>, <Element a at 0x2555e650408>, 10 <Element li at 0x2555e650548>, <Element a at 0x2555e650588>, 11 <Element li at 0x2555e6505c8>, <Element a at 0x2555e650608>]