当前位置: 首页 > 后端技术 > Python

Python爬虫实战-采用多线程爬取LOL高清壁纸

时间:2023-03-25 20:59:02 Python

随着移动终端的普及,出现了很多手机APP,应用软件也开始流行起来。最近拿起了英雄联盟手游,感觉还可以。PC端的英雄联盟可谓是一款爆款游戏。不知道英雄联盟在移动端的未来。今天我们采用多线程的方式爬取LOL官网的英雄高清壁纸。.页面分析目标网站:英雄联盟官网界面如图。小图代表英雄,可见一斑。我们的目的是爬取每个英雄的所有皮肤图片,全部下载下来保存到本地。二级页面之上的页面称为主页面。二级页面是每个英雄对应的页面。以黑暗女士为例。它的二级页面如下:我们可以看到有很多小图,每个小图对应一个皮肤,通过网络查看皮肤数据界面,如下图:我们知道皮肤信息是以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个了。...可以看出它有一定的变化规律,并没有按顺序加一个,所以要爬取所有的英雄皮肤图片,需要先获取所有的heroIds。为什么爬取想法要用多线程?让我在这里解释一下。我们在爬取图片、视频等数据的时候,因为需要保存在本地,所以会用到大量的文件读写操作,也就是IO操作。想象一下,如果我们执行同步请求操作;那么第二个请求直到第一个请求完成后才会进行,直到文件保存到本地,那么这样是非常低效的。如果采用多线程异步操作,效率会大大提高。所以需要用到多线程或者多进程,然后把那么多数据队列丢到线程池或者进程池去处理;在Python中,multiprocessingPool进程池,multiprocessing.dummy非常好用。multiprocessing.dummy模块:虚拟模块是多线程的;multiprocessing模块:multiprocessing就是多进程;multiprocessing.dummy模块和multiprocessing模块的api通用,代码切换更灵活;我们先用一个测试demo.py文件抓取英雄id。这里我已经写好了代码,获取了存储的英雄id列表,直接在主文件中使用即可;demo.pyurl='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英雄信息i??dList=[]forheroinheroes:hero_id=hero["heroId"]idList.append(hero_id)print(idList)得到idList如下:idlist=[1,2,3,….,875,876,877]#中间的英雄id不显示这里建好的url:page='http://www.bizhi88.com/s/470/{}.html'.format(i)这里的i代表id,动态构建url;然后我们自定义两个函数,一个用于爬取解析页面(spider),一个用于下载数据(download),开启线程池,使用for循环构建英雄皮肤并存放json,数据的url存放在列表作为一个url队列,使用pool.map()方法执行蜘蛛(爬虫)功能;defmap(self,fn,*iterables,timeout=None,chunksize=1):"""返回一个相当于map(fn,iter)的迭代器”""#这里我们使用:pool.map(spider,page)#spider:爬虫功能;page:url队列函数:提取列表中的每个元素作为函数的参数,创建进程,放入进程池;参数1:要执行的函数;参数2:迭代器,将迭代器中的数字作为参数依次传递给函数;json数据分析这里我们将使用暗黑女郎皮肤的json文件来进行展示和分析,我们需要获取的内容是:1.name2.skin_name3.mainImg因为我们发现heroName是一样的,所以我们使用英雄名称作为英雄皮肤文件夹名称,方便查看和保存;item={}item['name']=hero["heroName"]item['skin_name']=hero["name"]ifhero["mainImg"]=='':continueitem['imgLink']=hero["mainImg"]有一点注意:有些mainImg标签是空的,需要跳过,否则如果是空链接,请求时会报错;数据采集??导入相关第三方库importrequests#Requestfrommultiprocessing.dummyimportPoolasThreadPool#并发导入时间#效率importos#文件操作importjson#解析页面数据解析defspider(url):res=requests.get(url,headers=headers)result=res.content.decode('utf-8')res_dict=json.loads(result)skins=res_dict["skins"]#15英雄信息print(len(skins))forindex,heroinenumerate(skins):#这里使用enumerate获取下标,这样就可以给文件图片命名了;item={}#字典对象item['name']=hero["heroName"]item['skin_name']=hero["name"]ifhero["mainImg"]=='':continueitem['imgLink']=hero["mainImg"]print(item)下载load(index+1,item)download下载图片defdownload(index,contdict):name=contdict['name']path="skin/"+nameifnotos.path.exists(path):os.makedirs(path)content=requests.get(contdict['imgLink'],headers=headers).content与open('./skin/'+name+'/'+contdict['skin_name']+str(index)+'.jpg','wb')asf:f.write(content)这里我们使用OS模块创建文件夹。前面我们提到,每个英雄的heroName值都是一样的,所以创建一个文件夹并命名。方便保存(分类)皮肤,然后这里图片文件的路径需要注意,少了一个斜线会报错main()主函数defmain():pool=ThreadPool(6)page=[]foriinrange(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函数中,我们首先创建了6个线程池;通过for循环动态构建20个url,试试看,20个英雄皮肤,如果全部爬取,可以遍历前面的idList,然后动态构建url;使用map()函数在线程池中对url进行数据分析和存储操作;线程池关闭时,线程池并没有关闭,而是状态变为不能再插入元素的状态;程序运行if__name__=='__main__':main()的结果如下:当然,这里只抓取了部分图片,一共爬取了200+张图片,总体可以接受。总结这次我们使用多线程的方式爬取了英雄联盟官网的英雄皮肤高清壁纸。因为图片涉及到IO操作,所以我们采用了并发的方式,大大提高了程序的执行效率。当然,爬行动物只有一点点味道。本次我们爬取了20位英雄的皮肤图片。有兴趣的朋友可以爬取所有的皮肤,只需要将遍历元素改成之前的idlist即可。以上就是本次分享的全部内容。想了解更多Python知识,请前往公众号:Python编程学习圈,每日干货分享,送“J”,海量学习资料。