文章目录

  • 一. XPath库简介
  • 二. 安装lxml库
  • 三. XPath库详析
    • 1. XPath常用的规则
    • 2. 选取所有节点
    • 3. 选取子节点
    • 4. 选取父节点
    • 5. 以属性匹配
    • 6. 获取文本
    • 7. 获取属性
    • 8. 属性多值匹配
    • 9. 多属性匹配
    • 10. 按序选择
    • 11. 节点轴选择

一. XPath库简介

XPath 全称 XML Path Language,即 XML 路径语言,它是一门在 XML 文档中查找信息的语言。它最初是用来搜寻 XML 文档的,但是它同样适用于 HTML 文档的搜索。所以在做爬虫时,我们完全可以使用 XPath 来做相应的信息抽取。

XPath 的选择功能十分强大,它提供了非常简洁明了的路径选择表达式。另外,它还提供了超过100 个内建函数,用于字符串、数值、时间的匹配以及节点、序列的处理等。几乎所有我们想要定位的节点,都可以用 XPath 来选择。

XPath 于1999年11月16日成为 W3C 标准,它被设计为供 XSLT、XPointer 以及其他 XML 解析软件使用,更多的文档可以访问其官方网站:http s://www.w3.org/TR/xpath/

二. 安装lxml库

在 Ubuntu 虚拟机中,lxml 能通过 pip 来安装:

$ python -m pip install lxml
或者
$pip install lxml

其他平台下的安装可参考其他相应的安装方式,此处不做概述。

三. XPath库详析

1. XPath常用的规则

表达式 描述
nodename 选取此节点的所有子节点
/ 从当前节点选取直接子节点
// 从当前节点选取子孙节点
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性
* 选取所有元素节点与元素名
@* 选取所有属性
[@attrib] 选取具有给定属性的所有元素
[@attrib='value'] 选取给定属性具有给定值得所有元素
[tag] 选取所有具有指定元素的直接子节点
[tag='text'] 选取所有具有指定元素并且文本内容是text的节点

2. 选取所有节点

一般我们会用// 开头的XPath规则来选择所有符合要求的节点。我们看看下面的示例:

from lxml import etreehtml = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//*')
print(result)# 运行结果:
[<Element html at 0x7f8a1a02a748>, <Element body at 0x7f8a1a02a848>, <Element div at 0x7f8a1a02a888>, <Element div at 0x7f8a1a02a8c8>, <Element div at 0x7f8a1a02a908>, <Element ul at 0x7f8a1a02a988>, <Element li at 0x7f8a1a02a9c8>, <Element a at 0x7f8a1a02aa08>, <Element li at 0x7f8a1a02aa48>, <Element a at 0x7f8a1a02a948>, <Element li at 0x7f8a1a02aa88>, <Element a at 0x7f8a1a02aac8>, <Element li at 0x7f8a1a02ab08>, <Element a at 0x7f8a1a02ab48>, <Element div at 0x7f8a1a02ab88>, <Element span at 0x7f8a1a02abc8>, <Element a at 0x7f8a1a02ac08>, <Element strong at 0x7f8a1a02ac48>, <Element span at 0x7f8a1a02ac88>, <Element a at 0x7f8a1a02acc8>, <Element span at 0x7f8a1a02ad08>, <Element a at 0x7f8a1a02ad48>, <Element span at 0x7f8a1a02ad88>]

上述代码中的test.html 文件将用于下面所有的示例中,其内容如下:

<div class="header"><div class="header_contain"><div class="logo"></div><ul class="menu"><li><a href="../news/index.html">Home Page</a></li><li><a href="../course/course.html">Online Class</a></li><li><a href="../doc/docDownload.html">Download Document</a></li><li><a href="../news/search.html">Search</a></li></ul><div class="login"><span class="span Admin"><a href="../user/admin.html"><strong>USER ADMIN:</strong></a></span><span class="item"><a href="../user/login.html">SIGN IN | </a></span><span class="item"><a href="../user/register.html">SIGN UP | </a></span><span class="item" id="logout" >SIGN OUT</span></div></div>
</div>

上述例子获取的是整个HTML文本中所有的节点。运行结果返回的是一个列表,每一个元素都是Element类型,气候跟了节点名称,例如:html、body、div、ul、li、a等。

当然,选取节点时也可以匹配指定的节点名称,如果想获取所有span 节点,示例如下:

from lxml import etreehtml = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//span')
print(result)
print(result4[2])# 运行结果:
[<Element span at 0x7f491e251808>, <Element span at 0x7f491e251848>, <Element span at 0x7f491e251888>, <Element span at 0x7f491e2518c8>]
<Element span at 0x7f491e251888>

如果要取出其中一个对象,可以直接用中括号加索引来获取。

3. 选取子节点

我们通过 /// 即可查找元素的子节点或子孙节点。例如下面示例选取 li 节点的所有直接 a 子节点:

from lxml import etreehtml = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li/a')
print(result)# 运行结果:
[<Element a at 0x7f79ecb96848>, <Element a at 0x7f79ecb96888>, <Element a at 0x7f79ecb968c8>, <Element a at 0x7f79ecb96908>]

此处的 / 用于选取直接子节点,如果要选取所有的子孙节点,就可以使用 // ,例如:选取 class = "header_contain" 的 div 节点下的所有 a 节点:

from lxml import etreehtml = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//div[@class="header_contain"]//a')
print(result)# 运行结果:
[<Element a at 0x7f79ecb96748>, <Element a at 0x7f79ecb96a08>, <Element a at 0x7f79ecb96a48>, <Element a at 0x7f79ecb96a88>, <Element a at 0x7f79ecb96ac8>, <Element a at 0x7f79ecb96b48>, <Element a at 0x7f79ecb96b88>]

这里总共选取出七个 a 标签节点,其中前四个 a 节点是 li 节点下的子节点,而后三个是 span 节点下的子节点。代码中 [] 里的内容在XPath中被称为“谓语”,它被用来查找某个特定的节点或者包含某个指定的值的节点。谓语一般被嵌在方括号中。在 XPath 中,谓语是一个被经常用到的概念,下面的诸多示例中都有应用。

**注意:**如果通过 / 来获取某一节点下并不存在的直接子节点,则返回的列表为空。

4. 选取父节点

我们知道通过连续的 /// 可以查找子节点或子孙节点,那么假如我们知道了子节点,怎样来查找父节点呢?我们可以用 .. 来实现。例如,获取 class="menu" 的 ul 节点的父节点所拥有的 class 属性:

from lxml import etreehtml = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//ul[@class="menu"]/../@class')
print(result)# 运行结果:
['header_contain']

我们也可以通过 parent:: 来获取父节点,代码如下:

from lxml import etreehtml = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//ul[@class="menu"]/parent::*/@class')
print(result)

运行结果同上述示例。

5. 以属性匹配

在选取的时候,我们还可以用 @ 进行属性过滤。例如:下面示例中选取 class=“login” 的 div 节点:

from lxml import etreehtml = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//div[@class="login"]')
print(result)# 运行结果:
[<Element div at 0x7fbf6c6ef808>]

6. 获取文本

我们用 XPath 中的 text() 方法获取节点中的文本。下面我们尝试获取前面 test.html 文件中的 span 节点里的文本:

from lxml import etreehtml = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//span[@class="item"]/text()')
print(result)# 运行结果:
['SIGN OUT']

可以发现,运行结果获取了第三个 class="item" 的 span 节点下的文本内容,而前两个 span 节点下的文本内容并未获取到,这是为什么呢?因为 XPath 中 text() 前面是 / ,即选取直接子节点,很明显前两个 span 节点的直接子节点都是 a 节点,文本都是在 a 节点内部,所以这里匹配到的结果只有第三个 span 节点内部的文本内容。

那么,如果想要获取 class="item" 的 span 节点内部的文本,有两种方法:一是先选取 a 节点再获取其文本;二是使用 // ,下面我们就上面的例子进行修改并运行:

# 方法1:
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//span[@class="item"]/a/text()')
print(result)# 运行结果:
['SIGN IN | ', 'SIGN UP | ']# 方法2:
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//span[@class="item"]//text()')
print(result)# 运行结果:
['SIGN IN | ', 'SIGN UP | ', 'SIGN OUT']

上述两种方法的运行结果是不同的。我们不难发现其原因:方法1获取的是 span 节点下的 a 节点的文本内容,而方法2获取的是 span 节点下的所有节点的文本内容,包括 a 节点的文本内容。

通过上述示例我们可以发现两种方法之间的区别:如果想要获取子孙节点内部的所有文本,可以直接用 // 加 text() 的方式,这样可以保证获取到最全面的文本信息,但是可能会夹杂一些换行符(当某个节点未完全闭合时)等特殊字符;如果想要获取某些特定子孙节点下的所有文本,可以先选取到特定的子孙节点,然后再调用 text() 方法获取其内部文本,这样可以保证获取的结果是整洁的。

7. 获取属性

节点属性可以用@符号获取。例如:我们可以获取所有 li 节点下的所有 a 节点的 href 属性:

from lxml import etreehtml = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li/a/@href')
print(result)# 运行结果:
['../news/index.html', '../course/course.html', '../doc/docDownload.html', '../news/search.html']

这里我们通过 @href 即可获取节点的 href 属性。**注意:**此处和属性匹配的方法不同,属性匹配是中括号加属性名和值来限定某个属性,如 [@class="item"],而此处的 @href 指的是获取节点的某个属性,二者需要做好区分。

8. 属性多值匹配

有时候某些节点的某个属性可能有多个值,例如 test.html 文件中的 span 节点:

<span class="span Admin">USER ADMIN:</span>

这个 span 节点的 class 属性有两个值:span 和 Admin。此时如果依旧用之前的属性匹配获取节点或节点内容,那么返回的列表为空。这时需要用 contains() 函数解决这个问题:

from lxml import etreehtml = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//div[@class="login"]/span[contains(@class,"span")]/text()')
print(result)# 运行结果:
['USER ADMIN:']

contains() 方法需要传入两个参数:第一个参数传入属性名称,第二个参数传入属性值,只要此属性包含所传入的属性值就可以完成匹配。

9. 多属性匹配

另外,我们可能还遇到一种情况,那就是根据多个属性确定一个节点,这时就需要同时匹配多个属性。此时可以使用运算符 and 来连接。例如下面示例:

from lxml import etreehtml = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//span[@class="item" and @id="logout"]/text()')
print(result)# 运行结果:
['SIGN OUT']

这里的 span 节点有两个属性:id 和 name。要确定这个节点,需要同时根据 id 和 name 属性来选择。用 and 操作符连接两个条件,相连之后置于中括号内进行条件筛选。

XPath 中除了 and 操作符之外,还有很多运算符,常用的一些操作符如下表总结:

运算符 描述
or
and
mod 计算除法的余数
| 计算两个节点集
+ 加法
- 减法
* 乘法
div 除法
= 等于
!= 不等于
< 小于
<= 小于等于
> 大于
>= 大于等于

10. 按序选择

有时候,我们在选择的时候某些属性可能同时匹配了多个节点,但是我们只想要其中的某个节点,如第二个节点或者最后一个节点,这该怎么办呢?此时可以利用中括号传入索引的方法获取特定次序的节点,如下示例:

from lxml import etreehtml = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li[1]/a/text()')
print(result)
result = html.xpath('//li[last()]/a/text()')
print(result)
result = html.xpath('//li[position()<3]/a/text()')
print(result)
result = html.xpath('//li[last()-1]/a/text()')
print(result)# 运行结果:
['Home Page']
['Search']
['Home Page', 'Online Class']
['Download Document']

**注意:**此处的索引并不是从0开始,而是从1开始,这与python中的列表索引有所不同。

11. 节点轴选择

XPath 提供了很多节点轴选择方法,包括获取子元素 、兄弟元素、父元素、祖先元素等。如下示例:

from lxml import etreehtml = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//span[1]/ancestor::*')
print(result)
result = html.xpath('//span[1]/ancestor::div')
print(result)
result = html.xpath('//span[1]/attribute::*')
print(result)
result = html.xpath('//span[1]/child::a[@href="../user/admin.html"]')
print(result)
result = html.xpath('//span[1]/descendant::strong')
print(result)
result = html.xpath('//span[1]/following::*[2]')
print(result)
result = html.xpath('//span[1]/following-sibling::*')
print(result)# 运行结果:
[<Element html at 0x7f108143a708>, <Element body at 0x7f108143a808>, <Element div at 0x7f108143a848>, <Element div at 0x7f108143a888>, <Element div at 0x7f108143a8c8>]
[<Element div at 0x7f108143a848>, <Element div at 0x7f108143a888>, <Element div at 0x7f108143a8c8>]
['span Admin']
[<Element a at 0x7f108143a848>]
[<Element strong at 0x7f108143a888>]
[<Element a at 0x7f108143a808>]
[<Element span at 0x7f108143a848>, <Element span at 0x7f108143a8c8>, <Element span at 0x7f108143a908>]
  • 第一次选择返回结果是第一个 li 节点的所有祖先节点,包括 html、body、div和ul;
  • 第二次选择返回结果是祖先节点中的 div 节点;
  • 第三次选择返回结果是第一个 span 节点的所有属性值;
  • 第四次选择返回结果是 href 属性值为 “…/user/admin.html” 的 a 节点;
  • 第五次选择返回结果是 span 节点下的子节点 strong,而不是子节点 a;
  • 第六次选择返回结果是当前节点之后的所有节点中的第二个节点,即该 span 节点之后的第一个 span 节点下的 a 节点;如果不加索引,则返回结果是该 span 节点之后的所有 span 节点及其下面的 a 节点;
  • 第七次选择返回结果是当前的 span 节点之后的所有同级节点,即该 span 节点之后所有的 span 节点。

除了上述的几个节点轴之外还有其他一些节点轴,整理如下表所示:

轴名称 结果
ancestor 选取当前节点的所有先辈(父、祖父等)
ancestor-or-self 选取当前节点的所有先辈(父、祖父等)以及当前节点本身
attribute 选取当前节点的所有属性
child 选取当前节点的所有子元素
descendant 选取当前节点的所有后代元素(子、孙等)
descendant-or-self 选取当前节点的所有后代元素(子、孙等)以及当前节点本身
following 选取文档中当前节点的结束标签之后的所有节点
following-sibling 选取文档中当前节点的结束标签之后的所有同级节点
preceding 选取文档中当前节点的开始标签之前的所有节点
preceding-sibling 选取当前节点之前的所有同级节点
namespace 选取当前节点的所有命名空间节点
parent 选取当前节点的父节点
self 选取当前节点

如果想查询更多 XPath 的用法,可以查看: http://www.w3school.com.cn/xpath/index.asp。

爬虫基础(6)网页解析之XPath库相关推荐

  1. 爬虫基础(5) -网页解析

    网页解析 一,BeautifulSoup 安装:pip install beautifulsoup4 官方文档:https://www.crummy.com/software/BeautifulSou ...

  2. python自带网页解析器_Python爬虫Chrome网页解析工具-XPath Helper

    之前就说过Python爬虫中Xpath的用法,相信每一个写爬虫.或者是做网页分析的人,都会因为在定位.获取XPath路径上花费大量的时间,在没有这些辅助工具的日子里,我们只能通过搜索HTML源代码,定 ...

  3. python网页结构分析_Python爬虫基础之网页组成解析

    当我们用浏览器访问网站时,每个网页的大不相同,你是否想过它为什么会呈现多种不同的样式呢?就让我们一起了解一下网页的基本组成.结构和节点等内容吧! 网页的组成 网页可以分为三大部分--HTML.CSS和 ...

  4. 网页解析之xpath

    网页解析之xpath 一.简介 二.语法 三.xpath练习: 爬取全书网玄幻魔法分类中的完本小说 import requests from lxml import etree import re i ...

  5. 爬虫必备技术之解析Html工具库

    神器介绍 今天我要介绍一款操作Html文档非常好用的Java插件,强烈安利!因为实在太好用了! " Jsoup 是一款纯Java实现,可以非常方便读取和操作Html文档的一款插件.她的API ...

  6. python3.4.4实现网页爬虫基础之网页下载器三种方法

    这是是慕课网<Python开发简单爬虫>中网页下载器的三种实现方法,课程用的是python2.7,这里用最新的3.4.4实现出来,分享给新人: import urllib.request ...

  7. python爬虫基础教程115_Python解析网页源代码中的115网盘链接实例

    本文实例讲述了python解析网页源代码中的115网盘链接的方法.分享给大家供大家参考.具体方法分析如下: 其中的1.txt,是网页http://bbs.pediy.com/showthread.ph ...

  8. java爬虫面试题_Java 网络爬虫基础知识入门解析

    前言 说起网络爬虫,大家想起的估计都是 Python ,诚然爬虫已经是 Python 的代名词之一,相比 Java 来说就要逊色不少.有不少人都不知道 Java 可以做网络爬虫,其实 Java 也能做 ...

  9. 爬虫基础练习: 基于 java + Jsoup + xpath 爬取51job网站

    最基本的网页爬虫练习 爬取51jb网站,并将数据写入Excel中 需要导入jsoup包和POI相关包 JSoup简介 jsoup是一款Java的HTML解析器,主要用来对HTML解析, 可通过DOM, ...

最新文章

  1. linux vi 清除所有,Vi命令:如何删除全部内容
  2. POJ-3687-Labeling Balls
  3. 超级智能玩具《小小机器人》|全新50种玩法,创造力之源
  4. python 类似wordpress_python,_python 有没有类似WordPress的这种库?,python - phpStudy
  5. [转载] Java中的50个关键字
  6. 解决爬取网站过程中遇到的HTTP Error 302错误和中文乱码问题
  7. 基于yolo的口罩识别(开源代码和数据集)
  8. 用Pylint规范化Python代码,附PyCharm配置
  9. 虚拟局域网(VLAN)中端口tagged和untagged在多种情况下的作用
  10. 用UNet做PASCAL VOC2012图像分割
  11. 技嘉h310主板前置音频没声音_电脑前置插孔没声音 前置音频没声音解决方法
  12. 华硕 Asus TUF B360M+INTEL i5+Sapphire AMD VEGA56黑苹果EFI引导文件
  13. Graph Neural Network(GAE,GVAE,ARGA)
  14. surface装双系统后在移动硬盘上装ubuntu系统
  15. 罗永浩与他的锤子手机!
  16. 深度学习入门笔记(十六):计算机视觉之边缘检测
  17. 微信QQ等主流应用上线国产系统UOS:界面曝光
  18. 关于ARM的内核架构
  19. https安全传输揭秘 1
  20. Extract High-frequency Data via PC SAS

热门文章

  1. win10 UEFI+BMR无损改为UEFI+GPt
  2. matlab 类写法,matlab中函数表达式的写法
  3. 18级的谷歌卫星地图纹理如何匹配到15级的高程
  4. 往事如烟 - 老钟18
  5. POI生成word文档,图片显示为空白或不显示
  6. 深度学习之计算机视觉方向的知识结构
  7. 蓝牙耳机什么牌子好?十大蓝牙耳机品牌排行榜!
  8. Gaia蓝牙音箱android开发,Windows7系统配置蓝牙模块GAIA功能的方法
  9. 最近老是卖了就涨 买了就跌呢
  10. 九万字图文讲透彻 Linux 电源管理及实例分析