Python爬虫开发中,xpath速度是比较快的使用较为灵活,是在网页定位元素的较优选择。
虽然文本解析库中正则表达式的性能比Xpath要强,但是企业级爬虫开发里,Xpath比正则要更为常见得多。
对于我的话,“不管是什么魔法、什么妖法,只有有用的都会用。”
而往网上的文章,对于Xpath语句的记录都比较零碎,所以在这里统一整理一下,自己可能常用的Xpath。


运行环境 Runtime environment

1
2
3
操作系统 : Windows10  
IDE: JetBrains Python 2018.2.4 x64
语言: Pytthon 3.6.5

准备

在python中安装Xpath,在终端输入完成安装。
通常来讲安装是不会出现问题,如果出现了请根据报错搜索解决。
高版本的lxml是没有etree方法的,所以网上的代码不一定有用多数因为这个原因。

pip install lxml

测试用的前端网页代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<!DOCTYPE html>
<html>
<head>
<title>xpath test</title>
</head>
<body>
<div name="q">
<div>
<ul>
<li>时间</li>
<li>地点</li>
<li>任务</li>
</ul>
</div>
<div id='testid' data-h="first">
<h2>这里是个小标题</h2>
<ol>
<li data="one">1</li>
<li data="two">2</li>
<li data="three">3</li>
</ol>
<ul>
<li code="233">233</li>
<li code="666">666</li>
<li code="888">888</li>
</ul>
</div>
<div>
<h3>H3装了好多东西
<a href="http://www.baidu.com">百度一下</a>
<a href="http://www.baidu.com">谷歌一下</a>
<ul>
<li>test1</li>
<li>test2</li>
</ul>
</h3>
</div>
<div id="countList">
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
</div>
</div>
</body>
</html>

Xpath 应用

匹配节点下全匹配 .//

  • //获取文档中所有匹配的节点,
  • .获取当前节点,有的时候我们需要获取当前节点下的所有节点,
  • .//一定要结合.使用//,否则都会获取整个文档的匹配结果。

匹配包含某标签/属性的所有的属性值 //

  • 如果使用了类似Xpath Helper之类的浏览器插件,善用全匹配,可以有效的缩短插件自动生成的Xpath
    1
    2
    3
    4
    print(tree.xpath('//@code')) #匹配所有带有code属性的属性值

    print(tree.xpath('//li')) #匹配所有li标签

选取多个路径|

  • 该符号目的是为了在一个xpath中写多个表达式,用|分开,每个表达式互不干扰。
  • 符合匹配表达式的元素都会放到同一个列表里
    1
    print(tree.xpath('//div[@id="testid"]/h2/text() | //li[@data]/text()'))#多个匹配条件

position定位

  • 该符号目的是为了在一个xpath中写多个表达式,用|分开,每个表达式互不干扰。
  • 符合匹配表达式的元素都会放到同一个列表里
    1
    print(tree.xpath('//*[@id="testid"]/ol/li[position()=2]/text()')[0] )

Axes(轴)

对常用的Xpath轴先做简单的整合,再做分别测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
XPath 坐标轴:坐标轴用于定义当对当前节点的节点集合。

坐标轴名称 含义

ancestor 选取当前节点的所有先辈元素及根节点。

ancestor-or-self 选取当前节点的所有先辈以及当前节点本身。

ttibute 选取当前节点的所有属性。

child 选取当前节点的所有子元素。

descendant 选取当前节点的所有后代元素。

descendant-or-self 选取当前节点的所有后代元素以及当前节点本身。

following 选取文档中当前节点的结束标签之后的所有节点。

following-sibling 选取当前节点之后的所有同级节点。

namespace 选取当前节点的所有命名空间节点。

parent 选取当前节点的父节点。

preceding 选取当前节点的开始标签之前的所有节点。

preceding-sibling 选取当前节点之前的所有同级节点。

self 选取当前节点。

child:选取当前节点的所有子元素

1
2
3
4
5
6
7
8
#child子节点定位
print(tree.xpath('//div[@id="testid"]/child::ul/li/text()'))

#child::*当前节点的所有子元素
print(tree.xpath('//div[@id="testid"]/child::*'))

#定位某节点下为ol的子节点下的所有节点
print(tree.xpath('//div[@id="testid"]/child::ol/child::*/text()'))

attribute:选取当前节点的所有属性

1
2
print(tree.xpath('//div/attribute::id'))
print(tree.xpath('//div/attribute::id'))

ancestor:父辈元素 / ancestor-or-self:父辈元素及当前元素
descendant:后代 / descendant-or-self:后代及当前节点本身

1
2
print(tree.xpath('//div/attribute::id'))
print(tree.xpath('//div/attribute::id'))

following :选取文档中当前节点的结束标签之后的所有节点

1
print(tree.xpath('//div[@id="testid"]/following::div[not(@id)]/.//li[1]/text()'))

namespace:选取当前节点的所有命名空间节点

1
print(tree.xpath('//div[@id="testid"]/namespace::*'))

parent:选取当前节点的父节点

1
print(tree.xpath('//li[@data="one"]/parent::ol/li[last()]/text()'))

preceding:选取文档中当前节点的开始标签之前的所有节点

1
2
3
4
5
# 记住是标签开始之前,同级前节点及其子节点
print(tree.xpath('//div[@id="testid"]/preceding::div/ul/li[1]/text()')[0])
# 下面这两条可以看到其顺序是靠近testid节点的优先
print(tree.xpath('//div[@id="testid"]/preceding::li[1]/text()')[0])
print(tree.xpath('//div[@id="testid"]/preceding::li[3]/text()')[0])

preceding-sibling:选取当前节点之前的所有同级节点

1
2
3
4
print(tree.xpath('//div[@id="testid"]/preceding-sibling::div/ul/li[2]/text()')[0])

#这里返回的就是空的
print(tree.xpath('//div[@id="testid"]/preceding-sibling::li'))

self:选取当前节点

1
2
# 选取带id属性值的div中包含data属性的标签的所有属性值
print(tree.xpath('//div[@id]/self::div[@data]/attribute::*'))