Python多线程下载英雄联盟所有英雄皮肤

Python多线程爬取英雄联盟所有英雄皮肤

  • 一、选择目标
  • 二、网站分析
    • 1.访问网页
    • 2.获取所有英雄名称并创建对应的文件夹
    • 3.获取单个英雄URL并放进队列中
    • 4.访问单个英雄网页获取皮肤下载地址
    • 5.完整代码
    • 6.结果展示
  • 总结

土木工程在校学生一枚,对编程没什么了解,之前唯一接触过的编程类软件是Matlab,然而鼎鼎有名的科研大杀器在我手中却沦落成了计算器。
上周从导师那里了解到了python这个软件,感觉挺酷炫,再一查资料,莫名自信了起来,撸起袖子准备大干一场,各种百度google后才明白一个道理,纸上得来终觉浅,绝知此事要躬行!


一、选择目标

看了一些书籍的案例,不是很感兴趣,看的也是一头雾水,思前想后还是决定对英雄联盟这个网站下手,爬取所有英雄的皮肤图片。

二、网站分析

1.访问网页

英雄列表url:https://lol.qq.com/data/info-heros.shtml

F12一开,源代码好像很简单,非常清晰规律,直接开干!!!
requests请求后一番操作后我开始意识到不对劲,get请求获得的结果和网页源代码不一样,少了亿点内容???

import requests

HEADERS = {
     
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) "
                  "Chrome/87.0.4 280.88 Safari/537.36",
}
url = 'https://lol.qq.com/data/info-heros.shtml'
res = requests.get(url, headers=HEADERS).content.decode('gbk')
print(res)

各种百度google后在Network中找到了真实的请求地址
https://game.gtimg.cn/images/lol/act/img/js/heroList/hero_list.js
Python多线程下载英雄联盟所有英雄皮肤_第1张图片
再次请求,这一次终于获取到了所有英雄的信息,好家伙就这一个小问题折腾了一个上午!!!

2.获取所有英雄名称并创建对应的文件夹

预定的结果是在桌面建一个LOL文件夹,子目录中放每个英雄的皮肤。

请求返回的数据转换使用json.loads加载成dict类型,然后就方便获取数据了.

text = json.loads(res)
hero_info = text['hero']
for hero in hero_info:
    path = f"C:/Users/xiang/Desktop/LOL/{hero['name'] + hero['title']}"
    folder = os.path.exists(path)
    if not folder:
        os.mkdir(path)

自动创建了以英雄名称命名的文件夹后,下一步就是要把每个英雄的访问地址拿到,然后获取其皮肤图片。

3.获取单个英雄URL并放进队列中

访问英雄页面时又出现了一开始的数据不全问题,还是老样子从Network中找到真实的请求地址,以第一个英雄《黑暗之女-安妮》为例,其真实请求地址如下:

https://game.gtimg.cn/images/lol/act/img/js/hero/1.js

如果你以为1是序号,按顺寻排下去的那就错了,这个数字1是英雄ID,刚开始是按顺序的,后面的就不是了,所以要先拿到英雄ID,然后拼接成地址。

英雄ID信息在一开始请求的英雄列表中就有,因此在请求时可以顺带把ID获取了,拼接成单个英雄的URL

为了便于多线程操作,创建了一个队列来存放这些英雄的URL

 hero_id = hero['heroId']
            hero_url = hero_base_url + hero_id + '.js'
            print(hero_url)
            self.urls_queue.put(hero_url)

4.访问单个英雄网页获取皮肤下载地址

接下来只需要从队列中拿URL去请求,获取到皮肤下载链接即可,通过对请求返回结果分析,可以看到结果有很多个,需要去手动进行筛选
Python多线程下载英雄联盟所有英雄皮肤_第2张图片
仔细观察一下,发现每个皮肤信息中有好几个链接,有的一个链接也没有,那么一个个链接点进去看,发现只有‘mainImg’对应的URL才是真正的皮肤链接,所以就获取这个链接即可,通过关键词[.jpg]去mainImg中查找有没有链接
Python多线程下载英雄联盟所有英雄皮肤_第3张图片
具体的代码如下:

   res = requests.get(self.urls_queue.get()).content.decode('gbk')
            text = json.loads(res)
            skins = text['skins']
            for skin in skins:
                if '.jpg' in skin['mainImg']:
                    skin_url = requests.get(skin['mainImg']).content
                    if '[/:*?"<>|·]' in skin['name'] or skin['heroTitle']:
                        skin['name'] = re.sub('[/:*?"<>|·]', '-', skin['name'])
                        skin['heroTitle'] = re.sub('[/:*?"<>|·]', '', skin['heroTitle'])
                    file_name = f"C:/Users/xiang/Desktop/LOL/{skin['heroName'] + skin['heroTitle']}/" + skin['name'] + '.jpg '
                    if os.path.exists(file_name):
                        print(skin['heroName'] + skin['heroTitle'] + '的皮肤:' + skin['name'] + '已存在')
                        pass
                    else:
                        with open(file_name, 'wb') as f:
                            f.write(skin_url)
                        print('正在下载' + skin['heroName'] + skin['heroTitle'] + '的皮肤:' + skin['name'])

5.完整代码

import requests
import threading
from queue import Queue
import json
import os
import re
import time


class Producer(threading.Thread):

    def __init__(self, url_queue, *args, **kwargs):
        super(Producer, self).__init__(*args, **kwargs)
        self.target_url = 'https://game.gtimg.cn/images/lol/act/img/js/heroList/hero_list.js'
        self.urls_queue = url_queue

    def run(self):
        res = requests.get(self.target_url).content.decode('gbk')  # 访问英雄列表,获取所有英雄名字和ID
        text = json.loads(res)
        hero_info = text['hero']
        hero_base_url = f'https://game.gtimg.cn/images/lol/act/img/js/hero/'
        for hero in hero_info:
            path = f"C:/Users/xiang/Desktop/LOL/{hero['name'] + hero['title'] }"  # 以英雄名称命名文件夹
            folder = os.path.exists(path)
            if not folder:
                os.mkdir(path)
            else:
                pass
            hero_id = hero['heroId']  # 获取到英雄ID
            hero_url = hero_base_url + hero_id + '.js'  # 拼接单个英雄访问链接
            print(hero_url)
            self.urls_queue.put(hero_url)  # 将链接存储到队列中


class Consumer(threading.Thread):
    def __init__(self, url_queue, *args, **kwargs):
        super(Consumer, self).__init__(*args, **kwargs)
        self.urls_queue = url_queue

    def run(self):
        time.sleep(1)  # 好像不停1s也可以,但是我在多次调试运行时发现会报403错误
        while True:
            if self.urls_queue.empty():  # 当队列中没有链接了就停止循环
                break
            res = requests.get(self.urls_queue.get()).content.decode('gbk')
            text = json.loads(res)
            skins = text['skins']
            for skin in skins:
                if '.jpg' in skin['mainImg']:  # 通过.jpg关键词来筛选mainIMG中有没有链接存在,不要用None来判断,有一些mainIMG下虽然是没东西,但有空格存在,会报错
                    skin_url = requests.get(skin['mainImg']).content
                    if '[/:*?"<>|·]' in skin['name'] or skin['heroTitle']:  # 这里是把一些皮肤名字中的特殊字符去掉,防止命名错误
                        skin['name'] = re.sub('[/:*?"<>|·]', '-', skin['name'])
                        skin['heroTitle'] = re.sub('[/:*?"<>|·]', '', skin['heroTitle'])
                    file_name = f"C:/Users/xiang/Desktop/LOL/{skin['heroName'] + skin['heroTitle']}/" + skin['name'] + '.jpg '
                    if os.path.exists(file_name):
                        print(skin['heroName'] + skin['heroTitle'] + '的皮肤:' + skin['name'] + '已存在')
                        pass
                    else:
                        with open(file_name, 'wb') as f:
                            f.write(skin_url)
                        print('正在下载' + skin['heroName'] + skin['heroTitle'] + '的皮肤:' + skin['name'])


if __name__ == '__main__':
    urls_queue = Queue(200)
    
    for i in range(1):  
        t = Producer(urls_queue)  # 我发现这个地方用多线程会报错,而且队列中有很多重复的链接,应该是要加锁,但是我没学会,只能用1个线程了
        t.start()
    for i in range(5):
        t = Consumer(urls_queue)
        t.start()



6.结果展示

Python多线程下载英雄联盟所有英雄皮肤_第4张图片


总结

学习最重要的还是实践,只有独立思考,独立操作了才知道会遇到什么问题!

这个小案例花了我差不多一周的时间,遇到问题基本上就是各种百度、google、贴吧、论坛到处找答案,虽然是做出来了,感觉还是有很多问题存在,仅供参考,欢迎指正!

你可能感兴趣的:(Python爬虫,python,爬虫,多线程,队列)