目录
1. XPath 概览
2. XPath 常用规则
3. 准备工作
4.实例引入
5.所有节点
6. 子节点
7.父节点
8.属性匹配
9.文本获取
10.属性获取
11.属性多值匹配
12. 多属性匹配
13.按序选择
14. 节点轴选择
结语
1. XPath 概览
2. XPath 常用规则
常用的集中规则有:
- nodename 选择此节点的所有子节点
- / 从当前节点选择直接子节点
- // 从当前节点选择子孙节点
- . 选取当前节点
- .. 选取当前节点的父节点
- @ 选取属性
举个例子 : //title [@lang = 'ergou'] 这个就是选择所有标签名为title 并且 lang的属性值为二狗的元素
//title [@lang = 'ergou'][1] 这个就是选择所有标签名为title 并且 lang的属性值为二狗的元素的第一个元素
//title [@lang = 'ergou'][1]/@href 这个就是选择所有标签名为title 并且 lang的属性值为二狗的元素的第一个元素的href 属性
3. 准备工作
4.实例引入
from lxml import etree
text = '''
<div>
<ul>
<li class="item-0"><a href="link1. html">first item</a></li>
<li class="item-1"><a href="link2.html"> second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>
'''
html = etree.HTML(text)
result= etree.tostring(html)
print(result.decode('utf-8'))
可以看到,经过处理之后, li 节点标签被补全,并且还向动添加了 body,html 节点
另外,也可以直接读取文本文件进行解析,示例如下:
from lxml import etree
html = etree.parse('./test.html',etree.HTMLParser())
'''
其中test.html为上述的
<div>
<ul>
<li class="item-0"><a href="link1. html">first item</a></li>
<li class="item-1"><a href="link2.html"> second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>
'''
result= etree.tostring(html)
print(result.decode('utf-8'))
返回:
至于有 
,应该是我手打,不知哪错了,排除这个,结果与上面略有不同,就是多了一个声明DOCTYPE ,不过对解析没有任何影响
5.所有节点
from lxml import etree
html = etree.parse('./test.html',etree.HTMLParser())
'''
其中test.html为上述的
<div>
<ul>
<li class="item-0"><a href="link1. html">first item</a></li>
<li class="item-1"><a href="link2.html"> second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>'''
result= html.xpath('//*')
print(result)
返回:
from lxml import etree
html = etree.parse('./test.html',etree.HTMLParser())
'''
其中test.html为上述的
<div>
<ul>
<li class="item-0"><a href="link1. html">first item</a></li>
<li class="item-1"><a href="link2.html"> second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>'''
result= html.xpath('//li')
print(result)
print(result[0])
返回:
6. 子节点
from lxml import etree
html = etree.parse('./test.html',etree.HTMLParser())
'''
其中test.html为上述的
<div>
<ul>
<li class="item-0"><a href="link1. html">first item</a></li>
<li class="item-1"><a href="link2.html"> second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>'''
result= html.xpath('//li/a')
print(result)
返回:
此处的 / 用于选取直接子节点 ,如果要获取所有子孙节点,就可以使用 // 例如,要获取 ul 节点下的所有子孙a节点,可以这样实现:
from lxml import etree
html = etree.parse('./test.html',etree.HTMLParser())
'''
其中test.html为上述的
<div>
<ul>
<li class="item-0"><a href="link1. html">first item</a></li>
<li class="item-1"><a href="link2.html"> second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>'''
result= html.xpath('//ul//a')
print(result)
运行结果:
from lxml import etree
html = etree.parse('./test.html',etree.HTMLParser())
'''
其中test.html为上述的
<div>
<ul>
<li class="item-0"><a href="link1. html">first item</a></li>
<li class="item-1"><a href="link2.html"> second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>'''
result= html.xpath('//ul/a')
print(result)
运行结果为:
7.父节点
from lxml import etree
html = etree.parse('./test.html',etree.HTMLParser())
'''
其中test.html为上述的
<div>
<ul>
<li class="item-0"><a href="link1. html">first item</a></li>
<li class="item-1"><a href="link2.html"> second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>
'''
result= html.xpath('//a[@href="link4.html"]/../@class')
print(result)
返回:
同时,我们也可以通过 parent:: 来获取父节点:
from lxml import etree
html = etree.parse('./test.html',etree.HTMLParser())
'''
其中test.html为上述的
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>
'''
result= html.xpath('//a[@href="link4.html"]/parent::*/@class')
print(result)
8.属性匹配
from lxml import etree
html = etree.parse('./test.html',etree.HTMLParser())
'''
其中test.html为上述的
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>
'''
result= html.xpath('//li[@class="item-0"]')
print(result)
可见,匹配结果正是两个,至于是不是那正确的两个,后面再验证 。
9.文本获取
用 XPath 中的 text() 方法获取节点中的文本,接下来尝试获取前面 li 节点中的文本,相关
from lxml import etree
html = etree.parse('./test.html',etree.HTMLParser())
'''
其中test.html为上述的
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>
'''
result= html.xpath('//li[@class="item-0"]/text()')
print(result)
运行结果:
奇怪的是,我们并没有获取到任何文本,却有WIN回车换行,这是为什么呢?因为 XPath中text()前面是/,而此处/的含义是选取直接子节点,很明显li的直接子节点都是a节点,文本都是在a节点内部的,所以这里匹配到的结果就是被修正的li节点内部的符号,因为自动修正的 li 节点的尾标签有WIN回车换行。
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
from lxml import etree
html = etree.parse('./test.html',etree.HTMLParser())
'''
其中test.html为上述的
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>
'''
result= html.xpath('//li[@class="item-0"]/a/text()')
print(result)
输出为:
from lxml import etree
html = etree.parse('./test.html',etree.HTMLParser())
'''
其中test.html为上述的
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>
'''
result= html.xpath('//li[@class="item-0"]//text()')
print(result)
输出:
10.属性获取
from lxml import etree
html = etree.parse('./test.html',etree.HTMLParser())
'''
其中test.html为上述的
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>
'''
result= html.xpath('//li/a/@href')
print(result)
返回:
11.属性多值匹配
from lxml import etree
text = '''
<li class="li li-first"><a href ="link.html">first item</a></li>
'''
html = etree .HTML(text)
result = html.xpath('//li[contains(@class,"li")]/a/text()')
print(result)
注:此种方式在某个节点的某个属性有多个值时经常用到,如某个节点的 class 属性通常有多个。
12. 多属性匹配
from lxml import etree
text = '''
<li class="li li-first" name="item"><a href ="link.html">first item</a></li>
'''
html = etree .HTML(text)
result = html.xpath('//li[contains(@class,"li") and @name="item"]/a/text()')
print(result)
择,一个条件是 class 属性里面包含 li 字符串,另一个条件是 name 属性为 item 字符串,二者需要同时满足,需要用 and 操作符相连,相连之后置于中括号内进行条件筛选 运行结果如下:
13.按序选择
from lxml import etree
text='''
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>
'''
html = etree.HTML(text)
#序号是以1开头的,不是以0开头
result= html.xpath('//li[1]/a/text()') #第一个 li 节点
print(result)
result= html.xpath('//li[last()]/a/text()') #最后一个 li 节点
print(result)
result= html.xpath('//li[position()<3]/a/text()') #选取位置小于3的 li 节点,也就是1和2的节点
print(result)
result= html.xpath('//li[last()-2]/a/text()') #选取倒数第三个的节点,last()是最后一个,所有last()-2就是倒数第三个
print(result)
运行结果为:
14. 节点轴选择
from lxml import etree
text='''
<div>
<ul>
<li class="item-0"><a href="link1.html"><span>first item</span></a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>
'''
html = etree.HTML(text)
#序号是以1开头的,不是以0开头
result= html.xpath('//li[1]/ancestor::*')
'''第一次选择时,调用了ancestor轴,可以获取所有祖先节点 其后需要跟两个冒号,然后是节点的选择器,这里我们直接使用*,表示匹配所有节点,因此返回结果是第一个 li 节点的所有祖先节点,包括 html、body、div、ul'''
print(result)
result= html.xpath('//li[1]/ancestor::div')
'''第二次选择时,又加了限定条件,这次在冒号后面加了 div ,这样得到的结果就只有 div这个祖先节点了'''
print(result)
result= html.xpath('//li[1]/attribute::*')
'''第三次选择时,调用了 attribute 轴,可以获取所有属性值,其后跟的选择器还是*,这代获取节点的所有属性,返回值就是 li 节点的所有属性值'''
print(result)
result= html.xpath('//li[1]/child::a[@href="link1.html"]')
'''第四次选择时,调用了 child 轴,可以获取所有直接子节点 这里又加了限定条件,选 href 属性为 linkl.html的a节点'''
print(result)
result= html.xpath('//li[1]/descendant::span')
'''第五次选择时,调用了 descendant 轴,可以获取所有子孙节点 这里又加了限定条件获取 span 节点,所以返回的结果只包含 span 节点而不包含a节点'''
print(result)
result= html.xpath('//li[1]/following::*[2]')
'''第六次选择时,调用 following 轴,可以获取当前节点之后的所有节点 这里虽然使用的是*匹配,但又加了索引选择,所以只获取了第二个后续节点'''
print(result)
result= html.xpath('//li[1]/following-sibling::*')
'''第七次选择时,调用 fo llowing -s ibling ,可以获取当前节点之后的所有同级节点,使用*匹配,所以获取了所有后续同级节点'''
print(result)
运行结果:
结语
到这里,我们基本把会用到的xpath选择器介绍完了。xpath功能非常强大,内置函数非常多,熟练使用后会大大提升HTML信息的提取效率。