爬虫入门xpath

​ 衔接上一篇爬虫入门。为了更好爬输出,有必要Xpath用法有一定了解。

​ 了解Xpath之前先了解下XML、HTML。如果完全不知道是什么,建议系统学习HTML、XML下阅读下文。

XML

可扩展标记语言(英语:Extensible Markup Language,简称:XML),是一种标记语言。标记指计算机所能理解的信息符号,通过此种标记,计算机之间可以处理包含各种信息的文章等。如何定义这些标记,既可以选择国际通用的标记语言,比如HTML,也可以使用像XML这样由相关人士自由决定的标记语言,这就是语言的可扩展性。XML是从标准通用标记语言(SGML)中简化修改出来的。它主要用到的有可扩展标记语言、可扩展样式语言(XSL)、XBRL和XPath等。

示例-0

1
2
3
4
5
6
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>

HTML

超文本标记语言(英语:HyperText Markup Language,简称:HTML)是一种用于创建网页的标准标记语言。HTML是一种基础技术,常与CSS、JavaScript一起被众多网站用于设计令人赏心悦目的网页、网页应用程序以及移动应用程序的用户界面。网页浏览器可以读取HTML文件,并将其渲染成可视化网页。HTML描述了一个网站的结构语义随着线索的呈现,使之成为一种标记语言而非编程语言。

HTML元素是构建网站的基石。HTML允许嵌入图像与对象,并且可以用于创建交互式表单,它被用来结构化信息——例如标题、段落和列表等等,也可用来在一定程度上描述文档的外观和语义。HTML的语言形式为尖括号包围的HTML元素(如<html>),浏览器使用HTML标签和脚本来诠释网页内容,但不会将它们显示在页面上。

HTML可以嵌入如JavaScript的脚本语言,它们会影响HTML网页的行为。网页浏览器也可以引用层叠样式表(CSS)来定义文本和其它元素的外观与布局。维护HTML和CSS标准的组织万维网联盟(W3C)鼓励人们使用CSS替代一些用于表现的HTML元素。

示例-1

1
2
3
4
5
6
<html>
<body>
<h1>我的第一个标题</h1>
<p>我的第一个段落。</p>
</body>
</html>

XML和HTML有啥关系呢?

  • XML是被设计用来描述数据的,重点是:什么是数据,如何存放数据
  • HTML是被设计用来显示数据的,重点是:显示数据以及如何显示数据更好上面。

Xpath

XPath即为XML路径语言(XML Path Language),它是一种用来确定XML文档中某部分位置的语言。

XPath基于XML的树状结构,提供在数据结构树中找寻节点的能力。起初XPath的提出的初衷是将其作为一个通用的、介于XPointer间的语法模型。但是XPath很快的被开发者采用来当作小型查询语言。XPath 用于在 XML 文档中通过元素和属性进行导航。

标识法

最常见的XPath表达式是路径表达式(XPath这一名称的另一来源)。路径表达式是从一个XML节点(当前的上下文节点)到另一个节点、或一组节点的书面步骤顺序。这些步骤以“/”字符分开,每一步有三个构成成分:

  • 轴描述(用最直接的方式接近目标节点)
  • 节点测试(用于筛选节点位置和名称)
  • 节点描述(用于筛选节点的属性和子节点特征)

一般情况下,我们使用简写后的语法。虽然完整的轴描述是一种更加贴近人类语言,利用自然语言的单词和语法来书写的描述方式,但是相比之下也更加罗嗦。

简写后的语法

最简单的XPath如下:

  • /A/B/C

在这里选择所有符合规矩的C节点:C节点必须是B的子节点(B/C),同时B节点必须是A的子节点(A/B),而A是这个XML文档的根节点(/A)。此时的这种描述法类似于磁盘中文件的路径,从盘符开始顺着一级一级的目录最终找到文件。

这里还有一个复杂一些的例子,包含了全部构成成分(请详细的看):

  • A//B/*[1]

此时选择的元素是:在B节点下的第一个节点(B/*[1]),不论节点的名称如何(*);而B节点必须出现在A节点内,不论和A节点之间相隔几层节点(//B);与此同时A节点还必须是当前节点的子节点(A,前边没有/)。

轴描述语法

坐标 名称 说明 缩写语法
child 子节点 比自身节点深度大的一层的节点,且被包含在自身之内 默认,不需要
attribute 属性 @
descendant 子孙节点 比自身节点深度大的节点,且被包含在自身之内 不提供
descendant-or-self 自身引用及子孙节点 //
parent 父节点 比自身节点深度小一层的节点,且包含自身 ..
ancestor 祖先节点 比自身节点深度小的节点,且包含自身 不提供
ancestor-or-self 自身引用及祖先节点 不提供
following 下文节点 按纵轴视图,在此节点后的所有完整节点,即不包含其祖先节点 不提供
preceding 前文节点 按纵轴视图,在此节点前的所有完整节点,即不包含其子孙节点 不提供
following-sibling 下一个同级节点 不提供
preceding-sibling 上一个同级节点 不提供
self 自己 .
namespace 名称空间 不提供

选取节点

表达式 描述
nodename 选取此节点的所有子节点。
/ 从根节点选取。
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
. 选取当前节点。
.. 选取当前节点的父节点。
@ 选取属性。

节点测试

节点测试包括特定节点名或者更一般的表达式。至于XML里命名空间前缀gs已定义的文件,//gs:enquiry将找到所有在那命名空间里enquiry的节点。

其他节点格式:

  • comment()

    寻找XML注释节点,例如<!-- 注释 -->

  • text()

    寻找某点的文字型别,例如hello<k>hello</k>

  • node()

    寻找所有点

节点描述

节点描述为一个逻辑真假表达式,任何真假判断表达式都可在节点后方括号里表示,这条件必须在XPath处理这个节点前先被满足。在某一步骤可有多少个描述并没有限制。

范例如下: //a[@href='index.html'],这将检查元素a有没有href属性,并且该它的值是index.html

复杂一些的范例如下:

  • //a[@href='index.html'][../div/@class='header']/@target

  • //a[@href='index.html'][name(..)='div'][../@class='header']/@target

此例将会选择符合条件的元素atarget属性。 要求元素a

  • 具有属性href且值为index.html
  • 并且元素a具有父元素div
  • 并且父元素(div)其自身具备class属性,值为header

小试牛刀

使用text() 获取 h1、p标签中的文本内容。

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>我的第一个标题</h1>
<p>我的第一个段落。</p>
</body>
</html>

获取h1、p标签里面的内容

//h1/text() >> 我的第一个标题

//p/text() >> 我的第一个段落。


使用text() 获取 div id = main h1、p标签中的文本内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>我的第一个标题</h1>
<p>我的第一个段落。</p>
<div>
<div id="main">
<h1>我的第二个标题</h1>
<p>我的第二个段落。</p>
</div>
</div>
<div id="king">
<div>
<h1>我的第三个标题</h1>
<p>我的第三个段落。</p>
</div>
</div>
</body>
</html>

获取不同节点h1、p标签里面的内容

//div[@id=’main’]/h1/text() >> 我的第二个标题

//div[@id=’main’]/p/text() >> 我的第二个段落。

//div[@id=’main’]/h1/text() >> 我的第二个标题

//div[@id=’main’]/p/text() >> 我的第二个段落。

//div[@id=’king’]/div/h1/text() >> 我的第三个标题

//div[@id=’king’]/div/p/text() >> 我的第三个段落。


获取第一个div内h1、p标签里面的内容,和第二个div内h1、p标签里面的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<h1>我的第二个标题</h1>
<p>我的第二个段落。</p>
</div>
<div>
<h1>我的第三个标题</h1>
<p>我的第三个段落。</p>
</div>
</body>
</html>

获取相同节点h1、p标签里面的内容。

//div[1]/h1/text() >> 我的第二个标题

//div[1]/p/text() >> 我的第二个段落。

//div[2]/h1/text() >>我的第三个标题

//div[2]/p/text() >> 我的第三个段落。


获取ul中li title = 4的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<h1>我的第二个标题</h1>
<p>我的第二个段落。</p>
</div>
<div>
<h1>我的第三个标题</h1>
<p>我的第三个段落。</p>
<ul>
<li class="item-0">1</li>
<li class="item-1">2</li>
<li class="item-2">3</li>
<li class="item-3" title="4">4</li>
</ul>
</div>
</body>
</html>

//ul/li[@title=’4’]/text() >> 4

END