Py学习  »  Python

我要偷偷的学Python,然后惊呆所有人(第十五天)

看,未来 • 3 年前 • 314 次点击  

在这里插入图片描述

标题无意冒犯,就是觉得这个广告挺好玩的
上面这张思维导图喜欢就拿走,反正我也学不了这么多

前言

前期回顾: 我要偷偷学Python(第十四天)

第十四天告诉大家我们将进入一个漫长的练习阶段,不过不用慌,今天我们来看别人口中“永远滴神” – Xpath

我也不知道为什么被称作永远滴神,但是我知道肯定不是因为它难。


插播一条推送:(如果是小白的话,可以看一下下面这一段)

欢迎来到我们的圈子

我建了一个Python学习答疑群,有兴趣的朋友可以了解一下: 这是个什么群

群里已经有一千八百多个小伙伴了哦!!!
(严格控制群人数,所以今年年底之前不会满群)

直通群的传送门: 传送门


本系列文默认各位有一定的C或C++基础,因为我是学了点C++的皮毛之后入手的Python,这里也要感谢齐锋学长送来的支持。
本系列文默认各位会百度。

我要的不多,点个关注就好啦
然后呢,本系列的目录嘛,说实话我个人比较倾向于那两本 Primer Plus,所以就跟着它们的目录结构吧。

本系列也会着重培养各位的自主动手能力,毕竟我不可能把所有知识点都给你讲到,所以自己解决需求的能力就尤为重要,所以我在文中埋得坑请不要把它们看成坑,那是我留给你们的锻炼机会,请各显神通,自行解决。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Xpath

我们刚开始接触Xpath是什么时候?对,selenium的时候,那时候我们抓不下标签,就转方向从Xpath入手。

那么这个Xpath到底是个什么东西竟然如此的神通广大?

XPath即为XML路径语言(XML Path Language),它是一种用来确定XML文档中某部分位置的语言。

也不多哔哔,就知道Xpath是网页内容的导航就好。

Xpath基本语法截取

以下内容部分出自百度百科,截取适用于爬虫的部分。
毕竟人家都整理好了,咱也难出其右。

路径表达式

选取节点 XPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。 [1]
下面列出了最有用的路径表达式:
在这里插入图片描述

路径表达式栗子

在这里插入图片描述

选取节点后面的下标

在这里插入图片描述

选取未知节点,通配符

在这里插入图片描述

实例

在这里插入图片描述

选取若干路径

在这里插入图片描述

学习案例:抓取百度贴吧->王者荣耀吧的评论

找的时候是一篇伪代码,我做了微调,加了点注释。
也可以自己去调试一遍,自己动手的话印象会比较深刻。

# element tree: 文档树对象
import requests
from lxml import etree


# 爬取百度贴吧数据
class Tieba(object):

    def __init__(self,key):	
        self.url = "https://tieba.baidu.com/"+key	#动态url,理论上可以获取任一贴吧的网页


    

        #print(self.url)
        self.headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
        }

    # 获取数据
    def get_data(self, url):
        response = requests.get(url, headers=self.headers)
        return response.content	#以二进制形式返回(大可以用text去试一下,返回文本形式)

    # 数据提取
    def parse_data(self, data):
        # 解码,并删除源代码中的注释
        data = data.decode().replace("<!--", "").replace("-->", "")
        # 创建element对象
        html = etree.HTML(data)
        # 根据xpath语法提取每页标题对象的贴吧标题xpath对象
		# 关于这个xpath是如何确定的,代码后面会放上一张图,自己悟一下
        el_list = html.xpath("//li[@class=' j_thread_list clearfix']/div/div[2]/div[1]/div[1]/a")

        print(el_list)

        # 遍历每页的贴吧标题xpath对象
        data_list = []
        for el in el_list:
            # 根据每个xpath对象获取每个标题的titile属性和link属性
            temp = {}
            temp["title"] = el.xpath("./text()")[0]	#取第一个文本
            temp["link"] = "http://tieba.baidu.com" + el.xpath("./@href")[0] #同
            # 将每个标题属性字典添加到列表中
            data_list.append(temp)

        # 获取下一页url
        try:
            next_url = "https:" + html.xpath('//a[contains(text(),"下一页>")]/@href')[0]	#contains()函数,后面讲
        except:
            next_url = None
        # 返回每页的标题属性字典和下一页的url
        return data_list, next_url

    # 保存 伪代码
    def save_data(self, data_list):
        for data in data_list:
            print(data)

    # 运行
    def run(self):
        # 第一次的url是self.url
        next_url = self.url
        # 实现翻页 直到next_url == None 结束翻页
        while True:
            # 发送请求,获取响应
            data = self.get_data(next_url)
            # 从响应中提取数据(数据和翻页用的url)
            data_list, next_url = self.parse_data(data)
            self.save_data(data_list)
            print(next_url)
            # 判断是否结束
            if next_url == None:
                break


if __name__ == '__main__':
    tieba = Tieba("f?kw=王者荣耀")
    tieba.run()	#函数入口
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

在这里插入图片描述

Xpath常用函数及用例

以下皆为伪代码,测试文件木有,留着当工具书使用

标签补全

from lxml import etree

text = '''
<div>
    <ul>
        <li class="item-0"><a href="www.baidu.com">baidu</a>
        <li class="item-1"><a href="https://blog.csdn.net/qq_25343557">myblog</a>
        <li class="item-2"><a href="https://www.csdn.net/">csdn</a>
        <li class="item-3"><a href="https://hao.360.cn/?a1004">hao123</a>
'''

html = etree.HTML(text)
result = etree.tostring(html)
print(result.decode('UTF-8'))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

获取所有节点

html = etree.parse('./test.html',etree.HTMLParser()


    
)
result = html.xpath('//*')#'//'表示获取当前节点子孙节点,'*'表示所有节点,'//*'表示获取当前节点下所有节点
for item in result:
    print(item)
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4
result = html.xpath('//li')#将*改为li,表示只获取名称为li的子孙节点
#返回一个列表
for item in result:
    print(item)
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4
result = html.xpath('//li/a')#//li选择所有的li节点,/a选择li节点下的直接子节点a
for item in result:
    print(item)
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

我们也可以使用//ul//a首先选择所有的ul节点,再获取ul节点下的的所有a节点,最后结果也是一样的。但是使用//ul/a就不行了,首先选择所有的ul节点,再获取ul节点下的直接子节点a,然而ul节点下没有直接子节点a,当然获取不到。需要深刻理解//和/的不同之处。/用于获取直接子节点,//用于获取子孙节点。

根据属性获取

根据XPath常用规则可以通过@匹配指定的属性。我们通过class属性找最后一个li节点。

result = html.xpath('//li[@class="item-3"]')#最后一个li的class属性值为item-3,返回列表形式
print(result)
  • 1
  • 2
  • 1
  • 2

获取父节点

根据XPath常用规则可以通过…获取当前节点的父节点。现在我要获取最后一个a节点的父节点下的class属性。

result = html.xpath('//a[@href="https://hao.360.cn/?a1004"]/../@class')
#a[@href="https://hao.360.cn/?a1004"]:选择href属性为https://hao.360.cn/?a1004的a节点
#..:选取父节点
#@class:选取class属性,获取属性值
print(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

获取文本信息

很多时候我们找到指定的节点都是要获取节点内的文本信息。我们使用text()方法获取节点中的文本。现在获取所有a标签的文本信息。

result = html.xpath('//ul//a/text()')
print(result)
  • 1
  • 2
  • 1
  • 2

属性多值匹配

在上面的例子中所有的属性值都只有一个,如果属性值有多个还能匹配的上吗?(上面代码里面翻页看不懂的注意了啊)

遇到属性值有多个的情况我们需要使用contains()函数了,contains()匹配一个属性值中包含的字符串 。包含的字符串,而不是某个值。

from lxml import etree

text = '''
<div>
    <ul>
        <li class="item-0"><a href="www.baidu.com">baidu</a>
        <li class="spitem-1"><a href="https://blog.csdn.net/qq_25343557">myblog</a>
        <li class="item-2"><a href="https://www.csdn.net/">csdn</a>
        <li class="item-3"><a href="https://hao.360.cn/?a1004">hao123</a>
'''

html = etree.HTML(text)
result = html.xpath('//li[contains(@class,"item-0") and @name="one"]/a/text()')#使用and操作符将两个条件相连。
print(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

也许你会说这个直接使用name的属性值就可以得到了,然而,这里只是作为演示。

根据顺序选择

在上面的操作中我多次找第2个li节点或找最后一个li节点,使用属性值进行匹配。其实何必呢!我们可以根据顺序进行选择。

result = html.xpath('//li[2]/a/text()')#选择第二个li节点,获取a节点的文本

result = html.xpath('//li[last()]/a/text()')#选择最后一个li节点,获取a节点的文本

result = html.xpath('//li[last()-1]/a/text()')#选择倒数第2个li节点,获取a节点的文本

result = html.xpath('//li[position()<=3]/a/text()')#选择前三个li节点,获取a节点的文本
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

函数太多了,感觉我这里的函数满足不了你的话可以移步:https://www.w3school.com.cn/xpath/index.asp

XPath 轴

我们可以通过XPath获取祖先节点,属性值,兄弟节点等等,这就是XPath的节点轴。轴可定义相对于当前节点的节点集。

result = html.xpath('//li[1]/ancestor::*')
#ancestor表示选取当前节点祖先节点,*表示所有节点。合:选择当前节点的所有祖先节点。

result = html.xpath('//li[1]/ancestor::div')
#ancestor表示选取当前节点祖先节点,div表示div节点。合:选择当前节点的div祖先节点。

result =  html.xpath('//li[1]/ancestor-or-self::*')
#ancestor-or-self表示选取当前节点及祖先节点,*表示所有节点。合:选择当前节点的所有祖先节点及本及本身。

result =  html.xpath('//li[1]/attribute::*')
#attribute表示选取当前节点的所有属性,*表示所有节点。合:选择当前节点的所有属性。

result =  html.xpath('//li[1]/attribute::name')
#attribute表示选取当前节点的所有属性,name表示name属性。合:选择当前节点的name属性值。

result =  html.xpath('//ul/child::*')
#child表示选取当前节点的所有直接子元素,*表示所有节点。合:选择ul节点的所有直接子节点。

result =  html.xpath('//ul/child::li[@name="two"]')
#child表示选取当前节点的所有直接子元素,li[@name="two"]表示name属性值为two的li节点。合:选择ul节点的所有name属性值为two的li节点。

result =  html.xpath('//ul/descendant::*')
#descendant表示选取当前节点的所有后代元素(子、孙等),*表示所有节点。合:选择ul节点的所有子节点。

result =  html.xpath('//ul/descendant::a/text()')
#descendant表示选取当前节点的所有后代元素(子、孙等),a/test()表示a节点的文本内容。合:选择ul节点的所有a节点的文本内容。

result =  html.xpath('//li[1]/following::*')
#following表示选取文档中当前节点的结束标签之后的所有节点。,*表示所有节点。合:选择第一个li节点后的所有节点。

result =  html.xpath('//li[1]/following-sibling::*')
#following-sibling表示选取当前节点之后的所有同级节点。,*表示所有节点。合:选择第一个li节点后的所有同级节点。

result =  html.xpath('//li[1]/parent::*')
#选取当前节点的父节点。父节点只有一个,祖先节点可能多个。

result =  html.xpath('//li[3]/preceding::*')
#preceding表示选取文档中当前节点的开始标签之前的所有同级节点及同级节点下的节点。,*表示所有节点。合:选择第三个li节点前的所有同级节点及同级节点下的子节点。

result =  html.xpath('//li[3]/preceding-sibling::*')
#preceding-sibling表示选取当前节点之前的所有同级节点。,*表示所有节点。合:选择第三个li节点前的所有同级节点。

result =  html.xpath('//li[3]/self::*')
#选取当前节点。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

弄完这个,接下来就试试看能不能用Xpath将腾讯校园招聘网的数据抓取下来整理起来咯。

在这里插入图片描述

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/99313
 
314 次点击