当前位置 博文首页 > 主打Python的博客:python爬取英雄联盟皮肤结合多线程的方法

    主打Python的博客:python爬取英雄联盟皮肤结合多线程的方法

    作者:[db:作者] 时间:2021-07-02 21:37

    python爬取英雄联盟皮肤结合多线程的方法

    1.什么是多线程?

    多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。

    2.原理

    实现多线程是采用一种并发执行机制 。
    并发执行机制原理:简单地说就是把一个处理器划分为若干个短的时间片,每个时间片依次轮流地执行处理各个应用程序,由于一个时间片很短,相对于一个应用程序来说,就好像是处理器在为自己单独服务一样,从而达到多个应用程序在同时进行的效果 。
    多线程就是把操作系统中的这种并发执行机制原理运用在一个程序中,把一个程序划分为若干个子任务,多个子任务并发执行,每一个任务就是一个线程。这就是多线程程序 。
    在这里插入图片描述

    3.优点

    1、使用线程可以把占据时间长的程序中的任务放到后台去处理 。
    2、用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度。
    3、程序的运行速度可能加快 。
    4、在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。在这种情况下可以释放一些珍贵的资源如内存占用等 。
    5、多线程技术在IOS软件开发中也有举足轻重的作用。

    4.缺点

    1、如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换。
    2、更多的线程需要更多的内存空间。
    3、线程可能会给程序带来更多“bug”,因此要小心使用。
    4、线程的中止需要考虑其对程序运行的影响。
    5、通常块模型数据是在多个线程间共享的,需要防止线程死锁情况的发生。

    好的废话不多说,我们直接来实战吧

    1.进入英雄联盟官网点击游戏资料进入此画面

    在这里插入图片描述

    2.确定爬取的网页是同步加载还是异步加载

    1.鼠标右键打开网页源代码
    2.ctrl+f打开搜索框
    3.在搜索框了输入英雄的名字
    在这里插入图片描述

    没有搜索结果则为异步加载

    3.寻找英雄url地址

    回到英雄页面鼠标右击打开检查。
    在这里插入图片描述
    在获取的包中找到hero_list.js这个包 英语翻译过来英雄列表.js文件
    在这里插入图片描述
    点击网页上英雄安妮拿回地址再点击其他英雄拿回地址做比较。
    安妮:
    在这里插入图片描述
    狂战士:
    在这里插入图片描述
    点击headers拿回resquests url
    安妮:https://game.gtimg.cn/images/lol/act/img/js/hero/1.js
    狂战士:https://game.gtimg.cn/images/lol/act/img/js/hero/2.js

    可以发现变化在最后面 是英雄的id 这样我们有思路了

    1.第一次发送请求,拿回所有英雄的id和名字
    2.第二次请求,得到英雄皮肤名,英雄手机皮肤url, 英雄电脑皮肤url
    3.请求得到 手机图片二进制数据, 电脑图片二进制数据
    4.保存电脑版英雄联盟图片,保存手机版英雄联盟图片
    5.多进程实现保存数据

    是时候写一点代码了。。。嘿嘿

    起始地址弄成全局变量
    在这里插入图片描述
    观察网页
    在这里插入图片描述
    1第一次请求,我们要拿回这两个数据。可以看出preview里面是个json数据,导入jsonpath库进行提取数据。
    在这里插入图片描述
    2第二次请求,得到英雄皮肤名,英雄手机皮肤url, 英雄电脑皮肤url
    在这里插入图片描述
    在这里插入图片描述

    3 请求得到 手机图片二进制数据, 电脑图片二进制数据 利用try-except语句防止报错停止代码运行。
    在这里插入图片描述
    4.保存电脑版英雄联盟图片,保存手机版英雄联盟图片,利用try-except语句防止报错停止代码运行。

    在这里插入图片描述
    5.多进程实现保存数据 导包:import threading
    threading.Thread(target=self.函数名, args=(用到的参数)) 写法

    在这里插入图片描述

    代码全解:

    # !/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    import requests, jsonpath, os, threading
    from pprint import pprint
    
    
    class LOL(object):
        def __init__(self):
            self.hero_list_url = r'https://game.gtimg.cn/images/lol/act/img/js/heroList/hero_list.js?v=50'
            self.hero_url = r'https://game.gtimg.cn/images/lol/act/img/js/hero/{}.js'
            self.headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                              'Chrome/87.0.4280.66 Safari/537.36'
            }
    
        def parse_start_url(self):
            '''  第一次请求获取, 英雄id, 英雄名  '''
            response = requests.get(self.hero_list_url, headers=self.headers).json()
            hero_ids = jsonpath.jsonpath(response, '$..heroId')
            hero_names = jsonpath.jsonpath(response, '$..name')
            for hero_name, hero_id in zip(hero_names, hero_ids):
                self.parse_next_url(hero_id, hero_name)
    
        def parse_next_url(self, hero_id, hero_name):
            '''  第二次请求,得到英雄皮肤名,英雄手机皮肤url, 英雄电脑皮肤url '''
            hero_info = requests.get(self.hero_url.format(hero_id), headers=self.headers).json()
            hero_skins_names = jsonpath.jsonpath(hero_info, '$..name')[2::]
            hero_skins_computer_urls = jsonpath.jsonpath(hero_info, '$..mainImg')[1::]
            hero_skins_phone_urls = jsonpath.jsonpath(hero_info, '$..loadingImg')[1::]
            for hero_skins_name, hero_skins_computer_url, hero_skins_phone_url in zip(hero_skins_names, hero_skins_computer_urls, hero_skins_phone_urls):
                self.get_img_content(hero_skins_name, hero_skins_computer_url, hero_skins_phone_url, hero_name)
    
        def get_img_content(self, hero_skins_name, hero_skins_computer_url, hero_skins_phone_url, hero_name):
            '''  请求得到 手机图片二进制数据, 电脑图片二进制数据  '''
            try:
                hero_skin_computer_content = requests.get(hero_skins_computer_url, headers=self.headers).content
                hero_skin_phone_content = requests.get(hero_skins_phone_url, headers=self.headers).content
                self.process_save(hero_name, hero_skin_computer_content, hero_skin_phone_content, hero_skins_name)
            except Exception as e:
                print('***手机版***炫彩皮肤无法下载!{}'.format(hero_skins_name))
    
        def save_computer_img(self, hero_name, hero_skin_computer_content, hero_skins_name):
            ''' 保存电脑版英雄联盟图片 '''
            if not os.path.exists(r'./{}/{}'.format('电脑版皮肤', hero_name)):
                os.makedirs(r'./{}/{}'.format('电脑版皮肤', hero_name))
            with open(r'./{}/{}/{}.jpg'.format('电脑版皮肤', hero_name, hero_skins_name), 'wb') as f:
                f.write(hero_skin_computer_content)
                print(r'***正在下载电脑版皮肤:{}/{}.jpg'.format(hero_name, hero_skins_name))
    
        def save_phone_img(self, hero_name, hero_skin_phone_content, hero_skins_name):
            ''' 保存手机版英雄联盟图片 '''
            if not os.path.exists(r'./{}/{}'.format('手机版皮肤', hero_name)):
                os.makedirs(r'./{}/{}'.format('手机版皮肤', hero_name))
            with open(r'./{}/{}/{}.jpg'.format('手机版皮肤', hero_name, hero_skins_name), 'wb') as f:
                f.write(hero_skin_phone_content)
                print(r'***正在下载手机版皮肤:{}/{}.jpg'.format(hero_name, hero_skins_name))
    
        def process_save(self, hero_name, hero_skin_computer_content, hero_skin_phone_content, hero_skins_name):
            ''' 多进程实现保存数据 '''
            threading.Thread(target=self.save_computer_img, args=(hero_name, hero_skin_computer_content, hero_skins_name)).start()
            threading.Thread(target=self.save_phone_img, args=(hero_name, hero_skin_phone_content, hero_skins_name)).start()
    
        def main(self):
            self.parse_start_url()
    
    
    if __name__ == '__main__':
        lol = LOL()
        lol.main()
    
    

    最后在发一个守护线程防止报错的模板,大家好好参考。

    from threading import Thread
    from queue import Queue

    class Love(object):
    def init(self):
    # 队列容量,队列创建 ,[], {}
    self.q = Queue()

    def parse_data(self):
        """功能:往队列添加数据"""
        data = "第{}天----我爱你----"
        for i in range(1, 100):
            # 将数据放入队列,put的时候计数+1,get不会-1,get需要和task_done一起使用才会-1
            self.q.put(data.format(i))
        # 等待task_done()返回的信号量和put进去的数量一直才会往下执行
        # join所完成的工作就是线程同步,即主线程任务结束之后,进入阻塞状态,一
        # 直等待其他的子线程执行结束之后,主线程在终止,否则主线程会杀死子线程
        # 主线程结束后子线程无论是否执行完毕都将结束,因此join的作用就显现出来了
        self.q.join()
    
    def func2(self):
        """功能:从队列中获取数据"""
        while True:
            # 循环从队列中获取, 取出数据,队列为空的时候会等待
            result = self.q.get()
            print(result)
            # 使队列计数-1
            self.q.task_done()
    
    def run(self):
        # 进程创建
        """进程:功能:往队列中添加数据"""
        m1 = Thread(target=self.parse_data)
        """进程:功能:从队列里面获取数据"""
        m2 = Thread(target=self.func2)
        m1.start()
        # 将m2设置成守护进程 因为m2一直是死循环,设置成守护进程之后当主程序代码运行完毕,m2就会结束,不会成为僵尸进程
        # 即只在需要的时候才启动,完成任务后就自动结束
        m2.daemon = True
        m2.start()
        # 队列中维持了一个计数,计数不为0时候让主线程阻塞等待,队列计数为0的时候才会继续往后执行
    
        m1.join()
    

    if name == ‘main’:
    love = Love()
    love.run()
    在这里插入图片描述

    祝大家学习python顺利

    cs