12. XPath解析入门

目录

前言

模块安装

XPath涉及的基本概念

XPath基本语法

测试完整代码

XPath进阶用法

需求1:让xpath处理这个资源文件

需求2:找到标签位置,如html标签

需求3:找到无序列表(ul)中每一项(li)包裹的文本内容

需求4:找到无序列表(ul)中第一项(li)包裹的文本内容

需求5:找到无序列表(ul)中每一项(li)包裹的链接(a)内容

需求6:获取href为‘dapao’包裹的文字(属性筛选)

需求7:先获取列表项,再递归每个列表项取出包裹文本和a标签的href属性值

需求8:练习:打印第一个div的文本信息

XPath进阶用法完整代码


前言

XPath是一门在 XML 文档中查找信息的语言。XPath可用来在 XML 文档中对元素和属性进行遍历。而我们熟知的HTML恰巧属于XML的⼀个子集,所以完全可以用XPath去查找html中的内容。


模块安装

老样子,直接pip install lxml


XPath涉及的基本概念

下面是一段xml文档:

<book>
    <id>1</id>
    <name>野花遍地香</name>
    <price>1.23</price>
    <author>
        <nick>周大强</nick>
        <nick>周芷若</nick>
    </author>
</book>

首先,先了解几个概念:

1. book, id, name, price....都被称为节点.

2. Id, name, price, author被称为book的子节点

3. book被称为id, name, price, author的父节点

4. id, name, price,author被称为同胞节点

OK~ 有了这些基础知识后, 我们就可以开始了解xpath的基本语法了


XPath基本语法

还是以一段xml为例:

<book>
    <id>1</id>
    <name>野花遍地香</name>
    <price>1.23</price>
    <nick>臭豆腐</nick>
    <author>
        <nick id="10086">周大强</nick>
        <nick id="10010">周芷若</nick>
        <nick class="jay">周杰伦</nick>
        <nick class="jolin">蔡依林</nick>
        <div>
            <nick>热热热热热1</nick>
        </div>
        <span>
            <nick>热热热热热2</nick>
            <div>
                <nick>热热热热热3</nick>
            </div>
        </span>
    </author>

    <partner>
        <nick id="ppc">胖胖陈</nick>
        <nick id="ppbc">胖胖不陈</nick>
    </partner>
</book>

要想在Python中使用XPath解析方式,我们要先学会怎么导包。

from lxml import etree

从我们刚刚下载好的lxml库中导入etree模块,这样就能进行XPath解析了。

我们把上面的xml范例写入文件,用etree.XML()来处理这段信息:

xml = """
<book>
    <id>1</id>
    <name>野花遍地香</name>
    <price>1.23</price>
    <nick>臭豆腐</nick>
    <author>
        <nick id="10086">周大强</nick>
        <nick id="10010">周芷若</nick>
        <nick class="jay">周杰伦</nick>
        <nick class="jolin">蔡依林</nick>
        <div>
            <nick>热热热热热1</nick>
        </div>
        <span>
            <nick>热热热热热2</nick>
            <div>
                <nick>热热热热热3</nick>
            </div>
        </span>
    </author>

    <partner>
        <nick id="ppc">胖胖陈</nick>
        <nick id="ppbc">胖胖不陈</nick>
    </partner>
</book>
"""

tree = etree.XML(xml)

不仅如此,etree下除了处理xml的api还可以用etree.HTML()来处理html类型文件,还可以用etree.parse()来处理资源文件,我们后续会用到。

我们XPath是基于类似文件路径来解析的。我们可以看到标签都是互相嵌套的,所以这种算法学习了文件路径。

比如我们想请求name标签,它的XPath路径就是:/book/name。写为代码:

result = tree.xpath("/book/name")

‘/’表示层级关系。熟悉Linux的或者用Mac的应该知道第一个‘/’是根节点,Windows文件系统不会这么写,是以盘符开头的(C:/等)。其实Windows系统这么写是省略了根节点,它也存在一个根节点,它的子节点就是许多的盘符,但是出于便于阅读等各种原因就把根节点省略了。

如果想请求author标签包裹的所有nick标签,我们仍可以使用XPath:/book/author/nick就可以请求到这个路径上的所有nick标签了。这和文件系统也许有所不同,文件系统在同一目录下是不允许出现重名的,而网页中可以出现很多同样的标签。

result = tree.xpath("/book/author/nick")

这时可以打印我们的result发现它只是一个类似迭代器的结果,返回结果是找到xxx标签在xxx地址。但是我们实际想要的是它所包裹的文本内容或者属性值等有效信息,这怎么获取的?直接/text的话拿到的是它下面text标签所在位置。所以这时要text()来出马了,它的作用就是获取查找到标签所包裹的文本内容。

result = tree.xpath("/book/name/text()")  # text() 拿文本
# 返回结果:['野花遍地香']

类似地,我们可以获取/book/author/nick下的所有人名:

result = tree.xpath("/book/author/nick/text()")
# 返回结果:['周大强', '周芷若', '周杰伦', '蔡依林']

但是问题来了,如果我想拿到author标签里面所有nick标签包裹的所有文字怎么办?包括热热热热热1,热热热热热2,热热热热热3?先看代码再解释:

result = tree.xpath("/book/author//nick/text()")  # // 后代
# 返回结果:['周大强', '周芷若', '周杰伦', '蔡依林', '热热热热热1', '热热热热热2', '热热热热热3']

可以看到我们在author后加了双斜杠'//',它的含义是获取所有后代的nick标签,也就是这俩斜杠中间可以有0层,也可以有1,2,3,...,n层,只要是在author下的nick标签就全部被抓出来了。那么再获取text()就可以把/book/author/div/nick和/book/author/span/nick和/book/author/span/div/nick三个路径下的文本都拿到了。

实际操作中我们可能也不需要这么多层都拿到。可能只需要一层的结果,又该怎么处理?

result = tree.xpath("/book/author/*/nick/text()")  # * 是任意的节点. 通配符
# 返回结果:['热热热热热1', '热热热热热2']

可以看到,它获取了/book/author/div/nick和/book/author/span/nick的内容,author和nick中间的 * 星号表示任意符号(即通配符),可以代替各种标签名,但是只能替代一层,所以没有把下一层的/book/author/span/div/nick所包裹的热热热热热3拿出来。

再练习一下,把这段xml里面所有nick包裹的文本全给我拿出来!

result = tree.xpath("/book//nick/text()")
返回结果:['臭豆腐', '周大强', '周芷若', '周杰伦', '蔡依林', '热热热热热1', '热热热热热2', '热热热热热3', '胖胖陈', '胖胖不陈']

简单得很,只需要在book下面加双斜杠就好。


测试完整代码

测试的时候要把不必要的代码加注释,这样可以方便的查看运行结果,也可以通过debug看结果。

给代码行加注释在PyCharm中有个快捷键:ctrl+/,可以给选中的代码行加注释,很方便。

# Created at UESTC
# Author: Vector Kun
# Time: 2023/1/2 14:16

# xpath 是在XML文档中搜索内容的一门语言
# html是xml的一个子集
"""
<book>
    <id>1</id>
    <name>野花遍地香</name>
    <price>1.23</price>
    <author>
        <nick>周大强</nick>
        <nick>周芷若</nick>
    </author>
</book>
"""
# 安装lxml模块
# pip install lxml -i xxxxxx
# xpath解析
from lxml import etree

xml = """
<book>
    <id>1</id>
    <name>野花遍地香</name>
    <price>1.23</price>
    <nick>臭豆腐</nick>
    <author>
        <nick id="10086">周大强</nick>
        <nick id="10010">周芷若</nick>
        <nick class="jay">周杰伦</nick>
        <nick class="jolin">蔡依林</nick>
        <div>
            <nick>热热热热热1</nick>
        </div>
        <span>
            <nick>热热热热热2</nick>
            <div>
                <nick>热热热热热3</nick>
            </div>
        </span>
    </author>

    <partner>
        <nick id="ppc">胖胖陈</nick>
        <nick id="ppbc">胖胖不陈</nick>
    </partner>
</book>
"""

tree = etree.XML(xml)
# result = tree.xpath("/book")  # /表示层级关系. 第一个/是根节点
# result = tree.xpath("/book/name")
# result = tree.xpath("/book/name/text()")  # text() 拿文本
# result = tree.xpath("/book/author//nick/text()")  # // 后代
# result = tree.xpath("/book/author/*/nick/text()")  # * 是任意的节点. 通配符
result = tree.xpath("/book//nick/text()")
print(result)

XPath进阶用法

我们用一个简单的html文件来讲XPath还可以用来干什么:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>Title</title>
    </head>
    <body>
        <ul>
            <li><a href="http://www.baidu.com">百度</a></li>
            <li><a href="http://www.google.com">谷歌</a></li>
            <li><a href="http://www.sogou.com">搜狗</a></li>
        </ul>
        <ol>
            <li><a href="feiji">飞机</a></li>
            <li><a href="dapao">大炮</a></li>
            <li><a href="huoche">火车</a></li>
        </ol>
        <div class="job">李嘉诚</div>
        <div class="common">胡辣汤</div>
    </body>
</html>

放在py文件同级文件夹,命名为7_xpath_eg.html,当然可以自己修改文件名和代码部分。

需求1:让xpath处理这个资源文件

from lxml import etree

tree = etree.parse("7_xpath_eg.html")

这个时候我们就用到了parse这个函数,来处理我们本地的资源文件。

需求2:找到标签位置,如html标签

result = tree.xpath('/html')
# 运行结果:[<Element html at 0x15d667f6580>]

可以看到找到了标签位置,并指出所在地址。

需求3:找到无序列表(ul)中每一项(li)包裹的文本内容

result = tree.xpath("/html/body/ul/li/a/text()")
# 运行结果:['百度', '谷歌', '搜狗']

需求4:找到无序列表(ul)中第一项(li)包裹的文本内容

result = tree.xpath("/html/body/ul/li[1]/a/text()")  # xpath的顺序是从1开始数的, []表示索引
# 运行结果:['百度']

这里有点“大逆不道”了,没有从0开始数,而是1就是第一项,这一点要注意一下。

需求5:找到无序列表(ul)中每一项(li)包裹的链接(a)内容

result = tree.xpath("/html/body/ul/li/a/@href")
# 运行结果:['http://www.baidu.com', 'http://www.google.com', 'http://www.sogou.com']

我们可以用“@属性”的方法来获取属性对应的属性值

需求6:获取href为‘dapao’包裹的文字(属性筛选)

result = tree.xpath("/html/body/ol/li/a[@href='dapao']/text()")  # [@xxx=xxx] 属性的筛选
# 返回结果['大炮']

筛选属性可以在所属标签后加[@xxx属性=xxx属性值]筛选出含对应属性值的标签位置,这一点很像bs4的思路。

需求7:先获取列表项,再递归每个列表项取出包裹文本和a标签的href属性值

ol_li_list = tree.xpath("/html/body/ol/li")

for li in ol_li_list:
    # 从每一个li中提取到文字信息
    result = li.xpath("./a/text()")  # 在li中继续去寻找. 相对查找
    print(result)
    result2 = li.xpath("./a/@href")  # 拿到属性值: @属性
    print(result2)

./相信大家比较熟悉,它的意义就是以当前路径进行相对查找,'.'就代表/html/body/ol/li。

需求8:练习:打印第一个div的文本信息

print(tree.xpath('/html/body/div[1]/text()'))
# 返回结果:['李嘉诚']

XPath进阶用法完整代码

from lxml import etree

tree = etree.parse("7_xpath_eg.html")
# result = tree.xpath('/html')
# result = tree.xpath("/html/body/ul/li/a/text()")
# result = tree.xpath("/html/body/ul/li/a/@href")
# result = tree.xpath("/html/body/ul/li[1]/a/text()")  # xpath的顺序是从1开始数的, []表示索引

# result = tree.xpath("/html/body/ol/li/a[@href='dapao']/text()")  # [@xxx=xxx] 属性的筛选

# print(result)

# ol_li_list = tree.xpath("/html/body/ol/li")
#
# for li in ol_li_list:
#     # 从每一个li中提取到文字信息
#     result = li.xpath("./a/text()")  # 在li中继续去寻找. 相对查找
#     print(result)
#     result2 = li.xpath("./a/@href")  # 拿到属性值: @属性
#     print(result2)
#
# print(tree.xpath("/html/body/ul/li/a/@href"))

# print(tree.xpath('/html/body/div[1]/text()'))
# print(tree.xpath('/html/body/ol/li/a/text()'))

还是请大家自行注释对应代码段进行测试。


总结

本节我们进行了XPath的入门,学会了怎么处理xml文件和html文件,学习了多种情境和需求下XPath的用法,下一节我们将进行实战。

当前我们也已经学习了三种解析方式,大家可以自由搭配多种解析形式,快速高效获取自己想要的信息。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vec_Kun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值