欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

022.(RDC暑假学习期 7.13-7.14)认识XPath

程序员文章站 2023-11-01 14:24:28
认识XPath介绍二级目录三级目录6月6号早晨突然收到考核结果通过的通知,惊喜成为RDC大数据组的一员。两个月的思考与突破,是疫情在家学习时光的难忘记忆之一。从进度条的%5到接近所有任务要求的%90,一路下来,也靠行动,靠意志。考试月让工作室的日子暂告一段落。考试结束后的今天,将迎来RDC暑假学习期(小小996)。之前师兄师姐也提到过,大数据从业方向主要有算法和开发两者。暑假的学习主要在于前者,希望通过这1个月多的学习,自己既能更加确定自己的兴趣和方向,也能尽快提升团队合作能力,多多出力锻炼。今天,简...

参考书籍:《Python 3网络爬虫开发实战》


6月6号早晨突然收到考核结果通过的通知,惊喜成为RDC大数据组的一员。两个月的思考与突破,是疫情在家学习时光的难忘记忆之一。从进度条的%5到接近所有任务要求的%90,一路下来,也靠行动,靠意志。

考试月让工作室的日子暂告一段落。考试结束后的今天,将迎来RDC暑假学习期(小小996)。之前师兄师姐也提到过,大数据从业方向主要有算法和开发两者。暑假的学习主要在于前者,希望通过这1个月多的学习,自己既能更加确定自己的兴趣和方向,也能尽快提升团队合作能力,多多出力锻炼。

今天,简单回顾之前的爬虫基础后,便开始XPath1的学习。

介绍

之前自己实现的爬虫提取页面信息时,基本使用正则表达式,这还是比较
烦琐,而且万一有地方写错了,可能导致匹配失败,所以使用正则表达式提取页面信息、多多少少还有些不方便。

对于网页的节点(tag)来说,它可以定义 id,class 或其他属性,而且节点之间还有层次关系,在网页中可以通过 XPath,CSS选择器来定位一个或多个节点 那么,在页面解析时,利用 XPath,CSS选择器来提取某个节点,然后再调用相应方法获取它的正文内容或者属性,就可以提取我们想要的任意信息。

XPath 全称 XML Path Language ,即XML 路径语言,它是一门在 XML 文档中查找信息的语言。它最初是用来搜寻 XML 文档的,但是它同样适用于 HTML 文档2的搜索。所以在做爬虫时,我们完全可以使用 XPath 来做相应的信息抽取。

而常见的解析库包括 lxml(一种使用 Python 编写的库,可以迅速、灵活地处理 XML 和 HTML,还可以用于web爬取),Beautiful Soup,pyquery 等。

常用规则

022.(RDC暑假学习期 7.13-7.14)认识XPath

实例引入

from lxml import etree
# 导入 lxml 库的 etree 模块
# 然后声明一段 HTML 文本,调用 HTML类进行初始化,成功构造一个 XPath解析对象。

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

# HTML 文本中最后一个 li 节点没有闭合,但是 etree 模块可以自动修正 HTML 文本。
# 调用 tostring() 方法即可输出修正后的 HTML 代码,但结果是 bytes 类型
# 可以用 decode() 方法将其转化为 str 类型

# 结果如下:
<html><body><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>
</body></html>

# 经过处理后,li 节点标签被补全,并且还自动添加了 body、html 节点
# 还可以直接读取文本文件进行解析:
from lxml import etree

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

节点

用以 // 开头的 XPath 规则来选取所有符合要求的节点:

from lxml import etree

html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//*')
print(result)
# * 代表匹配所有节点,返回的结果是一个列表,每个元素都是一个 Element 类型,后跟节点名称。

# 运行结果:
"""
[<Element html at 0x1d6610ebe08>, <Element body at 0x1d6610ebf08>, 
<Element div at 0x1d6610ebf48>, <Element ul at 0x1d6610ebf88>, 
<Element li at 0x1d6610ebfc8>, <Element a at 0x1d661115088>, 
<Element li at 0x1d6611150c8>, <Element a at 0x1d661115108>, 
<Element li at 0x1d661115148>, <Element a at 0x1d661115048>, 
<Element li at 0x1d661115188>, <Element a at 0x1d6611151c8>, 
<Element li at 0x1d661115208>, <Element a at 0x1d661115248>]
"""

#  也可以指定匹配的节点名称:
result1 = html.xpath('//li')

# 通过 / 或 // 即可查找元素的子节点或子孙节点。
# 选择 li 节点的所有直接 a 子节点:

result2 = html.xpath('//li/a')

#  知道子节点,查询父节点可以用 .. 来实现:

result3 = html.xpath('//a[@href="link4.html"]/../@class')

# 获得 href 属性为 link4.html 的 a 节点的父节点的 class 属性
# parent::*也是父节点的表示(斜杠要加上)

文本获取

我们用 XPath 中的 text ()方法获取节点中的文本。

# 第一种
from lxml import etree

html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li[@class="item-0"]/a/text()')
print(result)

# 第二种(子孙节点内部的所有文本)
from lxml import etree

html = etree.parse('./test.html', etree.HTMLParser())
result = html.xpath('//li[@class="item-0"]//text()')
print(result)

# 第二种方法会获取到补全代码时换行产生的特殊字符
# 推荐使用第一种方法,可以保证获取的结果是整洁的。

属性获取

区别于属性匹配,但其实还是用@符号就可以实现。

# 获取节点的 href 属性
result = html.xpath('//li/a/@href') 
print(result)

# 运行结果:['link1.html', 'link2.html', 'link3.html', 'link4.html', 'link5.html']

属性多值匹配

当某个节点的某个属性有多个值时,前面的单一匹配法就为空了。

通过 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)

# 例中的li节点的class属性有2个值
# 运行结果:['first item']

多属性匹配

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()')

# 要用 and 操作符相连,相连之后置于 中括号 内进行条件筛选
# 这里的 and 其实是 XPath 中的运算符

022.(RDC暑假学习期 7.13-7.14)认识XPath

按序选择

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)
# 获取第一个(注意,这里和代码中不同,序号是以1开头的,不是以0开头)
result = html.xpath('//li[1]/a/text()')
print(result)
# 获取最后一个
result = html.xpath('//li[last()]/a/text()')
print(result)
# 获取前两个
result = html.xpath('//li[position()<3]/a/text()')
print(result)
# 获取倒数第三个
result = html.xpath('//li[last()-2]/a/text()')
print(result)

"""
运行结果:
['first item']
['fifth item']
['first item', 'second item']
['third item']
"""

这里使用了 last ()、 position()等函数。 XPath中,提供了 100 多个函数,包括存取值、字符串、逻辑、节点、序列等处理功能, 详细可见使用文档。

节点轴选择

XPath 提供了很多节点轴选择方法,包括获取子元素 、兄弟元素、父元素、祖先元素等。

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::*')
print(result)
# 获取 div 祖先节点
result = html.xpath('//li[1]/ancestor::div')
print(result)
# 获取当前节点所有属性值
result = html.xpath('//li[1]/attribute::*')
print(result)
# 获取 href 属性值为 link1.html 的直接子节点
result = html.xpath('//li[1]/child::a[@href="link1.html"]')
print(result)
# 获取所有的的子孙节点中包含 span 节点但不包含 a 节点
result = html.xpath('//li[1]/descendant::span')
print(result)
# 获取当前所有节点之后的第二个节点
result = html.xpath('//li[1]/following::*[2]')
print(result)
# 获取当前节点之后的所有同级节点
result = html.xpath('//li[1]/following-sibling::*')
print(result)

"""
[<Element html at 0x231a8965388>, <Element body at 0x231a8965308>, <Element div at 0x231a89652c8>, <Element ul at 0x231a89653c8>]
[<Element div at 0x231a89652c8>]
['item-0']
[<Element a at 0x231a89653c8>]
[<Element span at 0x231a89652c8>]
[<Element a at 0x231a89653c8>]
[<Element li at 0x231a8965308>, <Element li at 0x231a8965408>, <Element li at 0x231a8965448>, <Element li at 0x231a8965488>]
"""

关于XPath和正则表达式

XPath主要是用来处理 XML 格式文档的,它是基于 XML 文档的层次结构来确定一个到达指定节点的路径的,因此特别适合处理这种层级结构明显的数据。

正则表达式可以处理任何格式的字符串文档,它是基于文本的特征来匹配、查找指定数据的。

个人认为,要分清目标数据是层次结构明显还是特征明显3,这个分清楚了,该选什么也就基本确定了,后续要思考的其实算是优化的步骤。此外,XPath,可读性和可维护型都是比较好的。当然一般的正则表达式效
率是比较高的,这个前提是不太复杂的正则表达式。


  1. XPath 官方文档 ↩︎

  2. html 与 xml 的区别与联系 ↩︎

  3. XPath与正则表达式在文本数据提取时该如何选择? ↩︎

本文地址:https://blog.csdn.net/u013598957/article/details/107319594