XPath基础

第1关:XPath 路径表达式

任务描述

本关任务:根据给定的 xml 文档,使用 XPath 表达式选取指定内容。

相关知识

为了完成本关任务,你需要了解 XPath 路径表达式的基本语法,理解节点的基本类型,完成节点的获取、属性的匹配获取、多属性的匹配获取、文本的获取、按序选择等。

节点的基本类型

XPath 是一门在 XML 文档中查找信息的语言,虽然是被设计用来搜寻 XML 文档的,但是它也能应用于 HTML 文档,并且大部分浏览器也支持通过 XPath 来查询节点。在 Python 爬虫开发中,经常使用 XPath 查找提取网页中的信息,因此 XPath 非常重要。

在 XPath 中, XML 文档是被作为节点树来对待的,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点。树的根被称为文档节点或者根节点。以下面的 XML 文档为例进行说明:

 
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <bookstore>
  3. <book>
  4. <title lang="eng" class="good">Harry Potter</title>
  5. <price>29.99</price>
  6. </book>
  7. <book>
  8. <title lang="eng">Learning XML</title>
  9. <price>39.95</price>
  10. </book>
  11. </bookstore>
  • 根节点(Root Node)

  根节点是一棵树的最顶层,根节点是唯一的。树上其它所有元素节点都是它的子节点或后代节点。对根节点的处理机制与其它节点相同。对树的匹配总是先从根节点开始。文档中的<bookstore>即为根节点。

  • 元素节点(Element Nodes)

  元素节点相对应的是文档中每个元素,一个元素节点的子节点可以为元素节点、注释节点、处理指令节点和文本节点。元素节点可以定义一个唯一的标识(id)。元素节点可以有拓展名,由两部分组成:命名空间 URL 和本地命名。文档中的<book>即为元素节点。

  • 文本节点(Text Nodes)

  文本节点包含一组字符数据,任何一个文本节点都没有相邻的兄弟文本节点,而且文本节点没有扩展名。文档中的Learning XML即为文本节点。

  • 属性节点(Attribute Nodes)

  每个元素节点有一个相关联的属性节点集合,元素是每个属性节点的父节点,但属性节点却不是其父元素的子节点。这就是说,通过查找元素的子节点可以匹配出元素的属性节点,但反过来不成立,只是单向的。再有,元素的属性节点没有共享性,也就是说不同的元素节点不共有同一个属性节点。文档中的lang="eng"即为属性节点。

  • 命名空间节点(Namespace Nodes)

  每个元素节点都有一个相关联的命名空间节点集。在 XML 文档中,命名空间是通过保留属性声明的。因此,在 XPath 中,该类节点与属性节点极为相似,它们与父元素之间的关系是单向的,并且不具有共享性。

  • 处理指令节点(Processing Instruction Nodes)

  处理指令节点对应于 XML 文档中的每一条处理指令。它也有扩展名,扩展名的本地命名指向处理对象,而命名空间部分为空。

  • 注释节点(Comment Nodes)

  注释节点对应于文档中的注释。

Xpath路径表达式的基本语法

XPath 使用路径表达式来选取 XML 文档中的节点或节点集。节点是沿着路径(path)或者步(steps)来选取的。接下来介绍如何选取节点,首先了解一下常用的路径表达式,来进行节点的选取,如下表所示:

表达式描述
nodename选取此节点的所有子节点
/从根节点选取
//选择任意位置的某个节点
.选取当前节点
..选取当前节点的父节点
@选取属性

根据路径表达式的规则,我们对上文的的 XML 文档进行节点选取,如下表所示。

XPath路径表达式含义
bookstore选取 bookstore 元素的所有子节点
/bookstore选取根元素 bookstore
/bookstore/book/text()选取属于 bookstore 子元素的 book 元素下的所有文本内容
//book选取所有 book 子元素,而不管它们在文档中位置
//@eng选取名为 eng 的所有属性

上面选取的例子最后实现的效果都是选取了所有符合条件的节点,是否能选取某个特定的节点或者包含某一个指定的值的节点呢?这就需要用到谓语,谓语被嵌在方括号中,谓语的用法如下表所示。

XPath路径表达式含义
/bookstore/book[1]选取属于 bookstore 子元素的第一个 book 元素
/bookstore/book[last()]选取属于 bookstore 子元素的最后一个 book 元素
/bookstore/book[last()-1]选取属于 bookstore 子元素的倒数第二个 book 元素
/bookstore/book[position()<3]选取最前面的两个属于 bookstore 元素的子元素的 book 元素
//title[@lang]选取所有拥有名为 lang 的属性的 title 元素
//title[@lang='eng']选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性
//title[@lang='eng' and @class="good"] 选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性和值为good的class属性
/bookstore/book[price>35.00]选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00
/bookstore/book[price>35.00]/title选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00

XPath 在进行节点选取的时候可以使用通配符*匹配未知的元素,同时使用操作符|一次选取多条路径,使用示例如下表所示。

XPath路径表达式含义
/bookstore/*选取 bookstore 元素的所有子元素
//*选取文档中的所有元素
//title[@*]选取所有带有属性的 title 元素
//book/title 丨 //book/price选取 book 元素的所有 title 和 price 元素
//title 丨 //price选取文档中的所有 title 和 price 元素
/bookstore/book/title 丨 //price选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素
1.选取bookstore元素的所有子节点
********** Begin *********
bookstore
*********** End **********
 
2.选取所有拥有名为 lang 的属性的 title 元素
********** Begin *********
//title[@lang]
*********** End **********
 
3.选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性和值为good的class属性
********** Begin *********
//title[@lang='eng' and @class="good"]
*********** End **********
 
4.选取属于 bookstore 子元素的book元素下的所有文本内容
********** Begin *********
/bookstore/book/text()
*********** End **********
 
5.选取属于 bookstore 子元素的第一个 book 元素
********** Begin *********
/bookstore/book[1]
*********** End **********

第2关:XPath 轴定位

 

任务描述

本关任务:根据给定的 xml 文档,使用 XPath 表达式选取指定内容。

相关知识

为了完成本关任务,你需要了解:XPath 中如何使用轴定位。

在爬虫里面经常要用到定位,XPath 定位有着举足轻重的地位,因为它功能很强大。结合它里面的文本定位、模糊定位、逻辑定位等,基本能搞定所有的元素定位问题。

轴定义了所选节点与当前节点之间的树关系。在 Python 爬虫开发中,提取网页中的信息时,会遇到这种情况:首先提取到一个节点的信息,然后想在在这个节点的基础上提取它的子节点或者父节点,这时候就会用到轴的概念。轴的存在会使提取变得更加灵活和准确。

在上一关,我们了解了位置路径表达式中的相对位置路径、绝对位置路径和步的概念。位置路径可以是绝对的,也可以是相对的。绝对路径起始于正斜杠/,而相对路径不会这样。在两种情况中,位置路径均包括一个或多个步,每个步均被斜杠分割: /step/step/..(绝对位置路径),step/step/.. (相对位置路径)。

步(step)包括:轴(axis)、节点测试( node-test)、零个或者更多谓语( predicate),用来更深入地提炼所选的节点集。XPath 中的轴名及含义如下表所示:

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

轴需要通过步的语法,来实现节点的选取。步的语法为:轴名称::节点测试[谓语],大家可能觉比较抽象。下面通过案例演示来帮助理解,XML 文档示例如下所示:

 
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <bookstore>
  3. <book>
  4. <title lang="eng" class="good">Harry Potter</title>
  5. <price>29.99</price>
  6. </book>
  7. <book>
  8. <title lang="eng">Learning XML</title>
  9. <price>39.95</price>
  10. </book>
  11. </bookstore>

以下为通过步的语法选取节点的示例,表中的截图还演示了如何利用上一关的 XPath 表达式选取同样的节点,可以看出步的语法更简洁。

步的语法含义
child::book选取所有属于当前节点的子元素的 book 节点
例如:

,


选取了bookstore下的所有(两个)book节点
attribute::lang选取当前节点的 lang 属性
例如:

,


选取的为第一个book节点下的所有title节点中lang的属性
child::*选取当前节点的所有子元素
例如:

,


选取的为第一个book节点下的所有节点
attribute::*选取当前节点的所有属性
例如:

,


选取的为第一个book节点下的所有title节点中所有属性
child::text()选取当前节点的所有文本子节点
child::node()选取当前节点的所有子节点
descendant::book选取当前节点的所有 book 后代
例如:

,


选取了所有book节点
ancestor::book选择当前节点的所有 book 先辈。
例如:

,


选取的第一个price的父亲节点book
ancestor-or-self::book选取当前节点的所有 book 先辈以及当前节点(如果此节点是 book 节点)
例如:

,


当当前节点为book节点时,选取当前节点以及它的父亲节点book
child::*/child::price选取当前节点的所有 price 孙节点
例如:

,


选取所有的price节点
1.选取所有属于当前节点的子元素的 book 节点
********** Begin *********

child::book
*********** End **********

2.选取当前节点的 lang 属性
********** Begin *********

attribute::lang
*********** End **********

3.选取当前节点的所有 price 孙节点
********** Begin *********

child::*/child::price
*********** End **********

第3关:XPath 解析 

任务描述

本关任务:编写解析 HTML 文件的 Python 程序。

相关知识

为了完成本关任务,你需要掌握:Python 环境下使用 XPath 对 HTML 文件进行解析。

lxml的安装

lxml 是 一个 HTML/XML 的解析器,主要的功能是解析和提取 HTML/XML 数据。lxml 和正则一样,也是用 C 实现的,是一款高性能的 Python HTML/XML 解析器,我们可以利用之前学习的 XPath 语法,来快速地定位特定元素以及节点信息。

如果本地 Python 环境没有安装 lxml,可以在命令提示符窗口输入命令pip install lxml,安装 lxml 模块,如下图所示。

lxml的使用

使用 lxml,需要先导入相关包,语法如下:

 
  1. from lxml import etree

现在有如下 HTML 代码,需要获取第一个 book 节点下的 title 节点中的 class 的属性值:

 
  1. <html>
  2. <head></head>
  3. <body>
  4. <bookstore>
  5. <book>
  6. <title lang="eng" class="good">Harry Potter</title>
  7. <price>29.99</price>
  8. </book>
  9. <book>
  10. <title lang="eng">Learning XML</title>
  11. <price>39.95</price>
  12. </book>
  13. </bookstore>
  14. </body>
  15. </html>

使用 xpath 表达式解析网页之前,需要获取元素树对象,这里有两种方法:

  • 如果上述代码为本地文件,并且文件名为test.html,获取元素树对象的代码如下所示:
     
      
    1. parse = etree.HTMLParser(encoding='utf-8') # 添加编码
    2. tree = etree.parse('test.html', parse) # 指定本地HTML文件读取
  • 如果上述代码为字符串类型变量,并且变量名为html,获取元素树对象的代码如下所示:
     
      
    1. parse = etree.HTMLParser(encoding='utf-8') # 添加编码
    2. tree = etree.HTML(html, parse) # html为python字符串

获取元素树对象后,就可以使用 XPath 表达式解析网页了,代码如下所示:

 
  1. result = tree.xpath(xpath表达式) # 返回类型为列表

完成上述指定任务有多种实现方式,以下演示了四种不同的 XPath 表达式,都能够获取第一个 book 节点下的 title 节点中的 class 的属性值。

 
  1. # 相对路径 book 节点选择
  2. print(tree.xpath('//book[1]/title/@class')[0])
  3. # 相对路径 title 节点存在 class 属性条件选择
  4. print(tree.xpath('//title[@class]/@class')[0])
  5. # 同上, 但是使用了轴选择 class 属性值
  6. print(tree.xpath('//title[@class]/attribute::class')[0])
  7. # 绝对路径常规选择
  8. print(tree.xpath('/html/body/bookstore//book[1]/title/@class')[0])

以上代码都可以获取图中红框部分的内容。

,

# 导入lxml库
from lxml import etree

# 读取lll.html文件并转化为元素树对象
parse = etree.HTMLParser(encoding='utf-8')
tree = etree.parse('src/step3/lll.html', parse)

# 补充xpath表达式,获取所有书的名称
# ********** Begin ********* #       
print(tree.xpath('//book/title/text()'))
# *********** End ********** #

# 补充xpath表达式,获取所有书的价格
# ********** Begin ********* #       
print(tree.xpath('//book/price/text()'))
# *********** End ********** #

# 填写代码, 获取价格低于30的书名
# ********** Begin ********* #       

A=tree.xpath('//book[price<30.00]/title/text()')
print(A[0])
# *********** End ********** #


 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值