摘要
XPath 使用路径表达式来选取 XML 文档中的节点或节点集。节点是通过沿着路径 (path) 或者步 (steps) 来选取的。可以通过lxml模块来使用XPath语法
XPath的语法
初步筛选:
<img src="/wp-content/uploads/2018/08/XPath.png" />
注意:
html文档的根结点是html,但是"/body"并不会返回body,因为此时表示从根结点开始选取,实际上路径位于根结点一级,所以
这级只有html,没有body。需要从根结点选取body,需要表明从根结点的下一级选取,即"./body"
比如:
./body/div//span/@href
表示文档中body结点下儿子结点中所有的div结点下的所有子孙结点中的span结点的儿子结点中的href属性
即:
<html>
...
<body>
<!-- 儿子中所有的div -->
<div>
<!-- 子孙中所有的span -->
<span>
<!-- 儿子结点中所有的href属性的值 -->
href的值
</span>
</div>
</body>
</html>
我们发现,每一级的标签名都默认了选取“所有”该名字标签的前提,而body标签我之所以不用所有的来定义,只是因为一个html文档中只有一个body标签
/和//的区分是很重要的,后面进一步筛选结点也应该在它们的前提下
进一步筛选:
谓语:写在在[]中
相关函数 :
last() : 获取最后一个元素的序号
position() : 获取元素的位置序号
在[]中的可以是序号(从1开始),可以是属性,也可以是逻辑表达式(等式直接用"=")
比如:
./body/div[last()-1]/div[@id]/span[position()<3]/a[@href="#"]
表示选取文档中的根结点下的body的儿子结点中倒数第二个div结点的儿子结点中含id属性的所有div结点的儿子结点中前面两个span结点中的儿子结点中href属性等于"#"的a结点
即:
<html>
<body>
.......
<!-- 最后一个div -->
<div>
<!-- 带有id属性的所有div -->
<div id = "...">
<!-- 前面两个span-->
<span>
<!-- href属性等于"#"的a -->
<a href = "#"></a>
</span>
</div>
</div>
</body>
</html>
我们知道,上面选取方式都比较具体,那么有没有一种比较宽泛的方式选取结点呢?有,通配符
- 匹配任何元素节点。
@* 匹配任何属性节点。
node() 匹配任何类型的节点。
text() 匹配文本结点
string() 匹配所有文本结点
比如:
./body//[@]//*:
表示文档中body结点下的所有儿子结点的带属性的儿子结点的任何类型的子孙结点
即:
<html>
<body>
<!-- 所有任何类型的儿子结点 -->
<node>
<!-- 所有带属性的任何儿子结点-->
<node attr>
<!-- 所有任何类型的子孙结点 -->
<node>...</node>
</node>
</node>
</body>
</html>
通过上面的知识,我们已经可以很好的选取html文档中的元素了。
总结:
获取XPath路径,就是先使用/或//等初步筛选元素,然后通过谓语或者通配符进行进一步筛选
说明
以下代码默认已导入lxml模块中的etree模块
from lxml import etree
解析过程如下:
- 通过html内容生成selector
- 通过selector的xpath方法得到符合匹配条件的元素,返回列表
- 遍历列表或者获取列表中特定元素
比如:
# html内容
html = "<!DOCTYPE HTML><html><body><p><a>123</a></p></body></html>"
# 生成解析html的selector
selector = etree.HTML(html)
# 获取符合条件的元素
content = selector.xpath("//a")
print(content[0].text) # 123
# 或者
content = selector.xpath("//a/text()")
print(content[0]) # 123
补充内容
没有特定需求,其实不需要用到补充内容。
1.选取多个路径:
使用"|"运算符连接两个路径表示或,比如:
./body/div | ./body/div//a
表示./body/div 或 ./body/div//a
2.谓语中能够使用的运算符:
<img src="/wp-content/uploads/2018/08/XPathOpera.png" />
- 轴:
通过“轴名::”的前缀形式改变/的含义,可用改变选取元素的顺序,比如/默认为子元素轴可用/child:表示
a/parent::node() :表示a所有的任意类型父节点
比如,./body//a/parent::node()
表示选取文档中body的子孙结点中的所有a结点的任意类型的父节点
即:
<html>
<body>
<!--a的所有任意类型的父节点 -->
<node>
<!-- body的所有子孙结点中的a结点,记为a -->
<a></a>
</node>
</body>
</html>
可以容易发现,parent::改变了在文档树中从上到下选取元素的顺序
相关的轴:
<img src="/wp-content/uploads/2018/08/XPathAxes.jpg">