动态网页抓取( 移动端的英雄联盟官网英雄高清壁纸分析目标网站介绍)
优采云 发布时间: 2022-02-14 14:14动态网页抓取(
移动端的英雄联盟官网英雄高清壁纸分析目标网站介绍)
一、背景
随着移动终端的普及,出现了很多移动APP,应用软件也开始流行起来。最近看到英雄联盟手游上线了,感觉还行。PC端的英雄联盟可谓是一款热门游戏。不知道英雄联盟在移动端的未来是什么。墙纸。
二、页面分析
目标 网站:#Navi
官网界面如图所示。很明显,一张小图就代表了一个英雄。我们的目的是爬取每个英雄的所有皮肤图片,全部下载并保存到本地。
二级页面
上面的页面称为主页面,副页面是每个英雄对应的页面。以黑暗女士为例。其二级页面如下:
我们可以看到有很多小图,每张小图对应一个皮肤,通过网络查看皮肤数据接口,如下图所示:
我们知道皮肤信息是以json格式作为字符串传输的,那么我们只需要找到每个英雄对应的id,找到对应的json文件,提取需要的数据就可以得到高清皮肤壁纸。
那么这里暗黑少女的json的文件地址为:
hero_one = 'https://game.gtimg.cn/images/lol/act/img/js/hero/1.js'
这里的规则其实很简单。各英雄皮肤数据地址如下:
url = 'https://game.gtimg.cn/images/lol/act/img/js/hero/{}.js'.format(id)
所以问题是 id 的规则是什么?这里需要在首页查看英雄的id,如下:
我们可以看到两个列表[0, 99], [100, 156],也就是156个英雄,但是heroId已经到了240....可以看出有一定的变化规律,不是加一个因此,要爬取所有的英雄皮肤图片,首先需要获取所有的 heroId。
三、采集想法
为什么要使用多线程?在这里解释一下。我们在爬取图片、视频等数据的时候,需要保存在本地,所以会用到大量的文件读写操作,也就是IO操作。想象一下,如果我们执行同步请求操作;
然后,在第一次请求完成后,文件保存到本地后,才会发出第二次请求,效率非常低。如果使用多线程进行异步操作,效率会大大提高。
因此,需要使用多线程或多进程,然后将这么多数据队列扔到线程池或进程池中进行处理;
在 Python 中,multiprocessing Pool 进程池,multiprocessing.dummy 非常有用。
multiprocessing.dummy 模块:dummy 模块是多线程的;
多处理模块:多处理是多进程;
multiprocessing.dummy模块和multiprocessing模块的API通用,代码切换更灵活;
我们首先在一个测试 demo.py 文件中获取英雄 ID。我这里已经写了代码来获取英雄id的列表,可以直接在主文件中使用;
演示.py
url = 'https://game.gtimg.cn/images/lol/act/img/js/heroList/hero_list.js'
res = requests.get(url,headers=headers)
res = res.content.decode('utf-8')
res_dict = json.loads(res)
heros = res_dict["hero"] # 156个hero信息
idList = []
for hero in heros:
hero_id = hero["heroId"]
idList.append(hero_id)
print(idList)
idList获取方式如下:
idlist = [1,2,3,….,875,876,877] # 中间的英雄 id 这里不做展示
构造网址:page = '{}.html'.format(i)
这里的 i 表示 id,进行 url 的动态构建;
然后我们自定义两个函数,一个用于抓取和解析页面(spider),另一个用于下载数据(download),打开线程池,使用for循环构造一个url,存放英雄皮肤的json数据,它作为url Queue存储在一个列表中,使用pool.map()方法执行蜘蛛(爬虫)功能;
def map(self, fn, *iterables, timeout=None, chunksize=1):
"""Returns an iterator equivalent to map(fn, iter)”“”
# 这里我们的使用是:pool.map(spider,page) # spider:爬虫函数;page:url队列
功能:提取列表中的每一个元素作为函数的参数,一个一个地创建一个进程,放入进程池中;
参数1:要执行的函数;
参数2:迭代器,将迭代器中的数字作为参数依次传递给函数;
json数据解析
这里我们解析一下黑暗女神皮肤的json文件。我们需要获取的内容是1.name,2.skin_name,3.mainImg,因为我们发现heroName是一样的,所以以英雄名作为皮肤文件夹名英雄,便于查看和保存;
item = {}
item['name'] = hero["heroName"]
item['skin_name'] = hero["name"]
if hero["mainImg"] == '':
continue
item['imgLink'] = hero["mainImg"]
有一个注意事项:
有的 mainImg 标签是空的,所以我们需要跳过,否则如果是空的链接,请求时会报错;
四、数据采集
导入相关第三方库
import requests # 请求
from multiprocessing.dummy import Pool as ThreadPool # 并发
import time # 效率
import os # 文件操作
import json # 解析
页面数据分析
def spider(url):
res = requests.get(url, headers=headers)
result = res.content.decode('utf-8')
res_dict = json.loads(result)
skins = res_dict["skins"] # 15个hero信息
print(len(skins))
for index,hero in enumerate(skins): # 这里使用到enumerate获取下标,以便文件图片命名;
item = {} # 字典对象
item['name'] = hero["heroName"]
item['skin_name'] = hero["name"]
if hero["mainImg"] == '':
continue
item['imgLink'] = hero["mainImg"]
print(item)
download(index+1,item)
下载 下载图片
def download(index,contdict):
name = contdict['name']
path = "皮肤/" + name
if not os.path.exists(path):
os.makedirs(path)
content = requests.get(contdict['imgLink'], headers=headers).content
with open('./皮肤/' + name + '/' + contdict['skin_name'] + str(index) + '.jpg', 'wb') as f:
f.write(content)
这里我们使用 OS 模块来创建一个文件夹。前面我们提到过,每个英雄的 heroName 的值都是一样的,所以我们可以创建一个文件夹并命名,方便皮肤保存(分类),然后这里就是图片文件的路径。需要小心,少一个斜线会报错。
main() 主函数
def main():
pool = ThreadPool(6)
page = []
for i in range(1,21):
newpage = 'https://game.gtimg.cn/images/lol/act/img/js/hero/{}.js'.format(i)
print(newpage)
page.append(newpage)
result = pool.map(spider, page)
pool.close()
pool.join()
end = time.time()
操作说明:
在 main 函数中,我们更喜欢创建六个线程池;
通过for循环动态构建20个url,我们试一试,20个英雄皮肤,如果全部爬取,可以遍历之前的idList,然后动态构建url;
使用map()函数对线程池中的url进行数据解析和存储操作;
当线程池关闭时,线程池并没有关闭,而是状态变为不能再插入元素的状态;
五、程序运行
if __name__ == '__main__':
main()
结果如下:
当然,这里只抓取了部分图片,总共爬取了200+张图片,总体还可以。
六、总结
这次我们使用多线程抓取英雄联盟官网的英雄皮肤高清壁纸。因为图片中涉及到IO操作,所以我们使用并发的方式来大大提高程序的执行效率。
当然,爬虫也只是尝过而已。这一次,我爬取了20个英雄的皮肤图。有兴趣的朋友可以爬取所有的皮肤,只需要将遍历的元素改成之前的idlist即可。
原文链接: