前言
LOL的皮肤的爬取是我当年刚学习爬虫时就想爬的一个网站,课程设计做的也是这个,不过由于当时技术水平有限,爬取的数据很潦草,所以现在我有时间了就又重新去看看这个网站是怎么爬更好了。
目标
1.是获取到皮肤数据(我选择保存的皮肤格式是png)
2.是要获取到皮肤的名字,来为皮肤命名
示例如下:
网站分析
在网上我也看了一些别人爬的教程,不过他们爬到的数据会不全,有很多的皮肤会爬取不到,所以我就自己下手了。
1.打开LOL的官网,滑到最下面,随便点一个英雄
我直接就选择第一个英雄安妮
打开界面,按F12之后,F5刷新,在网站反回来的网络请求中我找到了这一个json文件请求文件,这个文件一看就很可疑,点击预览之后果然,所需要的数据都放在了这一个json文件里面。
可以看到在里面有我们需要的英雄的名字,进过我对这些图片链接的一个个访问,我也找到了我们需要的图片链接
1.1补充说明
在这里插一句,网上的一些获取图片的方式是通过组合图片的链接
这样的链接是可以访问,前面的1是英雄的id,最后的0是英雄的第几个皮肤,修改这两个参数可以获取到图片数据
但是有个问题,这个方法只对很老的皮肤生效,应该是自从某一时间点之后,腾讯修改了数据存放方式
例如:
安妮有16个皮肤,当我们把数字改成16时,组合成的链接并不能访问
2.数据链接
观察链接:
https://game.gtimg.cn/images/lol/act/img/js/hero/1.js?ts=2800033
?之后的都可以不用看,属于一些参数。链接后面的1就很可疑
当把1换成2之后,很明显的看出来是又数据返回的,不过当我尝试其他的数字时,会出现404(尝试数字为130)
明明LOL有163位英雄,为什么输个130会找不到链接????(我爬的时候是163个英雄吼)
经过观察我发现这个数字其实对应的是英雄的id或者说官方给的编号
可以看到最新出的英雄和上一个英雄之前的id差了很多,这个问题在早期是没有存在的
3.获取英雄的id
要拿到英雄的id,进入英雄详情页是拿不到的,所以我们需要到详情页的上一级去看看。
点击英雄列表,刷新之后可以找到这一个名叫hero_list的json文件,里面就有我们需要的英雄id
4.思路整理
1.我们先访问hero_list文件的链接获取到英雄的id
url = 'https://game.gtimg.cn/images/lol/act/img/js/heroList/hero_list.js'
2.然后访问id对应的json文件,获取到皮肤图片和图片的名字
代码编写
所需库:
import os
import requests
import time
from concurrent.futures import ThreadPoolExecutor
设置爬虫的头文件:
header = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.54'
}
1.根据上面的思路分析我们第一步是拿到英雄的id,因为请求的返回是一个JSON文件,然后我们要的数据是放在列表里面,所以我们就不拿文本直接拿JSON文件,然后拿取存放数据的列表,循环列表把我们需要的数据拿出来,存储为一个新的列表。
# 获取LOL英雄的id
def ID():
# 创建一个列表用于存放英雄的id
id = []
# 访问存放有英雄id的接口
burl = 'https://game.gtimg.cn/images/lol/act/img/js/heroList/hero_list.js'
breq = requests.get(burl).json() # 拿接口的JSON文件可以更快的帮我们定位到想要的数据
for i in breq['hero']:
id.append(i['heroId']) # 把获取到的id放入列表
return id
2.根据英雄id访问存放有皮肤名称和皮肤链接的JSON链接,来获取到我们所需要的数据。要是选择返回的是文本,则需要对返回来的数据进行转码。
# 获取LOL英雄的名字作为文件的命名
def name_spider(id, choose):
names = [] # 存放英雄皮肤名字的列表
surl = [] # 存放英雄皮肤链接的列表
uname = 'https://game.gtimg.cn/images/lol/act/img/js/hero/' + str(id) + '.js'
# 访问的链接,拿到每个英雄的详细信息的JSON文件
ureq = requests.get(uname, headers=header).json()
# 对访问到的JSON文件进行处理,获取到我们想要的数据
for k in ureq['skins']:
if k['chromas'] == '0': # 当chromas标签 = 0时会是皮肤的原画( = 1是皮肤的炫彩,不过我们不需要这个)
names.append(k['name'])
surl.append(k['mainImg'])
# 爬取图片和保存函数(全写在一起就太多了点不好看,就分开写了)
photo_spider(names, surl, choose) # 爬到的皮肤名称和图片的链接作为参数传进函数
3.访问获取到的皮肤链接,然后把图片下载下来,因为我们要爬的图片,所以我们进行request访问的时候我们拿的返回是二进制流返回。
# 爬取具体图片函数,choose=0会把图片分门别类,放到对应的英雄的文件夹里面,其他数字就直接把图片存入指定文件夹
def photo_spider(names, surl, choose=1, path=r'G:\\工作\\工作文件\\代码学习\\LOL皮肤'):
# 判断选择的数据存储方式
if choose == 0:
# 下载的图片保存到的文件夹
os.makedirs(path + r'\\' + str(names[0])) # 在指定文件夹下创建个英雄皮肤的文件夹
os.chdir(path + r'\\' + str(names[0])) # 切换当前工作路径为指定路径
else:
os.chdir(path) # 切换当前工作路径为指定路径
# 对图片链接进行访问
for i in range(len(surl)):
photoreq = requests.get(surl[i]).content # 因为是图片,所以这里我们拿二进制流返回
# 保存到文件夹,按照皮肤的名字命名,保存的格式为png格式
with open(names[i].replace('/', '') + '.png', 'wb') as f: # 因为某些皮肤的名字中有"/"(例:K/DA这个系列的皮肤),会影响文件的保存,所以要替换掉
f.write(photoreq)
这里我提供了两中保存的格式,一种是全部图片放在一个文件夹里面,一种是根据英雄名字创建文件夹,然后把这个英雄对应的皮肤放进文件夹里面
4.开启多线程爬取,为了不让线程混乱,所以我们还要引入一个线程池
来点多线程笑话:
你想象中的多线程:
实际的多线程:
不加线程池就会这样,不过我们爬取的是图片,如果爬到一样的,后面爬的会把前面爬的覆盖,但是这样会让我们效率降低,那你这个多线程开和没开有什么区别是吧
多线程我放在了主函数里面:
if __name__ == '__main__':
# 程序开始时间
time_start = time.time()
choose = input("输入需要存储的类型(0为把皮肤分类放到各个英雄的文件夹下):")
# 调用函数爬取英雄的id
id = ID()
# 创建线程池,开启10个进程
pool = ThreadPoolExecutor(max_workers=10)
for pic_url in id:
# 将耗时间的任务放到线程池中交给线程来执行
pool.submit(name_spider, pic_url, choose) # 执行的函数,函数所需的参数
pool.shutdown() # 让main函数在线程全部结束之后再结束
# 程序结束时间
time_end = time.time()
print('程序执行的时间:' + str(time_end - time_start))
我这边开10个线程爬完全部的图片是耗时35s,如果是开单线程的话用时是350s,刚好快了10倍
总结
整体来说爬取难度不难,全部爬取下来也不用多少时间,这就是开了多线程的快速,不过我也是只开了10个线程。不过线程也不要开太多,不要给别人的网站造成太大的服务器压力,要是把人家爬崩了,警察叔叔可就要来找你了。
完整代码
# -- coding: utf-8 --
# @Time : 2023/3/10 14:16
# @File : LOL_Photo.py
# @Software: PyCharm
import os
import requests
import time
from concurrent.futures import ThreadPoolExecutor
header = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.54'
}
# 获取LOL英雄的id
def ID():
# 创建一个列表用于存放英雄的id
id = []
# 访问存放有英雄id的接口
burl = 'https://game.gtimg.cn/images/lol/act/img/js/heroList/hero_list.js'
breq = requests.get(burl).json() # 拿接口的JSON文件可以更快的帮我们定位到想要的数据
for i in breq['hero']:
id.append(i['heroId']) # 把获取到的id放入列表
return id
# 获取LOL英雄的名字作为文件的命名
def name_spider(id, choose):
names = [] # 存放英雄皮肤名字的列表
surl = [] # 存放英雄皮肤链接的列表
uname = 'https://game.gtimg.cn/images/lol/act/img/js/hero/' + str(id) + '.js'
# 访问的链接,拿到每个英雄的详细信息的JSON文件
ureq = requests.get(uname, headers=header).json()
# 对访问到的JSON文件进行处理,获取到我们想要的数据
for k in ureq['skins']:
if k['chromas'] == '0': # 当chromas标签 = 0时会是皮肤的原画( = 1是皮肤的炫彩,不过我们不需要这个)
names.append(k['name'])
surl.append(k['mainImg'])
# 爬取图片和保存函数(全写在一起就太多了点不好看,就分开写了)
photo_spider(names, surl, choose) # 爬到的皮肤名称和图片的链接作为参数传进函数
# 爬取具体图片函数,choose=0会把图片分门别类,放到对应的英雄的文件夹里面,其他数字就直接把图片存入指定文件夹
def photo_spider(names, surl, choose=1, path=r'G:\\工作\\工作文件\\代码学习\\LOL皮肤'):
# 判断选择的数据存储方式
if choose == 0:
# 下载的图片保存到的文件夹
os.makedirs(path + r'\\' + str(names[0])) # 在指定文件夹下创建个英雄皮肤的文件夹
os.chdir(path + r'\\' + str(names[0])) # 切换当前工作路径为指定路径
else:
os.chdir(path) # 切换当前工作路径为指定路径
# 对图片链接进行访问
for i in range(len(surl)):
photoreq = requests.get(surl[i]).content # 因为是图片,所以这里我们拿二进制流返回
# 保存到文件夹,按照皮肤的名字命名,保存的格式为png格式
with open(names[i].replace('/', '') + '.png', 'wb') as f: # 因为某些皮肤的名字中有"/"(例:K/DA这个系列的皮肤),会影响文件的保存,所以要替换掉
f.write(photoreq)
if __name__ == '__main__':
# 程序开始时间
time_start = time.time()
choose = input("输入需要存储的类型(0为把皮肤分类放到各个英雄的文件夹下):")
# 调用函数爬取英雄的id
id = ID()
# 创建线程池,开启10个进程
pool = ThreadPoolExecutor(max_workers=10)
for pic_url in id:
# 将耗时间的任务放到线程池中交给线程来执行
pool.submit(name_spider, pic_url, choose) # 执行的函数,函数所需的参数
pool.shutdown() # 让main函数在线程全部结束之后再结束
# 程序结束时间
time_end = time.time()
print('程序执行的时间:' + str(time_end - time_start))