土木工程在校学生一枚,对编程没什么了解,之前唯一接触过的编程类软件是Matlab,然而鼎鼎有名的科研大杀器在我手中却沦落成了计算器。
上周从导师那里了解到了python这个软件,感觉挺酷炫,再一查资料,莫名自信了起来,撸起袖子准备大干一场,各种百度google后才明白一个道理,纸上得来终觉浅,绝知此事要躬行!
看了一些书籍的案例,不是很感兴趣,看的也是一头雾水,思前想后还是决定对英雄联盟这个网站下手,爬取所有英雄的皮肤图片。
英雄列表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
再次请求,这一次终于获取到了所有英雄的信息,好家伙就这一个小问题折腾了一个上午!!!
预定的结果是在桌面建一个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)
自动创建了以英雄名称命名的文件夹后,下一步就是要把每个英雄的访问地址拿到,然后获取其皮肤图片。
访问英雄页面时又出现了一开始的数据不全问题,还是老样子从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)
接下来只需要从队列中拿URL去请求,获取到皮肤下载链接即可,通过对请求返回结果分析,可以看到结果有很多个,需要去手动进行筛选
仔细观察一下,发现每个皮肤信息中有好几个链接,有的一个链接也没有,那么一个个链接点进去看,发现只有‘mainImg’对应的URL才是真正的皮肤链接,所以就获取这个链接即可,通过关键词[.jpg]去mainImg中查找有没有链接
具体的代码如下:
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'])
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()
学习最重要的还是实践,只有独立思考,独立操作了才知道会遇到什么问题!
这个小案例花了我差不多一周的时间,遇到问题基本上就是各种百度、google、贴吧、论坛到处找答案,虽然是做出来了,感觉还是有很多问题存在,仅供参考,欢迎指正!