XPath使用

XPath

一,简介

  全称XMl Path Language,是一种在XML中寻找信息的语言,同样适用于HTML文档搜索。XPath功能十分强大,提供了非常简洁明了的路径选择表达式。拥有超过100个内建函数,用于字符串,数字,时间以及节点的匹配,序列的处理等。几乎所有想要定位的节点都可以用XPath来选择。

 

二,使用

1.常用规则

  nodename :选取此节点的所有子节点

  /      :从当前节点选取直接子节点

  //     :从当前节点选取子孙节点

  .      :选取当前节点

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

  @      :选取属性

  

  示例://title[@lang='eng'],代表锁选择的名称为title,同时属性;lang的值为eng的节点。

2,安装lxml

  windows:pip3 install lxml

3,使用示例
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-active"><a href="link3.html">third item</a></li>
        <li class="item-4"><a href="link4.html">fourth item</a></li>
        <li class="item-5"><a href="link5.html">fifth item</a>
    </ul>
</div>
"""
html = etree.HTML(text)
res = etree.tostring(html)
print(res.decode('utf-8'))

  首先到如etree模块,声明一段HTML文本,调用HTML类进行初始化,这样就成功构造了一个Xpath解析对象,text中最后一个li节点时没有闭合的,但是etree可以自动修正HTML文本。调用tostring输出修正后的文本,在decode转成str。

<html><body><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-active"><a href="link3.html">third item</a></li>
        <li class="item-4"><a href="link4.html">fourth item</a></li>
        <li class="item-5"><a href="link5.html">fifth item</a>
    </li></ul>
</div>
</body></html>

  经过处理,li节点被不全,并自动添加了,body,html节点。另外,可以直接读取文本文件进行解析:

html = etree.parse('./text.html', etree.HTMLParser())
print(etree.tostring(html).decode('utf-8'))
 4.选取所有节点

  我们一般会使用//开头的XPath规则来选取所有符合要求的节点。

# 选取所有节点
html = etree.parse('./text.html', etree.HTMLParser())
res = html.xpath('//*')
print(res)

  运行结果:

[<Element html at 0x117994842c8>, <Element head at 0x117994843c8>, <Element meta at 0x11799484408>, 
<Element title at 0x11799484448>, <Element body at 0x11799484488>, <Element div at 0x11799484508>,
<Element ul at 0x11799484548>, <Element li at 0x11799484588>, <Element a at 0x117994845c8>,
<Element li at 0x117994844c8>, <Element a at 0x11799484608>, <Element li at 0x11799484648>,
<Element a at 0x11799484688>, <Element li at 0x117994846c8>, <Element a at 0x11799484708>,
<Element li at 0x11799484748>, <Element a at 0x11799484788>]

  这里*代表所有节点,返回的是一个列表,每个元素都是element类型,其后跟了节点的名称,如html,body,title,ul,li等等,所有的节点都包含在其中。当日也可以获取指定节点:

res = html.xpath('//li')
print(res)
print(res[0])

  运行结果:

[<Element li at 0x22855a593c8>, <Element li at 0x22855a59408>,<Element li at 0x22855a59448>, 
<Element li at 0x22855a59488>, <Element li at 0x22855a594c8>]
<Element li at 0x22855a593c8>

  想要取出其中的某一个对象,直接使用索引。

6.选取子节点

  通过/或//可以查找元素的子节点或者子孙节点。查找li节点的所有直接a节点

# 选取子节点
res = html.xpath('//li/a')
print(res)

  运行结果:

 [<Element a at 0x27107af43c8>, <Element a at 0x27107af4408>, 
<Element a at 0x27107af4448>, <Element a at 0x27107af4488>, <Element a at 0x27107af44c8>]

  这里通过追加/a选择所有li节点下面的直接子节点a。//li用于选取所有的li节点,/a用于选中li节点中所有直接子节点a。想要获取所有子孙节点可以使用//,获取ul节点下的所有孙子a节点:  

res = html.xpath('//ul//a')
print(res)

  运行结果:

[<Element a at 0x1a88f8c43c8>, <Element a at 0x1a88f8c4408>, <Element a at 0x1a88f8c4448>, 
<Element a at 0x1a88f8c4488>, <Element a at 0x1a88f8c44c8>]

  与上面的例子运行结果是相同的。但是这里如果使用//ul/a,就无法获取任何结果了,ul节点下面没有直接子节点a。

7.属性选择

  在选取的时候,我们还可以使用@符号进行属性过滤。选取class值为item-0的li节点:

# 属性选择
res = html.xpath('//li[@class="item-0"]')
print(res)

  运行结果:

[<Element li at 0x178e69653c8>]
8.父节点

  选取href属性为link4.html的a节点的父节点,再获取其class的属性,使用..选择父节点:

res = html.xpath('//a[@href="link4.html"]/../@class')
print(res)

  运行结果:

['item-4']
9.文本获取

  我们可以使用XPath中的text()方法进行文本获取,尝试获取li节点中的文本:

# 文本获取,选取li节点所有子孙节点的文本
res = html.xpath('//li[@class="item-5"]//text()')
print(res)

  运行结果:

['fifth item']
# 文本获取,先选取直接子节点a,再获取其文本res = html.xpath('//li[@class="item-5"]/a/text()')
print(res)

  运行结果:

['fifth item']
 10.属性多值匹配

  某些时候,某个节点的属性可能存在多个值,例如:

from lxml import etree

text = "<li class='li li-first'><a href='link.html'>first li</a></li>"
html = etree.HTML(text)
res = html.xpath('//li[@class="li"]/a/text()')
print(res)

运行结果:
[]

  这里的li节点的属性存在li,li-first两个值,使用之前的获取方法是无法获取的。此时就需要使用contains()函数了,:

res = html.xpath('//li[contains(@class,"li")]/a/text()')
print(res)

运行结果:
['first li']

  通过contains()方法,第一个参数传入属性名称,第二个参数传入属性值,只要此属性包含所传入的属性值,就能完成匹配了。可以在某个节点的某个属性存在多个值的时候使用。

11.多属性匹配

  根据多个属性来确定一个节点,需要同时匹配多个属性:

text = "<li class='li li-first' name='item'><a href='link.html'>first li</a></li>"
html = etree.HTML(text)
res = html.xpath('//li[contains(@class,"li") and @name="item"]/a/text()')
print(res)

运行结果:
['first li']

  li节点里面又增加了一个name属性,且li节点的class属性存在多个值。此时需要同时使用class和name属性进行选择,一个条件时class里面包含li字符串,另一个条件是name的属性值为item,二者需要同时满足,使用and操作符相连,之后再置于中括号内进行条件筛选。

   xpath运算符介绍

  or:或。          例:age=19 or age=20,如果age是19或20,则返回True,否则返回False

  and:与。          例:age>19 and age<21,同时满足and前后条件则返回True,否则False

  mod:计算除法的余数。   例:5 mod 2 , 1

  |  :计算两个节点集。     例://bock | //cd ,返回所有用于bock和cd元素的节点集

  +:    加。        例:6 + 2,  8

  -:  减。        例:6 - 2,   4

  *:  乘。        例:6 * 2,  12

  div:   除。        例:6 div 2,  3

  =:  等于

  !=: 不等于

  <:  小于

  <=:  小于等于

  >:    大于

  >=:   大于等于

 

12.按序选择

  有时候,我们在选择节点时同时匹配了多个节点,但是只想要其中的某个节点,或者第二个节点,这时候该怎么办呢?

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-active"><a href="link3.html">third item</a></li>
        <li class="item-4"><a href="link4.html">fourth item</a></li>
        <li class="item-5"><a href="link5.html">fifth item</a>
    </ul>
</div>
"""
# 按序选择
html = etree.HTML(text)
res = html.xpath('//li[1]/a/text()')
print(res)
res2 = html.xpath('//li[last()]/a/text()')
print(res2)
res3 = html.xpath('//li[position()<3]/a/text()')
print(res3)
res4 = html.xpath('//li[last()-2]/a/text()')
print(res4)

  运行结果:

['first item']
['fifth item']
['first item', 'second item']
['third item']

  第一次选择,选取第一个li节点,在括号中传入数字1。这里与索引不同,序号是以1开始的。

  第二次选择,选取最后一个li节点,使用last()即可。

  第三次选择,选取位置小于3的li节点,也就是位置序号为1和2的li节点。

  第四次选择,选取倒数第三个li节点,在括号中传入last()-2即可,因为last()为最后一个,那么last()-2则为倒数第三个。

13.节点轴选择

  XPath提供了很多节点轴的选择方法,包括子元素,兄弟元素,父元素,祖先元素等,:

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-active"><a href="link3.html">third item</a></li>
        <li class="item-4"><a href="link4.html">fourth item</a></li>
        <li class="item-5"><a href="link5.html">fifth item</a>
    </ul>
</div>
"""
# 节点轴选择
html = etree.HTML(text)
res = html.xpath('//li[1]/ancestor::*')
print(res)
res2 = html.xpath('//li[1]/ancestor::div')
print(res2)
res3 = html.xpath('//li[1]/attribute::*')
print(res3)
res4 = html.xpath('//li[1]/child::a[@href="link1.html"]')
print(res4)
res5 = html.xpath('//li[1]/descendant::span')
print(res5)
res6 = html.xpath('//li[1]/following::*[2]')
print(res6)
res7 = html.xpath('//li[1]/following-sibling::*')
print(res7)

  运行结果:

[<Element html at 0x165f62363c8>, <Element body at 0x165f6236348>, <Element div at 0x165f6236308>, <Element ul at 0x165f6236408>]
[<Element div at 0x165f6236308>]
['item-0']
[<Element a at 0x165f62364c8>]
[<Element span at 0x165f6236548>]
[<Element a at 0x165f6236588>]
[<Element li at 0x165f6236648>, <Element li at 0x165f6236688>, <Element li at 0x165f62366c8>, <Element li at 0x165f6236708>]

  第一次选择,调用ancestor轴可以获取所有祖先节点。其后跟两个冒号,然后是节点的选择器,这里直接使用*表示匹配所有节点,返回的是第一个li节点的所有祖先节点,包括body,html,div和ul。

  第二次选择,我们在后面加上了限制条件,只选择div。

  第三次选择,调用attribute轴,可以获取所有节点的属性。加上*,返回的是li节点的所有属性值。

  第四次选择,调用child轴,可以获取所有子孙节点。加上先限制条件,选取href值为link1.html的a节点。

  第五次选择,调用descendant轴,可以获取所有子孙节点。这里我们又加上了限制span节点,返回结果只包含span节点而不包含a节点。

  第六次选择,调用following轴,获取当前节点之后的所有节点。这里虽然使用了*匹配所有,但又加上了索引,返回的只有第二个后续节点。

  第七次选择,调用following-sibling轴,获取当前节点之后所有的同级节点。使用*获取后续所有的同级节点。

 

三,总结

  到此为止,我们基本上把XPath可能用到的选择器介绍完了。是不是觉得,哇!!!XPath好强大!内置函数非常多,熟练之后可以大大提升HTML信息提取效率。

posted @ 2019-05-18 21:50  ZivLi  阅读(417)  评论(0编辑  收藏  举报