第64天:XPath 和 lxml

简介: 第64天:XPath 和 lxml

XPath 和 lxml

XPath 全称为 Xml Path Language,即 Xml 路径语言,是一种在 Xml 文档中查找信息的语言。它提供了非常简洁的路径选择表达式,几乎所有的节点定位都可以用它来选择。

XPath 可以用于 Xml 和 Html,在爬虫中经常使用 XPath 获取 Html 文档内容。

lxml 是 Python 语言用 Xpath 解析 XML、Html文档功能最丰富的、最容易的功能模块。

XPath 术语

节点


在 XPath 中有七种节点分别是元素、属性、文本、文档、命名空间、处理指令、注释,前3种节点为常用节点


请看下面的 Html 例子,(注:这个例子全文都需要使用)



<!DOCTYPE html><html>    <body>        <div>            <!-- 这里是注释 -->            <h4>手机品牌商<span style="margin-left:10px">4</span></h4>            <ul>               <li>小米</li>               <li>华为</li>                <li class='blank'> OPPO </li>               <li>苹果</li>            </ul>        </div>        <div>            <h4>电脑品牌商<span style="margin-left:10px">3</span></h4>            <ul class="ul" style="color:red">                <li>戴尔</li>                 <li>机械革命</li>                 <li>ThinkPad</li>            </ul>        </div>    </body></html>

在上面的例子中



<html> 为文档节点<li>小米</li> 为元素节点class='blank' 为属性节点<!-- 这里是注释 --> 为注释节点


节点关系


在 XPath中有多中节点关系分别是父节点、子节点、同胞节点、先辈节点、后代节点

在上面的例子中


  1. 父节点:每个元素以及属性都有一个父节点,如:div 节点的父节点是 body 节点
  2. 子节点:元素节点可有零个、一个或多个子节点,如:第一个 ul 节点的子节点是4个 li 节点
  3. 同胞节点:拥有相同的父节点的节点,如:两个 div 节点是同胞节点
  4. 先辈节点:某节点的父节点、父节点的父节点...,如:ul 节点的先辈节点有 div 节点、body 节点
  5. 后代节点:某个节点的子节点,子节点的子节点...,如:body 节点的后代节点有div 节点、ul 节点、li 节点

XPath 语法

基本语法



表达式 描述
nodeName 选择nodeName节点的所有子节点
/ 从根节点开始
// 从匹配的节点开始选择节点
. 选择当前节点
.. 选择当前节点的父节点
@ 选择元素
* 匹配任意元素节点
@* 匹配任意属性节点


用上面的 Html 文档举个例子


路径表达式 描述
body 选取 body 的所有子节点
/html 选取 html 节点
//div 选取所有 div 节点
//div/./h4 div 节点下的 h4 节点
../div 选取当前节点的父节点下的所有 div 节点
//@class 所有带有 class 元素的节点
//* 选择所有节点
//@* 选择所有属性节点


常用函数


表达式 描述
position() 返回节点的 index 位置
last() 返回节点的个数
contains(string1,string2) string1 是否包含 string2
text() 返回文本节点
comment() 返回注释节点
normalize-space(string) 去除首位空格,中间多个空格用一个空格代替
substring(string,start,len) 返回从 start 位置开始的指定长度的子字符串,第一个字符下标为1
substring-before(string1,string2) 返回string1中位于第一个string2之前的部分
substring-after(string1,string2) 返回string1中位于第一个string2之后的部分

同样用上面的Html文档举个例子

路径表达式 描述
//div[position()>1] 选择第二个 div 节点
//div[last()] 选择最后一个 div 节点
contains(//h4[2],'手机') 第二个 h4 标签是否包含手机字符串
//li/text() li 节点中的文本内容
//div/comment() div 节点下的 html 注释
normalize-space(//li[@class='blank']) li 节点下 class属性为 blank 的文本去掉空格
substring(//h4[1],1,2) 第一个 h4 节点的前2个字
substring-before(//h4[1],'品牌商') 第一个 h4 节点的品牌商字符串之前的字符串
substring-after(//h4[1],'品牌商') 第一个 h4 节点的品牌商字符串之后的字符串


谓语


XPath 中的谓语就是删选表达式,相当于 SQL 中的 Where 条件,谓语被嵌在 [ ] 中

路径表达式 描述
//div[1] 选择第一个 div 节点
//div[2]/ul/li[last()] 选择第二个 div 节点下的最后一个 li 节点
//div[2]/ul/li[position()>3] 选择第二个 div 节点下的前两个 li 节点
//ul[@class] 选择所有带 class 属性的 ul 节点
//ul[@class='computer'] 选择 class 属性为 computer 的 ul 节点
//h4[span = 4] 选择 h4 节点下 span 值等于4的节点


Xpath 结语


以上内容介绍了 XPath 的基本语法,下面将介绍 XPath 如何在 Python 中使用。

lxml 模块

安装


sudo pip3 install lxml==4.4.1


解析 HTML 文档


lxml.etree 一个强大的 Xml 处理模块,etree 中的 ElementTree 类是一个主要的类,用于对XPath的解析、增加、删除和修改节点。


from lxml import etree

etree.parse() 函数可以解析一个网页文件还可以解析字符串, 在网页中下载的数据一般都是字符串形式的,使用 parse(StringIO(str)) 将整个页面内容解析加载构建一个 ElementTree 对象,ElementTree 可以使用 XPath 语法精准找到需要的数据。

1.加载页面到内存


from lxml import etreefrom io import StringIO
test_html = '''<html>    <body>        <div>            <!-- 这里是注释 -->            <h4>手机品牌商<span style="margin-left:10px">4</span></h4>            <ul>               <li>小米</li>               <li>华为</li>               <li class='blank'> OPPO </li>               <li>苹果</li>            </ul>        </div>        <div>            <h4>电脑品牌商<span style="margin-left:10px">3</span></h4>            <ul class="ul" style="color:red">                <li>戴尔</li>                <li>机械革命</li>                <li>ThinkPad</li>            </ul>        </div>    </body></html>'''
html = etree.parse(StringIO(test_html))print(html)

结果:


<lxml.etree._ElementTree object at 0x10bd6b948>


2.获取所有 li 标签数据


li_list = html.xpath('//li')
print("类型:")print(type(li_list))
print("值:")print(li_list)
print("个数:")print(len(li_list))
for l in li_list:    print("li文本为:" + l.text)

结果:


类型:<class 'list'>值:[<Element li at 0x10543c9c8>, <Element li at 0x10543ca08>, <Element li at 0x10543ca48>, <Element li at 0x10543ca88>, <Element li at 0x10543cac8>, <Element li at 0x10543cb48>, <Element li at 0x10543cb88>]个数:7li文本为:小米li文本为:华为li文本为:OPPO li文本为:苹果li文本为:戴尔li文本为:机械革命li文本为:ThinkPad

3.获取带 class='blank' 属性数据


blank_li_list = html.xpath('//li[@class="blank"]')
print("类型:")print(type(blank_li_list))
print("值:")print(blank_li_list)
print("个数:")print(len(blank_li_list))
for l in blank_li_list:    print("li文本为:" + l.text)

结果:



类型:<class 'list'>值:[<Element li at 0x105253a48>]个数:1li文本为:OPPO

4.属性操作



ul = html.xpath('//ul')[1]#遍历属性for name, value in ul.attrib.items():    print('{0}="{1}"'.format(name, value))
#添加新的属性ul.set("new_attr", "true")# 获取单个属性new_attr = ul.get('new_attr')print(new_attr)

结果:


class="ul"style="color:red"true

5.获取最后一个div标签数据


last_div = html.xpath('//div[last()]')print("TAG:")print(last_div.tag)print("值:")print(last_div.text)

结果


div值:

6.添加子节点


child = etree.Element("child")child.text = "这里是新的子元素"last_div.append(child)# 在最后一个 div 标签查找新的子元素clild_text = last_div.find("child").textprint(clild_text)

7.删除子元素


# 查找并设置第一个查询到的元素first_ul = html.find("//ul")ul_li = first_ul.xpath("li")for li in ul_li:    # 删除元素    first_ul.remove(li)
ul_li = first_ul.xpath("li")if len(ul_li) == 0:    print("元素被删除了")

8.遍历元素后代


body = html.find("body")for sub in body.iter():    print(sub.tag)    print(sub.text)

结果


body
        div
            <cyfunction Comment at 0x10c374b10> 这里是注释 h4手机品牌商span4ul...

工具

  1. 在 google 浏览器开发者模式下,Elements 界面选择元素后右键 Copy,可以 Copy 元素的 XPath 路径
  2. XPath Helper 是一个 google 浏览器插件,可以验证 XPath 是否正确

总结

  1. 学习了 XPAth 的知识,可以快速匹配单个或多个元素节点和属性,在工作中大大加快了工作的效率。
  2. lxml 是一个 Python 中强大的 Xml 和 Html 处理模块,结合 XPath 的使用在程序中快速、便捷的分析、修改网页内容。

示例代码:https://github.com/JustDoPython/python-100-day/tree/master/day-064


系列文章

第63天:正则表达式第62天:HTTP 入门第61天:Python Requests 库高级用法从 0 学习 Python 0 - 60 大合集总结

目录
相关文章
|
存储 Kubernetes 负载均衡
K8S原理和实践
K8S原理和实践
470 0
|
关系型数据库 分布式数据库 数据库
学习分布式不得不会的ACP理论
2000年7月,加州大学伯克利分校的Eric Brewer教授在ACM PODC会议上提出CAP猜想。2年后,麻省理工学院的Seth Gilbert和Nancy Lynch从理论上证明了CAP。
3192 0
|
12月前
|
数据采集 数据安全/隐私保护 开发者
一些`StringIndexOutOfBoundsException`异常的实际案例
本内容展示了几个实际中遇到的`StringIndexOutOfBoundsException`异常案例,帮助开发者理解该异常发生的原因及解决方法。
272 5
|
设计模式 算法 数据可视化
分析大型软件系统的经典方法
【6月更文挑战第19天】本文介绍ATAM(架构权衡分析方法)是一种评估大型软件系统架构的工具,着重考虑性能、可用性、安全性和可修改性等质量属性的交互。ATAM提供了一种结构化方法,确保在设计早期就能理解和平衡各种质量属性,以优化系统整体性能。
798 2
分析大型软件系统的经典方法
|
SQL 关系型数据库 MySQL
【Mysql】 深入理解MySQL的执行计划
【Mysql】 深入理解MySQL的执行计划
466 4
GitHub 上的超级 Python 游戏项目,不容错过!
今天分享一个超级牛的 GitHub 项目,是一个专门的基于 Pygame 开发小游戏的项目。该项目就开源在 G 站上,目前已经获得了 2.6K 的 Star 和 1.5K 的 Fork,可以说是超级牛掰了!
GitHub 上的超级 Python 游戏项目,不容错过!
|
Java 数据库连接 mybatis
【已解决】nested exception is org.apache.ibatis.binding.BindingException: Parameter ‘qcBizname‘ not found
【已解决】nested exception is org.apache.ibatis.binding.BindingException: Parameter ‘qcBizname‘ not found
1016 0
|
存储 Java 索引
深入学习Java集合之ArrayList的实现原理
深入学习Java集合之ArrayList的实现原理
164 0
|
前端开发 流计算
css:text-decoration给文字增加上划线、删除线、下划线
css:text-decoration给文字增加上划线、删除线、下划线
2969 0
css:text-decoration给文字增加上划线、删除线、下划线