了解Xpath之前先了解下XML、HTML。如果完全不知道是什么,建议系统学习HTML、XML下阅读下文。
XML
可扩展标记语言(英语:Extensible Markup Language,简称:XML),是一种标记语言。标记指计算机所能理解的信息符号,通过此种标记,计算机之间可以处理包含各种信息的文章等。如何定义这些标记,既可以选择国际通用的标记语言,比如HTML,也可以使用像XML这样由相关人士自由决定的标记语言,这就是语言的可扩展性。XML是从标准通用标记语言(SGML)中简化修改出来的。它主要用到的有可扩展标记语言、可扩展样式语言(XSL)、XBRL和XPath等。
示例-0
<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
<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
此例将会选择符合条件的元素a的target属性。 要求元素a:
- 具有属性
href且值为index.html; - 并且元素
a具有父元素div; - 并且父元素(
div)其自身具备class属性,值为header。
小试牛刀
使用text() 获取 h1、p标签中的文本内容。
<!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标签中的文本内容。
<!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标签里面的内容。
<!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的内容。
<!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