前言
今天,我们用python语言来实现爬荣耀所有英雄的皮肤图片,下载并保存到本地,文章后面有完整代码。
一、思路分析
网站分析
在官网上我们可以看到许多的英雄,每一个英雄都对应着一个网页,而每一个网页网址的区别在于数字不同(大家可以去看看),我们要爬的皮肤图片就在里面。当时我猜测,英雄网页网址的构建是不是存在规律可循,经过分析发现没有任何规律可循,那没办法,我们只能从官网上把每一个英雄网址爬下来,然后一个个去请求(小部分爬不到,估计是异步加载),获得皮肤图片。
当我们请求英雄网址,爬下来的数据跟在网页中的数据有出入。
于是我去尝试抓包,找到了皮肤图片地址,经过分析发现518(每一个英雄网页都有特定的数字)代表特定的标识,1代表第一张皮肤图片,2代表第一张皮肤图片,3代表第一张皮肤图片,以此类推(其他的都是这样的)
那我们只要构建皮肤图片地址,发送请求就能获取到图片了,但是问题又来了,那我们怎们知道皮肤的数量呢?英雄的皮肤有时多有时少,很不统一,其实我们分析可以发现,皮肤名跟皮肤的图片是按顺序一一对应的,如 英雄名1:图片1 英雄名2:图片2 英雄名3:图片3,以此类推,所以我们可以通过英雄名来知道图片的数量,还能给每一张图片取到对应的名字,一举两得。下面讲一讲代码思路
代码思路
爬取官网每一个英雄对应的网址,然后一个个去请求,获得皮肤图片的数量(不知道皮肤图片的数量就不知道构建几个皮肤图片地址),构建皮肤图片地址后,再去一张张请求皮肤地址,获得皮肤图片,一张张保存图片。到此就完。
二、环境配置
# python+3.8.8
# requests 2.25.1
# parsel 1.6.0
# 安装
# pip install requests==2.25.1
# pip install parsel==1.6.0
# 如果觉得安装太慢,可以用镜像源
# pip install -i https://mirrors.tuna.tsinghua.edu.cn/help/pypi/ requests==2.25.1
# pip install -i https://mirrors.tuna.tsinghua.edu.cn/help/pypi/ parsel==1.6.0
三、完整代码
import requests
import parsel
import os # 内置库不需要安装
import re # 内置库不需要安装
import logging # 内置库不需要安装
# 发送请求
def get_url(url):
headers = {
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'accept-language': 'zh-CN,zh;q=0.9',
'pragma': 'no-cache',
'referer': 'https://pvp.qq.com/',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36'}
response = requests.get(url=url, headers=headers)
# 获取网页编码,避免乱码
response.encoding = response.apparent_encoding
return response
# 解析数据
def parser_data(html_data, css_rule, css_1, css_2):
# 传入html数据
html = parsel.Selector(html_data)
# 二次提取
li = html.css(css_rule)
data_list = []
for i in li:
# 获取英雄名(皮肤名)
name = i.css(css_1).get()
# 获取网页地址(图片地址)
url_data = i.css(css_2).get()
data_list.append([name, url_data])
# [[],[],[]......]
return data_list
def first_data(url):
html_1 = get_url(url).text
data_list = parser_data(html_1, '.herolist li', 'a::text', 'a::attr(href)')
u = []
for k in data_list:
# 提取网址中的数字,后面构造图片地址用
num = re.findall(r'\d+', k[1])[0]
# 构造每一个英雄的网页地址
url_ju = 'https://pvp.qq.com/web201605/' + k[1]
# 替换原先的地址
k[1] = url_ju
k.append(num)
u.append(k)
print(k)
# 返回形式[[],[],[],[].....]
return u
def second_data(url_list):
total = []
for g in url_list:
html_2 = get_url(g[1]).text
# 皮肤名字爬取不到了,返回为None
data = parser_data(html_2, '.pic-pf-list ', 'li p::text', '::attr(data-imgname)')
# 将None值删除
for y in data:
y.remove(None)
# 将英雄名和数字插入
data[0].insert(0, g[0])
data[0].insert(1, g[2])
total.append(data)
print(data)
# [[],[],[],[]......]
return total
def save_file(data, dir_name, file_name):
'''
:param data: 图片数据
:param dir_name: 英雄的目录名
:param file_name: 保存图片的文件名
:return:
'''
if not os.path.exists('荣耀图片'):
os.mkdir('荣耀图片')
if not os.path.exists('王者图片\\' + dir_name):
os.mkdir('荣耀图片\\' + dir_name)
with open('荣耀图片\\' + dir_name + '\\' + file_name + '.jpg', mode='wb') as f:
f.write(data)
# 去除特殊字符,返回集合
def del_str(data_str):
name_list = []
jpg_name = data_str.split('|')
for h in jpg_name:
t = h.replace('&', '')
for number in range(10):
t = t.replace(str(number), '')
name_list.append(t)
return name_list, len(name_list)
if __name__ == '__main__':
# 设置输出的格式
LOG_FORMAT = "时间:%(asctime)s - 日志等级:%(levelname)s - 日志信息:%(message)s"
# 对logger进行配置——日志等级&输出格式
logging.basicConfig(level=logging.INFO, format=LOG_FORMAT)
# 官网地址!!!
url = ''
d = first_data(url)
total_list = second_data(d)
logging.info('总共' + str(len(total_list)) + '个英雄')
logging.info('开始爬取图片')
a = 1
# 遍历每一条数据
for n in total_list:
# 计数 英雄数
dir_name = str(a) + '.' + n[0][0]
logging.info('正在爬取' + dir_name + '皮肤')
# 特定的英雄id
num_id = n[0][1]
# 传入皮肤名 返回一个集合 ([皮肤名],皮肤数)
name_num = del_str(n[0][-1])
a += 1
# 构建图片地址,并爬取,皮肤地址是从1开始
for j in range(1, name_num[1] + 1):
logging.info('正在爬取' + name_num[0][j - 1])
jpg_url = f'https://game.gtimg.cn/images/yxzj/img201606/skin/hero-info/{
num_id}/{
num_id}-bigskin-{
j}.jpg'
# 爬取皮肤图片
jpg_content = get_url(jpg_url).content
# 保存图片
save_file(jpg_content, dir_name, name_num[0][j - 1])
logging.info('已保存' + name_num[0][j - 1])
logging.info(str(len(total_list)) + '个英雄' + '爬取完毕!')
四、总结
爬皮肤图片比较基础,适合刚入门练习爬虫。如果皮肤图片爬多了会遭到反爬,部分爬不到了,我记的刚开始是能爬到的,后来爬的次数多了,就爬不到了,大家可以尝试换ip,设置延迟时间,或者用selenium来爬。
如文章有错误,请指正,最后谢谢大家!!!