《从零开始学网络爬虫》之网络爬虫基础(使用XPath提取信息)3

即使我们了解了HTML的树结构,要设法解析这棵树以获取文本内容,那也将是一个十分艰巨的任务。好消息是,已经有人替我们实现了这些功能,通过一种被称为XPath的语言,就可以轻松地定位并提取元素、属性和文本。Scrapy爬虫框架中,也引入了XPath语言来定位和提取数据。

XPath全称XML Path Language,即XML路径语言。它是一门在XML文档中查找信息的语言。HTML与XML结构类似,也可以在HTML中查找信息。

XPath使用路径表达式来选取HTML文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。

例如Windows系统中,要指明桌面上的文件hello.py的路径,通常可以写成:C:\Users\tao\Desktop\hello.py,从C盘开始,使用反斜杠(\)逐级往下找,直到最终的目标。表2-2列举了常用的XPath路径表达式。

 

表2-2  XPath常用路径表达式

表达式

描述

示例

nodename

选取此节点的所有子节点

div,p,h1

/

从根节点选取(描述绝对路径)

/html

//

不考虑位置,选取页面中所有子孙节点

//div

.

选取当前节点(描述相对路径)

./div

..

选取当前节点的父节点(描述相对路径)

h1/../

@属性名

选取属性的值

@href,@id

text()

获取元素中的文本节点

//h1/text()

有计算机基础的读者对诸如:斜杠(/)、点(.)、两点(..)的用法一定不会陌生。即使不熟悉也没关系,因为XPath的语法实在太简单了。下面通过一些实例来加深对XPath用法的理解。

还是以电影排行的HTML文档为例,使用XPath提取页面信息。HTML代码和显示页面如图2-10所示。

《从零开始学网络爬虫》之网络爬虫基础(使用XPath提取信息)3

图2-10  电影排行代码和显示页面

  1. 安装lxml

使用XPath之前,要先安装Python的lxml库。lxml类库是一个HTML/XML的解析器。安装方法非常简单,在命令行中输入:

>pip install lxml

  1. 导入lxml

提取数据之前,先要导入lxml库的etree模块,再使用etree读取movies.html文件,生成一个节点树的对象,如下代码所示:

#导入lxml库的etree模块

from lxml import etree
#
解析movies.html文件,返回一个节点树的对象
html_selector = etree.parse("movies.html",etree.HTMLParser())

节点树的对象生成后,就可以使用XPath抽取数据了。

  1. 获取html元素

使用斜杠(/)从根节点开始获取html元素。

#获取根节点html的元素
root = html_selector.xpath("/html")
print(root)

运行结果:

[<Element html at 0x1a1a9395948>]

       可以看出,返回的root是一个列表,列表中存储一个Element(元素)类型的对象,对象中节点的名称为html。

  1. 获取title元素

可以使用斜杠(/)从根节点开始逐层查找元素的子节点。

#斜杠(/)获取节点title

title = html_selector.xpath("/html/head/title")
print(title)

运行结果:

[<Element title at 0x1a1a9395a48>]

需要注意的是,斜杠(/)在起始位置时,代表的是从根节点开始查找,其他位置代表查找子节点。

  1. 获取title的文本

可以使用text()获取节点title中的文本。

#text()获取节点title的文本
title_name = html_selector.xpath("/html/head/title/text()")
print(title_name)

运行结果:

['电影排行']

  1. 获取所有电影名称

电影名称所在的p节点相对根节点很远,如果从根节点逐层往下查找,XPath的表达式就会很长,如“/html/body/div/div/p/text()”。使用双斜杠(//)可以不考虑位置,获取页面中所有符合规则的子孙节点。

movie_name = html_selector.xpath("//p/text()")
print(movie_name)

运行结果:

['1.肖申克的救赎', '2.霸王别姬']

代码使用了双斜杠(//)抽取出页面中所有节点p的文本。注意抽取出来的所有数据,均是保存于列表中的。

       当然双斜杠(//)也可以放在路径表达式中的中间,如以下代码所示:

name = html_selector.xpath("/html//div[@id='content']/h1/text()")

运行结果:

['1.肖申克的救赎', '2.霸王别姬']

  1. 获取网页的编码格式

有时要获取的信息保存在属性中。例如页面的编码格式就是在meta标签的charset属性中。可以使用“@属性名”的方式获取属性值。

#使用@获取属性的值
meta = html_selector.xpath("//meta/@charset")
print(meta)

运行结果:

['UTF-8']

在爬虫中,经常使用@href获取超链接。

  1. 获取div的id属性值

通过斜杠(/)或双斜杠(//)可以查找子节点或子孙节点,那么如何通过子节点,查找父节点呢?这可以用两点(..)来实现。

attr = html_selector.xpath("//h1/../@id")
print(attr)

运行结果:

['content']

h1的后面是两点(..),即定位到h1的父节点<div id="content">,再使用@id获取属性id的值。

有时需要查找某个特定的节点或者包含某个指定值的节点,如获取属性idcontentdiv元素,或者获取第一个p节点的文本等,如果使用XPath路径表达式,实现起来就比较困难,这时就需要用到谓语了。谓语被嵌在方括号([ ])中,用于查找特定节点或指定值的节点。表2-3列举了常用的带谓语的路径表达式。还是以电影排行的HTML文档为例。

表2-3  常用的带谓语的路径表达式

谓语表达式

说明

结果

//div[@id='content']

选取属性id为content的div元素

[<Element div at 0x179ec884cc8>]

//div[@class]

选取所有带有属性class的div元素

[<Element div at 0x17398837cc8>]

//div/p[1]/text()

选取div节点中的第一个p元素的文本

['1.肖申克的救赎']

//div/p[2]/text()

选取div节点中的第二个p元素的文本

['2.霸王别姬']

//div/p[last()]/text()

选取div节点中的最后一个p元素的文本

['2.霸王别姬']

XPath功能非常强大,内置函数也很丰富,熟练使用能大大提高HTML信息的提取效率。在Scrapy爬虫中,主要也是使用Xpath定位和提取数据。更多XPath的用法,可以参考w3cschool(http://www.w3school.com.cn/xpath/index.asp)。

节选自书籍:《从零开始学Scrpay网络爬虫》,作者张涛,欢迎选购,京东,当当均有售。

下一节继续讲解:《从零开始学网络爬虫》之Scrapy网络爬虫框架