使用时先安装 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或许会增加或减少像activeclass。用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 ']

获取内容

**刚刚已经提到过,可以使用.texttext()的方式来获取元素的内容



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(超详细)的更多相关文章

  1. Python入门教程 超详细1小时学会Python

    Python入门教程 超详细1小时学会Python 作者: 字体:[增加 减小] 类型:转载 时间:2006-09-08我要评论 本文适合有经验的程序员尽快进入Python世界.特别地,如果你掌握Ja ...

  2. Python入门教程 超详细1小时学会Python

    Python入门教程 超详细1小时学会Python 本文适合有经验的程序员尽快进入Python世界.特别地,如果你掌握Java和Javascript,不用1小时你就可以用Python快速流畅地写有用的 ...

  3. Python入门教程 超详细1小时学会Python(转)

    假设我们有这么一项任务:简单测试局域网中的电脑是否连通.这些电脑的ip范围从192.168.0.101到192.168.0.200. 思路:用shell编程.(Linux通常是bash而Windows ...

  4. 【Python可视化】超详细Pyecharts 1.x教程,让你的图表动起来~

    前言 pyecharts 是一个用于生成 Echarts 图表的Python库.Echarts是百度开源的一个数据可视化 JS 库,可以生成一些非常酷炫的图表. Pyecharts在1.x版本之后迎来 ...

  5. Python 基础学习笔记(超详细版)

    1.变量 python中变量很简单,不需要指定数据类型,直接使用等号定义就好.python变量里面存的是内存地址,也就是这个值存在内存里面的哪个地方,如果再把这个变量赋值给另一个变量,新的变量通过之前 ...

  6. 【python】10分钟教你用python打造贪吃蛇超详细教程

    10分钟教你用python打造贪吃蛇超详细教程 在家闲着没妹子约, 刚好最近又学了一下python,听说pygame挺好玩的.今天就在家研究一下, 弄了个贪吃蛇出来.希望大家喜欢. 先看程序效果: 0 ...

  7. NumPy 超详细教程(3):ndarray 的内部机理及高级迭代

    系列文章地址 NumPy 最详细教程(1):NumPy 数组 NumPy 超详细教程(2):数据类型 NumPy 超详细教程(3):ndarray 的内部机理及高级迭代 ndarray 对象的内部机理 ...

  8. NumPy 超详细教程(2):数据类型

    系列文章地址 NumPy 最详细教程(1):NumPy 数组 NumPy 超详细教程(2):数据类型 NumPy 超详细教程(3):ndarray 的内部机理及高级迭代 文章目录 NumPy 数据类型 ...

  9. NumPy 超详细教程(1):NumPy 数组

    系列文章地址 NumPy 最详细教程(1):NumPy 数组 NumPy 超详细教程(2):数据类型 NumPy 超详细教程(3):ndarray 的内部机理及高级迭代 文章目录 Numpy 数组:n ...

  10. Linux 学习笔记之超详细基础linux命令 Part 13

    Linux学习笔记之超详细基础linux命令 by:授客 QQ:1033553122 ---------------------------------接Part 12---------------- ...

随机推荐

  1. 3种 Springboot 全局时间格式化方式,别再写重复代码了

    本文收录在个人博客:www.chengxy-nds.top,技术资料共享,同进步 时间格式化在项目中使用频率是非常高的,当我们的 API 接口返回结果,需要对其中某一个 date 字段属性进行特殊的格 ...

  2. 【Spring】IOC容器注解汇总,你想要的都在这儿了!!

    写在前面 之前,我们在[Spring]专题中更新了不少关于Spring注解相关的文章,有些小伙伴反馈说,看历史文章的话比较零散,经常会忘记自己看到哪一篇了.当打开一篇新文章时,总感觉自己似乎是看到过了 ...

  3. Linux平台Zabbix Agent的安装配置

    这里简单总结一下Linux平台Zabbix Agent的安装配置,实验测试的Zabbix版本比较老了(Zabbix 3.0.9),不过版本虽然有点老旧,但是新旧版本的安装步骤.流程基本差别不大.这里的 ...

  4. vue相关知识点及面试

    ### vue #### vue生命周期 beforeCreated `实例初始化,数据观察和event/watch事件配置之前被调用` created `实例创建后立即调用,数据观测,数据和方法运算 ...

  5. Leetcode 全排列专题(更新ing)

    总览 涉及到的题目有 题号 名字 难度 Leetcode 60 第k个排列 中等 Leetcode 46 全排列 中等 待更新...... Leetcode 46 全排列 题目 基础题 给定一个 没有 ...

  6. vue父子组件状态同步的最佳方式

    哈喽!大家好!我是木瓜太香,一位老牌儿前端工程师,平时我们在使用 vue 开发的时候,可能会遇到需要父组件与子组件某个状态需要同步的情况,通常这个是因为我们封装组件的时候有一个相同的状态外面要用,里面 ...

  7. ByteCTF2019

    VIP 第一阶段: 先检查一下程序开的保护: 程序只开了canary和nx保护.接下来用IDA分析反编译出的伪代码 如上图,载edit函数中我们可以控制size的大小,并且程序没有做任何检查,我们再跟 ...

  8. 2020重新出发,JAVA高级,JVM种设计模式

    Java的23种设计模式全面解析 设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路.它不是语法规定,而是一套用来提高代码可复用性.可维护性.可读性.稳健 ...

  9. python 3 字典

    字典,是可变的无序集合,同时是一种以键值对为基本元素的可以存储各种数据类型的集合,用大括号({})表示字典的开始和结束,元素之间用(,)分隔. 键值对,由键(Key)和值(Value)组成,中间用冒号 ...

  10. 浅说iOS二维码的那些事儿

    二维码需要用到 Quartz 2D 一般是三步走~1导入CoreImage框架,编写字符串转二维码图;2渲染二维码;3显示二维码. 导入头文件 #import <CoreImage/CoreIm ...