1、准备工作
Python 3.7
Python安装有requests模块
王者荣耀官网地址:https://pvp.qq.com/web201605/wallpaper.shtml###
2、网站分析
由于现在的电脑分辨率基本都是1920x1080,所以这里以此分辨率作为例子
2.1、首先我们分析同一图片,不同分辨率的图片,我们对比他们的相同点和不同点。下面以庄周·奇妙博物学为例,打开他的不同分辨率的照片,查看他的url地址
1920x1080:http://shp.qpic.cn/ishow/2735030613/1551850681_-695593207_28893_sProdImgNo_6.jpg/0
1400x900:http://shp.qpic.cn/ishow/2735030613/1551850681_-695593207_28893_sProdImgNo_5.jpg/0
1920x1200:http://shp.qpic.cn/ishow/2735030613/1551850682_-695593207_28893_sProdImgNo_7.jpg/0
对比以上三个url地址可以看出,图片分辨率的是sProdImgNo的号码不同,爬取不同的分辨率我们只需要更改后面的号码就可以了。
2.2、下面我们来分析相同分辨率不同角色图片
庄周·奇妙博物学:http://shp.qpic.cn/ishow/2735030613/1551850681_-695593207_28893_sProdImgNo_6.jpg/0
1:1等身雕塑·凯:http://shp.qpic.cn/ishow/2735022611/1551150496_-695593207_2194_sProdImgNo_6.jpg/0
盘古·创世神祝:http://shp.qpic.cn/ishow/2735021914/1550557556_1186005513_23586_sProdImgNo_6.jpg/0
这里我们可以看到,出了前面相同的域名和后面表示高清的图像分辨率参数之外,再结合2.1的对比,中间的这一串数字参数我想大概就是表示图片的id了,但是我们要批量爬取,就必须要找出所有图片中间这一串id的分布规律,这里我按照图片的显示顺序连续点开了几个,并没有发现比较明显的规律。
2.3、接下来我点击了下面的翻页按钮,发现每翻一页,网站的整个页面并没有全部被刷新,只有显示图片的这一部分被刷新。那么在点击到第几页的时候,肯定是响应了某个函数向服务器发送了get或者post请求,然后服务器只传回了下一页图片的相关信息。这就简化了我们的操作步骤,我们只需要模拟这一个请求,服务器就会传回图片信息。接下来做如下操作:
我们看到当我们点击到第二页的时候,浏览器向服务器只发送了一个网络请求,所以这个请求就是我们获取图片地址的关键。由于是get方法,我们只需要把这个url贴到浏览器地址栏,就能得到服务器传回的信息。
上图可以看到服务器传回了一堆很长的字符串,我这里用postman重新请求了一下,如下图:
看起来比浏览器上稍微工整了一点,的但还是不太容易读,可以看出他是一个jQuery数据包,里面封装的是一个json数据包,于是我换成了json格式的页面,一下格式就变得非常明显
从这个数据包里我们可以看到他的List参数里面,每一个List,都封装了一个图片的信息,而这一个List里面,有20个数据,对应了一页的20张图片。而对我们来说,需要的就只有下载图片的地址和保存图片时需要的图片名称,由前面的分析,我们只需要每个List的这两个参数,如下图:
当然对应不同的分辨率,我们可以选择不同的sProdImgNo参数。
2.4、由2.3我们可以得到想要的参数值,但是这个参数值是经过编码隐藏的,如果不进行解析,我们将不能直接使用。我们先来观察图片的地址:
加密地址:"http%3A%2F%2Fshp%2Eqpic%2Ecn%2Fishow%2F2735012213%2F1548136530%5F%2D695593207%5F9275%5FsProdImgNo%5F6%2Ejpg%2F200"
真实地址:http://shp.qpic.cn/ishow/2735012213/1548136530_-695593207_9275_sProdImgNo_6.jpg/0
这里我们仔细对比这个加密地址和真实的地址,有一部分是我们看的懂的,有一部分又是看不懂的,再与其他地址对比,得出了以下解密表:
'%3A'==>':' '%2F'==> '/' '%2E'==>'.' '%5F'==> '_' '%2D'==> '-' '200'==>'0'
以上是url的解密,这个在接收后只需要进行字符串的替换就可以解决。但是图片名字并不是这样的编码。这里他使用了函数编码,所以只能用函数解码,在python中,可以使用unquote(String)来进行解码。对应的地址为urllib.parse.unquote(String)
2.5、上面我们解决了获得一页图片链接的方法,但是我们想要的不只是一页数据,因此我们接下来要分析的是多页的请求。
连续请求了2、3、4页的数据,对比他们的请求参数 ,以上是三个请求的区别。第一个page参数就代表页数,第一页的值为0,往后依次增加。第二个参数jsoncallback其实就是上面我们收到的jQuery包的包头,他最后一位的数值会随着请求的次数进行加1,最后一位其实是当前的时间戳。经过请求实践发现,如果去掉jsoncallback参数,收到的数据将会是去掉外层jQuery包的纯json数据,最后一个时间戳的参数同样也可以不传。所以需要哪一页的数据,只需要更改page的参数值就可以了。使用时就可以用如下url进行请求:
https://apps.game.qq.com/cgi-bin/ams/module/ishow/V1.0/query/workList_inc.cgi?activityId=2735&sVerifyCode=ABCD&sDataType=JSON&iListNum=20&totalpage=0&page={页码-1}&iOrder=0&iSortNumClose=1&iAMSActivityId=51991&_everyRead=true&iTypeId=2&iFlowId=267733&iActId=2735&iModuleId=2735
3、源码
from urllib import request, parse
import requests
# 设置request代理服务器
proxy_support = request.ProxyHandler({'http': 'http://xx.xx.xx.xx:xx'})
opener = request.build_opener(proxy_support, request.HTTPHandler)
request.install_opener(opener)
# 设置requests代理服务器
ip, port = ("125.126.222.12", "9999")
proxy_url = "http://{0}:{1}".format(ip, port)
proxy_dict = {
'https': proxy_url,
}
class wzry(object):
def __init__(self):
self.page = 0 # 抓取的起始页
self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64)'}# 伪装成浏览器
def get_page(self):
try:
while self.page <= 10: # 设置抓取的结束页
url = 'https://apps.game.qq.com/cgi-bin/ams/module/ishow/V1.0/query/workList_inc.cgi?'\
'activityId=2735&sVerifyCode=ABCD&sDataType=JSON&iListNum=20&totalpage=0&page='+str(self.page)+ '&i' \
'Order=0&iSortNumClose=1&iAMSAc'\
'tivityId=51991&_everyRead=true&iTypeId=2&iFlowId=267733&'\
'iActId=2735&iModuleId=2735&_=1554873059538'
self.page = self.page + 1
req = request.Request(url, headers=self.headers)
response = request.urlopen(req)
data = response.read()
list_data = eval(data)['List']
for ls in list_data:
# 抓取图片url并替换特殊符号
sProdImgNo_6 = ls['sProdImgNo_6'].replace('%3A', ':').replace('%2F', '/').replace('%2E', '.').replace('%5F', '_').replace('%2D', '-').replace('200', '0')
sProdName = ls['sProdName']
img_name = parse.unquote(sProdName) # 解码字符串
img = requests.get(sProdImgNo_6, proxies=proxy_dict, verify=False) # 抓取图片
print(f'正在抓取 {img_name} 高清皮肤......')
# 写入文件
with open(f'G:/wzry/{img_name}.jpg', 'wb') as f:
f.write(img.content)
except request.URLError as e:
if hasattr(e, 'reason'):
print(f'抓取失败,失败原因:{e.reason}')
class main():
wzry().get_page()
if __name__ == '__main__':
main()
print('抓取完成。。。')
注意:请求url的时候一定要设置代理服务器,因为连续大量的请求,服务器会认为这是黑客攻击,会拒绝你当前ip的连接,导致请求失败,严重的话服务器会把你的ip或者与你同网段的ip地址拉入黑名单,导致一段时间无法访问连接。
下面附上成果图: