python使用xpath(超详细)
使用时先安装 lxml 包
开始使用
和beautifulsoup类似,首先我们需要得到一个文档树
- 把文本转换成一个文档树对象
from lxml import etree
if __name__ == '__main__':
doc='''
<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> # 注意,此处缺少一个 </li> 闭合标签
</ul>
</div>
'''
html = etree.HTML(doc)
result = etree.tostring(html)
print(str(result,'utf-8'))
- 把文件转换成一个文档树对象
from lxml import etree
# 读取外部文件 index.html
html = etree.parse('./index.html')
result = etree.tostring(html, pretty_print=True) #pretty_print=True 会格式化输出
print(result)
均会打印出文档内容
节点、元素、属性、内容
xpath 的思想是通过 路径表达 去寻找节点。节点包括元素,属性,和内容
- 元素举例
html ---> <html> ...</html>
div ---> <div> ...</div>
a ---> <a> ...</a>
这里我们可以看到,这里的元素和html中的标签一个意思。单独的元素是无法表达一个路径的,所以单独的元素不能独立使用
路径表达式
/ 根节点,节点分隔符,
// 任意位置
. 当前节点
.. 父级节点
@ 属性
通配符
* 任意元素
@* 任意属性
node() 任意子节点(元素,属性,内容)
谓语
使用中括号来限定元素,称为谓语
//a[n] n为大于零的整数,代表子元素排在第n个位置的<a>元素
//a[last()] last() 代表子元素排在最后个位置的<a>元素
//a[last()-] 和上面同理,代表倒数第二个
//a[position()<3] 位置序号小于3,也就是前两个,这里我们可以看出xpath中的序列是从1开始
//a[@href] 拥有href的<a>元素
//a[@href='www.baidu.com'] href属性值为'www.baidu.com'的<a>元素
//book[@price>2] price值大于2的<book>元素
多个路径
用| 连接两个表达式,可以进行 或匹配
//book/title | //book/price
函数
xpath内置很多函数。更多函数查看https://www.w3school.com.cn/xpath/xpath_functions.asp
- contains(string1,string2)
- starts-with(string1,string2)
- ends-with(string1,string2) #不支持
- upper-case(string) #不支持
- text()
- last()
- position()
- node()
可以看到last()也是个函数,在前面我们在谓语中已经提到过了
案例
定位元素
匹配多个元素,返回列表
from lxml import etree
if __name__ == '__main__':
doc='''
<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> # 注意,此处缺少一个 </li> 闭合标签
</ul>
</div>
'''
html = etree.HTML(doc)
print(html.xpath("//li"))
print(html.xpath("//p"))
【结果为】
[<Element li at 0x2b41b749848>, <Element li at 0x2b41b749808>, <Element li at 0x2b41b749908>, <Element li at 0x2b41b749948>, <Element li at 0x2b41b749988>]
[] #没找到p元素
html = etree.HTML(doc)
print(etree.tostring(html.xpath("//li[@class='item-inactive']")[0]))
print(html.xpath("//li[@class='item-inactive']")[0].text)
print(html.xpath("//li[@class='item-inactive']/a")[0].text)
print(html.xpath("//li[@class='item-inactive']/a/text()"))
print(html.xpath("//li[@class='item-inactive']/.."))
print(html.xpath("//li[@class='item-inactive']/../li[@class='item-0']"))
【结果为】
b'<li class="item-inactive"><a href="link3.html">third item</a></li>\n '
None #因为第三个li下面没有直接text,None
third item #
['third item']
[<Element ul at 0x19cd8c4c848>]
[<Element li at 0x15ea3c5b848>, <Element li at 0x15ea3c5b6c8>]
使用函数
contains
有的时候,class作为选择条件的时候不合适@class='....' 这个是完全匹配,当王爷样式发生变化时,class或许会增加或减少像active的class。用contains就能很方便
from lxml import etree
if __name__ == '__main__':
doc='''
<div>
<ul>
<p class="item-0 active"><a href="link1.html">first item</a></p>
<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> # 注意,此处缺少一个 </li> 闭合标签
</ul>
</div>
'''
html = etree.HTML(doc)
print(html.xpath("//*[contains(@class,'item')]"))
【结果为】
[<Element p at 0x23f4a9d12c8>, <Element li at 0x23f4a9d13c8>, <Element li at 0x23f4a9d1408>, <Element li at 0x23f4a9d1448>, <Element li at 0x23f4a9d1488>]
starts-with
from lxml import etree
if __name__ == '__main__':
doc='''
<div>
<ul class='ul items'>
<p class="item-0 active"><a href="link1.html">first item</a></p>
<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> # 注意,此处缺少一个 </li> 闭合标签
</ul>
</div>
'''
html = etree.HTML(doc)
print(html.xpath("//*[contains(@class,'item')]"))
print(html.xpath("//*[starts-with(@class,'ul')]"))
【结果为】
[<Element ul at 0x23384e51148>, <Element p at 0x23384e51248>, <Element li at 0x23384e51288>, <Element li at 0x23384e512c8>, <Element li at 0x23384e51308>, <Element li at 0x23384e51388>]
[<Element ul at 0x23384e51148>]
ends-with
print(html.xpath("//*[ends-with(@class,'ul')]"))
【结果为】
Traceback (most recent call last):
File "F:/OneDrive/pprojects/shoes-show-spider/test/xp5_test.py", line 18, in <module>
print(html.xpath("//*[ends-with(@class,'ul')]"))
File "src\lxml\etree.pyx", line 1582, in lxml.etree._Element.xpath
File "src\lxml\xpath.pxi", line 305, in lxml.etree.XPathElementEvaluator.__call__
File "src\lxml\xpath.pxi", line 225, in lxml.etree._XPathEvaluatorBase._handle_result
lxml.etree.XPathEvalError: Unregistered function
看来python的lxml并不支持有的xpath函数列表
upper-case
和ends-with函数一样,也不支持。同样报错lxml.etree.XPathEvalError: Unregistered function
print(html.xpath("//a[contains(upper-case(@class),'ITEM-INACTIVE')]"))
text、last
#最后一个li被限定了
print(html.xpath("//li[last()]/a/text()"))
#会得到所有的`<a>`元素的内容,因为每个<a>标签都是各自父元素的最后一个元素。
#本来每个li就只有一个<a>子元素,所以都是最后一个
print(html.xpath("//li/a[last()]/text()"))
print(html.xpath("//li/a[contains(text(),'third')]"))
【结果为】
['fifth item']
['second item', 'third item', 'fourth item', 'fifth item']
[<Element a at 0x26ab7bd1308>]
position
print(html.xpath("//li[position()=2]/a/text()"))
#结果为['third item']
上面这个例子我们之前以及讲解过了
*这里有个疑问,就是position()函数能不能像text()那样用呢
print(html.xpath("//li[last()]/a/position()"))
#结果 lxml.etree.XPathEvalError: Unregistered function
这里我们得到一个结论,函数不是随意放在哪里都能得到自己想要的结果
node
返回所有子节点,不管这个子节点是什么类型(熟悉,元素,内容)
print(html.xpath("//ul/li[@class='item-inactive']/node()"))
print(html.xpath("//ul/node()"))
【结果为】
[<Element a at 0x239a0d197c8>]
['\n ', <Element li at 0x239a0d19788>, '\n ', <Element li at 0x239a0d19888>, '\n ', <Element li at 0x239a0d19908>, '\n ', <Element li at 0x239a0d19948>, '\n ', <Element li at 0x239a0d198c8>, ' 闭合标签\n ']
获取内容
**刚刚已经提到过,可以使用.text和text()的方式来获取元素的内容
from lxml import etree
if __name__ == '__main__':
doc='''
<div>
<ul class='ul items'>
<li class="item-0 active"><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> # 注意,此处缺少一个 </li> 闭合标签
</ul>
</div>
'''
html = etree.XML(doc)
print(html.xpath("//a/text()"))
print(html.xpath("//a")[0].text)
print(html.xpath("//ul")[0].text)
print(len(html.xpath("//ul")[0].text))
print(html.xpath("//ul/text()"))
【结果为】
['first item', 'second item', 'third item', 'fourth item', 'fifth item']
first item
18
['\n ', '\n ', '\n ', '\n ', '\n ', ' 闭合标签\n ']
看到这里,我们观察到text()和.text的区别。自己总结吧。不太好表达,就不表达了
获取属性
print(html.xpath("//a/@href"))
print(html.xpath("//li/@class"))
【结果为】
['link1.html', 'link2.html', 'link3.html', 'link4.html', 'link5.html']
['item-0 active', 'item-1', 'item-inactive', 'item-1', 'item-0']
自定义函数
我们从使用函数的过程中得到结论,就是有的函数不支持,有的支持,那问题来了,到底那些方法支持呢。我们在lxml官网找到了答案。https://lxml.de/xpathxslt.html。lxml 支持XPath 1.0 ,想使用其他扩展,使用libxml2,和libxslt的标准兼容的方式。XPath 1.0官方文档 以及其他版本的XPath文档 https://www.w3.org/TR/xpath/
lxml supports XPath 1.0, XSLT 1.0 and the EXSLT extensions through libxml2 and libxslt in a standards compliant way.
除此之外,lxml还提供了自定义函数的方式来扩展xpath的支持度 https://lxml.de/extensions.html
from lxml import etree
#定义函数
def ends_with(context,s1,s2):
return s1[0].endswith(s2)
if __name__ == '__main__':
doc='''
<div>
<ul class='ul items'>
<li class="item-0 active"><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> # 注意,此处缺少一个 </li> 闭合标签
</ul>
</div>
'''
html = etree.XML(doc)
ns = etree.FunctionNamespace(None)
ns['ends-with'] = ends_with #将ends_with方法注册到方法命名空间中
print(html.xpath("//li[ends-with(@class,'active')]"))
print(html.xpath("//li[ends-with(@class,'active')]/a/text()"))
【结果为】
[<Element li at 0x2816ed30548>, <Element li at 0x2816ed30508>]
['first item', 'third item']
- 形参
s1会传入xpath中的第一个参数@class,但这里注意@class是个列表 - 形参
s2会传入xpath中的第二个参数'active','active'是个字符串
官网例子https://lxml.de/extensions.html
def hello(context, a):
return "Hello %s" % a
from lxml import etree
ns = etree.FunctionNamespace(None)
ns['hello'] = hello
root = etree.XML('<a><b>Haegar</b></a>')
print(root.xpath("hello('Dr. Falken')"))
# 结果为 Hello Dr. Falken
python使用xpath(超详细)的更多相关文章
- Python入门教程 超详细1小时学会Python
Python入门教程 超详细1小时学会Python 作者: 字体:[增加 减小] 类型:转载 时间:2006-09-08我要评论 本文适合有经验的程序员尽快进入Python世界.特别地,如果你掌握Ja ...
- Python入门教程 超详细1小时学会Python
Python入门教程 超详细1小时学会Python 本文适合有经验的程序员尽快进入Python世界.特别地,如果你掌握Java和Javascript,不用1小时你就可以用Python快速流畅地写有用的 ...
- Python入门教程 超详细1小时学会Python(转)
假设我们有这么一项任务:简单测试局域网中的电脑是否连通.这些电脑的ip范围从192.168.0.101到192.168.0.200. 思路:用shell编程.(Linux通常是bash而Windows ...
- 【Python可视化】超详细Pyecharts 1.x教程,让你的图表动起来~
前言 pyecharts 是一个用于生成 Echarts 图表的Python库.Echarts是百度开源的一个数据可视化 JS 库,可以生成一些非常酷炫的图表. Pyecharts在1.x版本之后迎来 ...
- Python 基础学习笔记(超详细版)
1.变量 python中变量很简单,不需要指定数据类型,直接使用等号定义就好.python变量里面存的是内存地址,也就是这个值存在内存里面的哪个地方,如果再把这个变量赋值给另一个变量,新的变量通过之前 ...
- 【python】10分钟教你用python打造贪吃蛇超详细教程
10分钟教你用python打造贪吃蛇超详细教程 在家闲着没妹子约, 刚好最近又学了一下python,听说pygame挺好玩的.今天就在家研究一下, 弄了个贪吃蛇出来.希望大家喜欢. 先看程序效果: 0 ...
- NumPy 超详细教程(3):ndarray 的内部机理及高级迭代
系列文章地址 NumPy 最详细教程(1):NumPy 数组 NumPy 超详细教程(2):数据类型 NumPy 超详细教程(3):ndarray 的内部机理及高级迭代 ndarray 对象的内部机理 ...
- NumPy 超详细教程(2):数据类型
系列文章地址 NumPy 最详细教程(1):NumPy 数组 NumPy 超详细教程(2):数据类型 NumPy 超详细教程(3):ndarray 的内部机理及高级迭代 文章目录 NumPy 数据类型 ...
- NumPy 超详细教程(1):NumPy 数组
系列文章地址 NumPy 最详细教程(1):NumPy 数组 NumPy 超详细教程(2):数据类型 NumPy 超详细教程(3):ndarray 的内部机理及高级迭代 文章目录 Numpy 数组:n ...
- Linux 学习笔记之超详细基础linux命令 Part 13
Linux学习笔记之超详细基础linux命令 by:授客 QQ:1033553122 ---------------------------------接Part 12---------------- ...
随机推荐
- Apache Pulsar 社区周报:08-08 ~ 08-14
关于 Apache Pulsar Apache Pulsar 是 Apache 软件基金会顶级项目,是下一代云原生分布式消息流平台,集消息.存储.轻量化函数式计算为一体,采用计算与存储分离架构设计,支 ...
- 使用kind快速创建本地集群
简 介 kind是另一个Kubernetes SIG项目,但它与minikube有很大区别.它可以将集群迁移到Docker容器中,这与生成虚拟机相比,启动速度大大加快.简而言之,kind是一个使用Do ...
- unity坑-编译错误
问题: 项目里面有一个 StreamReader来读取一个文件,使用OpenText() 方法. 但是UNITY却提示 StreamReader类不包含OpenText()方法,并且也没有找到扩展方法 ...
- maven配置settings.xml【阿里云】
<?xml version="1.0" encoding="utf-8"?> <settings xmlns="http://mav ...
- C# 调用SendMessage刷新任务栏图标(强制结束时图标未消失)
本文参考C++改写 https://blog.csdn.net/dpsying/article/details/20139651 (该文章的坐标理解的有误解,会导致功能无效) SendMessage ...
- Linux通过命令行将英文改成中文
1.首先查看当前系统是否有中文语言包 locale -a 如果没有zh_CN.utf8,就需要下载中文语言包,否则,如果有中文语言包,跳过第二步. 2.安装中文语言包 Ubuntu: sudo apt ...
- windows-android-appium环境搭建
一.安装jdk 安装jdk1.7以上版本,会生成一个jdk目录,和单独的jre目录(注意:不是jdk里面的jre,时安装过程中设置的那个jre路径)安装完成后并配置环境变量 在系统环境变量中,新建:J ...
- sqli-labs 1-10关
学习sql注入的必做靶场我终于来尝试一下下了. 第一关 ?id=1'时出现 在测试?id=1' and '1'='1,页面正常 ?id=1' and '1'='2 页面错误 判断存在单引号字符型注入 ...
- Linux驱动之设备树的基础知识
前期知识 1. 如何编写一个简单的Linux驱动(一)--驱动的基本框架 2. 如何编写一个简单的Linux驱动(二)--设备操作集file_operations 3. 如何编写一个简单的 ...
- 蓝桥杯校内模拟赛Python解答
@ 目录 1. 15.125GB 2. 约数个数 3. 叶结点数 4. 数字9 5. 数位递增的数 6. 递增三元组 7. 音节判断 8. 长草 9. 序列计数 10. 晚会节目单 1. 15.125 ...