Xpath的使用

Xpath的使用

  • Xpath全称是XML Path Language,即XML路径语言,用来在XML文档中查找信息。

  • 虽然最初是用来搜寻XML文档的,但是在HTML文档中也同样适用。

  • 在做爬虫的时候,完全可以使用XPath实现相应的信息提取

  • Xpath提供了100多个内建函数,用于字符串、数值、时间的匹配以及节点、序列的处理等

1. XPath常用规则

表达式 描述
nodename 选取此节点的所有子节点
/ 从当前节点选取直接子节点
// 从当前节点选取子孙节点
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性

//title[@lang='eng']代表选择所有名称为title,lang属性值为eng的节点

2. 安装XPath

使用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-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'))

首先导入lxml模块中的etree模块,接着声明一段HTML代码,然后调用HTML类来进行初始化,这样就成功构造了一个XPath解析对象。

HTML代码最后的一个<li>标签没有闭合,etree模块可以自动修正HTML文本,之后调用tostring方法即可输出修正后的HTML代码,但是结果是bytes类型,需要使用decode方法将其转换成str格式。

另外也可以直接读取文本文件进行解析:

from lxml import etree

html = etree.parse('./test.html', etree.HTMLParser())
result = etree.tostring(html)
print(result.decode('utf-8'))

4. 所有节点

from lxml import etree
html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//*')
print(result)
-------------------
[<Element html at 0x1055cca40>, <Element body at 0x119084700>, <Element div at 0x119084740>, <Element ul at 0x119085dc0>, <Element li at 0x119085ec0>, <Element a at 0x119084840>, <Element li at 0x119085f80>, <Element a at 0x119085fc0>, <Element li at 0x119086000>, <Element a at 0x119085e80>, <Element li at 0x119086040>, <Element a at 0x119086080>, <Element li at 0x1190860c0>, <Element a at 0x119086100>]
  • 一般都会使用//开头的XPath规则,来选取所有符合要求的节点

  • *代表匹配所有节点,也就是获取整个HTML文档中的所有节点

  • 从运行结果来看,返回的是一个列表,列表中的每个元素都是Element类型类型后面跟着节点名称

  • 还可以指定名称:

    from lxml import etree
    html = etree.parse('./test.html', etree.HTMLParser())
    result = html.xpath('//li')
    print(result)
    
    --------------------
    [<Element li at 0x117a9f9c0>, <Element li at 0x117ac0dc0>, <Element li at 0x117ac0e40>, <Element li at 0x117ac0e80>, <Element li at 0x117ac1000>]
    

    //li用来获取整个HTML文档中所有li节点

5.子节点

通过///可以查找元素的子节点或者子孙节点

选择所有li节点下所有直接子节点a:

from lxml import etree

html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li/a')
for re in result:
    print(re)
--------------------
<Element a at 0x12737b280>
<Element a at 0x127389600>
<Element a at 0x1273896c0>
<Element a at 0x127389700>
<Element a at 0x127389880>
<Element a at 0x127389940>
<Element a at 0x127389800>
<Element a at 0x127389a40>
<Element a at 0x127389a80>
<Element a at 0x127389980>
<Element a at 0x127389ac0>
<Element a at 0x127389b00>
<Element a at 0x127389b40>
<Element a at 0x127389b80>
<Element a at 0x127389bc0>
<Element a at 0x127389c00>
<Element a at 0x127389c40>
<Element a at 0x127389c80>
<Element a at 0x127389cc0>
<Element a at 0x127389d00>
<Element a at 0x127389d40>
<Element a at 0x127389d80>
<Element a at 0x127389dc0>
<Element a at 0x127389e00>
<Element a at 0x127389e40>
<Element a at 0x127389e80>
<Element a at 0x127389ec0>
<Element a at 0x127389f00>
<Element a at 0x127389f40>
<Element a at 0x127389f80>
<Element a at 0x127389fc0>
<Element a at 0x12738a000>
<Element a at 0x12738a040>

选择所有ul节点下所有子孙节点a:

from lxml import etree

html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//ul//a')
for re in result:
    print(re)

如果写成//ul/a,这样就没有结果输出,因为ul节点的直接子节点是li,不是a

6.父节点

如果知道子节点,要通过..来寻找父节点

寻找属性href值为"https://www.douban.com/mine/"的a节点的父节点:

from lxml import etree

html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//a[@href="https://www.douban.com/mine/"]/..')
print(result)
-----------
[<Element td at 0x11fd2cf80>]

另外也可以通过parent::获取父节点:

from lxml import etree

html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//a[@href="https://www.douban.com/mine/"]/parent::*')
print(result)
----------------
[<Element td at 0x11f97e000>]

7.属性匹配

在选取节点的时候,可以使用@符号来实现属性过滤

如://a[@href="https://www.douban.com/mine/"],就是选取href属性值为"https://www.douban.com/mine/"的a标签

8.文本获取

XPath中的text方法可以获取节点中的文本

显示class属性值为"nav-user-account"的li节点下的span节点的文本

from lxml import etree

html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li[@class="nav-user-account"]/a/span/text()')
print(result)
-------
['彧晖的帐号']

如果想要直接获取li节点下的所有子孙节点文本:

from lxml import etree

html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li[@class="nav-user-account"]//text()')
print(result)
------------
['\n      ', '\n        ', '彧晖的帐号', '\n      ', '\n      ', '\n        ', '\n          ', '\n            ', '\n              ', '\n                ', '个人主页', '\n              ', '\n            ', '\n            ', '\n              ', '\n                ', '我的订单', '\n              ', '\n            ', '\n            ', '\n              ', '\n                ', '我的钱包', '\n              ', '\n            ', '\n            ', '\n              ', '\n                ', '帐号管理', '\n              ', '\n            ', '\n            ', '\n              ', '\n                ', '退出', '\n              ', '\n            ', '\n          ', '\n        ', '\n      ', '\n    ']

9. 属性获取

可以使用@符号获取属性

获取li节点的直接子节点a的href属性值:

from lxml import etree

html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li[@class="nav-user-account"]/a/@href')
print(result)

获取li节点下所有子孙节点的href属性值:

from lxml import etree

html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li[@class="nav-user-account"]//@href')
print(result)

10. 属性多值匹配

有时候节点的某个属性拥有多个属性值,这时就需要使用contains方法来进行匹配

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)

11.多属性匹配

  • 有时候节点里面有多个属性,要通过匹配多个属性的属性值,才能确定唯一的节点,这时就需要将要匹配的属性通过and连接起来
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)

上述的代码中就是寻找li的class属性值为li,并且name属性值为item的li节点,然后将该li节点下a节点的文本打印出来

  • XPath中除了and运算符外,还有其他的运算符
运算符 作用
or
and
mod 计算除法的余数
| 计算两个节点集,如//li|//a就是返回拥有li元素和a元素的节点集
+ 加法
- 减法
* 乘法
div 除法
= 等于
!= 不等于
< 小于
<= 小于或等于
> 大于
>= 大于或等于

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-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 = html.xpath('//li[1]/a/text()')  # 选择第一个li节点,开始是从1开始
print(result)
result = html.xpath('//li[last()]/a/text()')  # last()选择最后一个节点
print(result)
result = html.xpath('//li[position()<4]/a/text()')  # 选择节点小于4的节点,也就是1、2、3节点
print(result)
result = html.xpath('//li[last()-1]/a/text()')  # 选择倒数第二个节点,使用最后一个节点-1
print(result)

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

13.节点轴选择

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)
result = html.xpath('//li[1]/ancestor::*')  # 选择第一个li节点的所有父节点,ancestor后面需要跟两个':',然后是选择器
print(result)
result = html.xpath('//li[1]/ancestor::div')  # 选择第一个li节点的div父节点
print(result)
result = html.xpath('//li[1]/child::*')  # 选择第一个li节点的所有直接子节点
print(result)
result = html.xpath('//li[1]/attribute::*')  # 选择第一个li节点的所有属性值
print(result)
result = html.xpath('//li[1]/following::*')  # 选择第一个li节点之后的所有节点
print(result)
result = html.xpath('//li[1]/following-sibling::')  # 选择第一个li节点之后的所有同级节点
print(result)
result = html.xpath('//li[1]/following-sibling::li[@class="item-1"]')  # 选择第一个li节点之后的所有class属性为"item-1"的同级节
# 点
print(result)
posted @ 2021-12-16 09:16  写代码的小灰  阅读(498)  评论(0)    收藏  举报