最近在写爬虫,基本的请求库、解析库也复习了一遍。我这个人比较喜欢造轮子,不喜欢黑盒子,爬虫还是喜欢用基本的库一点一点写,这样虽然复杂了一点,但是有了掌控感。
闲来无事,找个事情做一做,就写个爬虫爬一下LOL官网上英雄的皮肤玩玩,也算是纪念曾经玩LOL的生涯。
打开英雄总览的网页,所有的英雄都在这里了。首先想到的是从这个页面拿到各个英雄的子页面,在子页面提取到皮肤图片的链接,下载图片完工。
就参照这个思路开始做吧!
首先在主页面看了一下网页源码,没有找到各个英雄的信息。比如暗裔剑魔、亚托克斯、Aatrox这些全都找不到。
主页面只是个空壳,没有有用信息
不过找到了一个js函数,发现了构造英雄子页面链接的一些端倪:
因为各个英雄的链接只有id不同,其实也就是英雄的英文名字,现在我只需要拿到英雄的id就好了。那么从哪里找这些id呢?
随后我在开发者工具中搜索剑魔的idAatrox
,发现一个名为champion.js
的文件中有所有英雄的,而且有用信息是json格式,这就比较方便了。于是构造请求,拿到这个js的信息:
英雄的id都在
champion.js
中
def getHeroDetail(this)->None:
url = 'https://lol.qq.com/biz/hero/champion.js'
r = requests.get(url = url, headers = this.headers)
rawContent = r.content.decode('utf-8')
this.herosDetail = json.loads(rawContent[50:-1])
现在已经有了各个英雄的id,可以构造出来子页面的链接了。可是到子页面一看,网页源码里面依旧没有皮肤图片的链接。
子页面也是个空壳
构造皮肤图片需要找到ids
编号
不过我又找到了一个跟显示图片有关的js函数:
原来所有图片对应的url只有ids不同。
不过到哪里找这个ids呢?我又从开发者工具中搜索剑魔的默认皮肤图片的ids266000
,找到了一个js。我把这个js复制到本地研究了一下,发现里面含有剑魔的所有皮肤对应的ids。而且这个js是以英雄的id命名的。
ids编号在
英雄id.js
中
刚才已经拿到了所有英雄的id,那么就可以拿到所有英雄的这个js文件了,只需要从这个js里拿到ids就可以构造出皮肤图片的url。那么这样的话,英雄子页面的链接就没什么用了!
def getSkins(this)->list:
'''
返回皮肤的url,以及皮肤的名字。
第一级元素是英雄级别
第二级元素是单个英雄的皮肤级别
第三级元素是皮肤url和皮肤名字。比如:(url, 腥红之月 亚托克斯)
'''
skinBaseUrl = 'https://ossweb-img.qq.com/images/lol/web201310/skin/big'
skins = []
for title in this.herosDetail['keys'].values():
jsUrl = 'https://lol.qq.com/biz/hero/'+title+'.js'
r = requests.get(jsUrl, this.headers)
rawContent = r.content.decode('utf-8')
hero = json.loads(re.search('(.*?\=){2}(\{.*\})', rawContent).group(2))
skins.append([(skinBaseUrl+i['id']+'.jpg', i['name'] if i['name'] != 'default' else this.herosDetail['data'][title]['name']) for i in hero['data']['skins']])
return skins
保存图片要注意文件名不能有
/
符号
已经拿到了皮肤的url和皮肤名字,随后就是皮肤图片的保存了:
def saveSkins(this):
for aHero in this.getSkins():
for skinUrl, name in aHero:
try:
name = re.sub('/', '/', name)#文件名中不能有`/`符号,要把半角替换为全角
r = requests.get(skinUrl, this.headers)
with open(name+'.jpg', 'wb') as f:
f.write(r.content)
print('Saved', name)
except(KeyboardInterrupt):
sys.exit(0)
except:
print('Filed to save', name)
再定义一个启动函数:
def run(this):
this.getHeroDetail()
this.saveSkins()
开启主函数
if __name__ == '__main__':
spider = LOLSpider()
spider.run()
到这里已经写完了所有的代码。贴一下完整的代码:
#============================================================
# Create Time: 2019-07-15 22:48:33
# Writer: Wenhao [email protected]
# File Name: LOLSpider.py
# File Type: PY Source File
# Tool: Mac -- vim & python
# Information: 爬取LOL官网上的所有英雄皮肤,存到当前文件夹中
#============================================================
#coding=utf-8
import requests
import json
import re
import sys
class LOLSpider():
headers = {'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36'}
def getHeroDetail(this)->None:
url = 'https://lol.qq.com/biz/hero/champion.js'
r = requests.get(url = url, headers = this.headers)
rawContent = r.content.decode('utf-8')
this.herosDetail = json.loads(rawContent[50:-1])
def getSkins(this)->list:
'''
返回皮肤的url,以及皮肤的名字
第一级元素是英雄级别
第二级元素是单个英雄的皮肤级别
第三级元素是皮肤url和皮肤名字。比如:(url, 腥红之月 亚托克斯)
'''
skinBaseUrl = 'https://ossweb-img.qq.com/images/lol/web201310/skin/big'
skins = []
for title in this.herosDetail['keys'].values():
jsUrl = 'https://lol.qq.com/biz/hero/'+title+'.js'
r = requests.get(jsUrl, this.headers)
rawContent = r.content.decode('utf-8')
hero = json.loads(re.search('(.*?\=){2}(\{.*\})', rawContent).group(2))
skins.append([(skinBaseUrl+i['id']+'.jpg', i['name'] if i['name'] != 'default' else this.herosDetail['data'][title]['name']) for i in hero['data']['skins']])
return skins
def saveSkins(this):
for aHero in this.getSkins():
for skinUrl, name in aHero:
try:
name = re.sub('/', '/', name)#文件名中不能有`/`符号,要把半角替换为全角
r = requests.get(skinUrl, this.headers)
with open(name+'.jpg', 'wb') as f:
f.write(r.content)
print('Saved', name)
except(KeyboardInterrupt):
sys.exit(0)
except:
print('Filed to save', name)
def run(this):
this.getHeroDetail()
this.saveSkins()
if __name__ == '__main__':
spider = LOLSpider()
spider.run()
这个爬虫写的还算顺利,虽然中间也经历了一些曲折,并没有按照自己最初设想的步骤完成,也算是在尝试中达到了目的。要注意有用的信息在js里面,主页面都是一个空壳,要想拿到信息还得去找到信息所在的请求。