城市直播房产教育博客汽车
投稿投诉
汽车报价
买车新车
博客专栏
专题精品
教育留学
高考读书
房产家居
彩票视频
直播黑猫
投资微博
城市上海
政务旅游

python爬虫之多线程threading多进程协程aioh

3月15日 生死族投稿
  一、单线程常规下载
  常规单线程执行脚本爬取壁纸图片,只爬取一页的图片。importdatetimeimportreimportrequestsfrombs4importBeautifulSoupstartdatetime。datetime。now()j0headers{UserAgent:Mozilla5。0(WindowsNT10。0;Win64;x64)AppleWebKit537。36(KHTML,likeGecko)Chrome106。0。0。0Safari537。36}defpicre(url):rerequests。get(urlurl,headersheaders)returnre。textdefpicdownload(soup):listsoup。find(classcontlistwmtw)。findall(li)foriteminlist:globaljpicnameitem。find(img)〔alt〕picurlitem。find(img)〔lazysrc〕。replace(。278。154。jpg,)pictypere。sub(rh。d。,,picurl)print(picname,pictype,picurl)filename{}。{}。format(picname,pictype)print(开始下载:filename)withopen(filename,wb)asf:f。write(requests。get(picurl,headersheaders)。content)print(filename下载完成)j1defmain():foriinrange(1,2):urlhttps:desk。3gbizhi。comdeskMVindex{}。html。format(i)htmlpicre(url)soupBeautifulSoup(html,lxml)picdownload(soup)dateall(datetime。datetime。now()start)。totalseconds()print(f总共{j}张图片,下载总用时:{dateall}s)ifnamemain:main()
  执行结果:开始下载:站在油菜花地的小清新美女背影。jpg站在油菜花地的小清新美女背影。jpg下载完成开始下载:穿大花袖子连衣裙的印度美女近照摄影。jpg穿大花袖子连衣裙的印度美女近照摄影。jpg下载完成总共24张图片,下载总用时:485。885113s进程已结束,退出代码0
  结果,第一页24张图片,就下载差不多8分钟,排除网络等因素,还没有手动下载快。二、多线程下载
  上面的有两个循环,第一个是页面的循环,一页一页地加载,每页在单独循环单独下载图片。
  所以有两个等待时间,第一个就是等待第一页下载完成,才会到第二页。第二个等待就是每页图片一张下载完才下载第二张。
  综上,优化两点:
  第一点,第一步提取所有图片链接保存,不用一页等一页的提取。
  第二点,所有图片多线程同时下载,不用等一个一个下载。
  1:创建列表,储存图片信息
  只需要两个信息,图片名称,和图片链接,储存到piclist〔〕列表。piclist〔〕defgetpiclist(soup):listsoup。find(classcontlistwmtw)。findall(li)foriteminlist:globaljpicnameitem。find(img)〔alt〕picurlitem。find(img)〔lazysrc〕。replace(。278。154。jpg,)pictypere。sub(rh。d。,,picurl)filenamepic{}。{}。format(picname,pictype)piclist。append(〔filename,picurl〕)
  2:读取列表,多线程同时下载
  threading说明:创建空列表tlist,将三个子线程放入该列表,用于执行join,执行子线程(start),start方法开启一个新线程。把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用run()方法。执行阻塞(join),join函数可以理解为,如果某个子进程执行了join函数,那么在该子进程执行到join之前,父进程都会等待。
  threading。Thread命令参数:
  第一个为参数为函数,第二次参数为函数值。使用args传递参数threading。Thread(targettarget,args(10,100,100)),args参数为元组,所以只有一个参数,以,结尾,例:args(10,)使用kwargs传递参数threading。Thread(targettarget,kwargs{a:10,b:100,c:100})同时使用args和kwargs传递参数threading。Thread(targettarget,args(10,),kwargs{b:100,c:100})
  分别创建下载函数,和多线程函数。
  代码如下:defimagedown(filename,imageurl):rerequests。get(imageurl,headersheaders)print(开始下载:filename)withopen(filename,wb)asf:f。write(re。content)print(filename下载完成)defthreaddown():tlist〔〕forurlinpiclist:globaljtthreading。Thread(targetimagedown,kwargs{filename:url〔0〕,imageurl:url〔1〕})tlist。append(t)t。start()j1fortintlist:t。join()
  最终代码为:importdatetimeimportreimportrequestsfrombs4importBeautifulSoupimportthreadingstartdatetime。datetime。now()j0piclist〔〕headers{UserAgent:Mozilla5。0(WindowsNT10。0;Win64;x64)AppleWebKit537。36(KHTML,likeGecko)Chrome106。0。0。0Safari537。36}defpicre(url):rerequests。get(urlurl,headersheaders)returnre。textdefgetpiclist(soup):listsoup。find(classcontlistwmtw)。findall(li)foriteminlist:globaljpicnameitem。find(img)〔alt〕picurlitem。find(img)〔lazysrc〕。replace(。278。154。jpg,)pictypere。sub(rh。d。,,picurl)filenamepic{}。{}。format(picname,pictype)piclist。append(〔filename,picurl〕)defimagedown(filename,imageurl):rerequests。get(imageurl,headersheaders)print(开始下载:filename)withopen(filename,wb)asf:f。write(re。content)print(filename下载完成)defthreaddown():tlist〔〕forurlinpiclist:globaljtthreading。Thread(targetimagedown,kwargs{filename:url〔0〕,imageurl:url〔1〕})tlist。append(t)t。start()j1fortintlist:t。join()defmain():foriinrange(1,24):urlhttps:desk。3gbizhi。comdeskMVindex{}。html。format(i)htmlpicre(url)soupBeautifulSoup(html,lxml)getpiclist(soup)threaddown()dateall(datetime。datetime。now()start)。totalseconds()print(f总共{j}张图片,下载总用时:{dateall}s)ifnamemain:main()
  执行结果,533张图片,只用了差不多3分钟就下载完了开始下载:pic超清4K长发少女,高清到毛孔都看的见强烈推荐。pngpic超清4K长发少女,高清到毛孔都看的见强烈推荐。png下载完成开始下载:pic超高清长发清纯学生妹街拍电脑背景。jpgpic超高清长发清纯学生妹街拍电脑背景。jpg下载完成开始下载:pic图书馆的气质少女高清头像壁纸图片真8K壁纸推荐。pngpic图书馆的气质少女高清头像壁纸图片真8K壁纸推荐。png下载完成总共533张图片,下载总用时:182。8669s进程已结束,退出代码0
  三、图片不完整解决
  以上虽然速度上来了,但是查看图片有下载失败,如0kb,或者图片不完整,半截是灰色的。
  原因多半是网络原因,壁纸多半都是大尺寸,容量也大,图片1M到20M不等,经常会加载不全就下载下来或网络访问失败。
  对于网络失败的(0kb),让他返回重新访问。用re。statuscode200判断即可。
  对于下载不全的,用其他的方式下载。这里用imageImage。open(BytesIO(re。content)
  因为这种方法,如果图片下载不全,会报错异常OSError:imagefileistruncated(Xbytesnotprocessed),通过捕获异常,同样重新返回执行。
  为了防止因为网络原因,陷入死循环,设置返回次数,超过规定次数,则停止返回。
  改imagedown函数即可。fromPILimportImagefromioimportBytesIOdefimagedown(filename,imageurl):rerequests。get(imageurl,headersheaders)ifre。statuscode200:try:print(开始下载:filename)imageImage。open(BytesIO(re。content))image。save(filename)print(filename下载完成)exceptOSError:count1print(图片不完整,重新下载)ifcount5:returnimagedown(filename,imageurl)else:print(filename下载失败)count1else:count1print(网络错误,重新下载)ifcount5:returnimagedown(filename,imageurl)else:print(filename下载失败)count1
  再次执行,图片全部下载完成,而且没有不全的图片了。四、多进程下载
  除了多线程之外,我们还可以使用多进程来提高爬虫速度。
  在Python中multiprocessing提供了两个用于多进程的类,即Process和Pool类multiprocessing。Process无法批量开启子进程,可以直接用multiprocesssing。Queue等进行通信multiprocessing。Pool可以批量开启子进程,不能直接用multiprocessing。Queue进行通信,只能通过共享内存,或者用multiprocessing。Manager()进行进程间通信。
  Pool仅在内存中分配正在执行的进程,而Process在内存中分配所有任务,因此,当任务数较小时,我们可以使用Process类;当任务数较大时,我们可以使用Pool。
  1、Process(用于创建进程)
  multiprocessing模块提供了一个Process类来代表一个进程对象。
  在multiprocessing中,每一个进程都用一个Process类来表示。
  构造方法:Process(〔group〔,target〔,name〔,args〔,kwargs〕〕〕〕〕)start():启动进程,并调用该子进程中的p。run()run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁isalive():返回进程是否在运行。如果p仍然运行,返回Truejoin(〔timeout〕):进程同步,主进程等待子进程完成后再执行后面的代码。线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间(超过这个时间,父线程不再等待子线程,继续往下执行),需要强调的是,p。join只能join住start开启的进程,而不能join住run开启的进程
  使用方法和多线程threading。Thread的使用方法差不多。pProcess(targetrunproc,args(test,))p。start()p。join()
  如,前面的代码,增加defmultiprocessdown()类,同样,同时创建多个子进程,加入列表,子进程并发跑。frommultiprocessingimportProcessdefmultiprocessdown():plist〔〕forurlinpiclist:globaljpProcess(targetimagedown,kwargs{filename:url〔0〕,imageurl:url〔1〕})plist。append(p)p。start()j1forpinplist:p。join()。。。。。。defmain():foriinrange(1,3):urlhttps:desk。3gbizhi。comdeskMVindex{}。html。format(i)htmlpicre(url)soupBeautifulSoup(html,lxml)getpiclist(soup)threaddown()multiprocessdown()dateall(datetime。datetime。now()start)。totalseconds()print(f总共{j}张图片,下载总用时:{dateall}s)
  执行结果开始下载:picLED大屏幕前的欧美美女超清桌面壁纸下载。jpgpicLED大屏幕前的欧美美女超清桌面壁纸下载。jpg下载完成开始下载:pic手捧窗帘的欧美时尚模特高清壁纸。jpgpic手捧窗帘的欧美时尚模特高清壁纸。jpg下载完成开始下载:pic站在油菜花地的小清新美女背影。jpgpic站在油菜花地的小清新美女背影。jpg下载完成总共48张图片,下载总用时:172。714426s进程已结束,退出代码0
  可以看出,这种多进程,显然没有多线程速度块,虽然也用了多个子进程同时跑,速度提升还是没有多线程速度快。
  2、Pool(用于创建管理进程池)
  构造方法:Pool(〔processes〔,initializer〔,initargs〔,maxtasksperchild〔,context〕〕〕〕〕)processes:要创建的进程数,如果省略,将默认使用cpucount()返回的数量。initializer:每个工作进程启动时要执行的可调用对象,默认为None。如果initializer是None,那么每一个工作进程在开始的时候会调用initializer(initargs)。initargs:是要传给initializer的参数组。maxtasksperchild:工作进程退出之前可以完成的任务数,完成后用一个新的工作进程来替代原进程,来让闲置的资源被释放。maxtasksperchild默认是None,意味着只要Pool存在工作进程就会一直存活。context:用在制定工作进程启动时的上下文,一般使用Pool()或者一个context对象的Pool()方法来创建一个池,两种方法都适当的设置了context。
  方法:apply(),
  函数原型:apply(func〔,args()〔,kwds{}〕〕)
  该函数用于传递不定参数,同python中的apply函数一致,主进程会被阻塞直到函数执行结束(不建议使用,并且3。x以后不再出现)applyasync
  函数原型:applyasync(func〔,args()〔,kwds{}〔,callbackNone〕〕〕)
  与apply用法一致,但它是非阻塞的且支持结果返回后进行回调map()
  函数原型:map(func,iterable〔,chunksizeNone〕)
  Pool类中的map方法,与内置的map函数用法行为基本一致,它会使进程阻塞直到结果返回
  注意:虽然第二个参数是一个迭代器,但在实际使用中,必须在整个队列都就绪后,程序才会运行子进程mapasync()
  函数原型:mapasync(func,iterable〔,chunksize〔,callback〕〕)
  与map用法一致,但是它是非阻塞的close()
  关闭进程池(pool),使其不再接受新的任务terminal()
  结束工作进程,不再处理未处理的任务join()
  主进程阻塞等待子进程的退出,join方法要在close或terminate之后使用
  Pool。applyasync:异步执行,结果的顺序不能保证与调用的顺序相同
  Pool。map:同步执行,阻塞直到返回完整的结果,
  map和mapasync一次调用一个作业列表,但是apply和applyasync只能调用一个作业。但是,applyasync是在后台并行执行作业的。
  我这里用applyasync,也可以用map,但是map正常情况只能传一个参数,要用map传多个参数,用starmap即可。frommultiprocessingimportPooldefmultipolldown():pPool(multiprocessing。cpucount())forurlinpiclist:globaljp。applyasync(imagedown,〔url〔0〕,url〔1〕〕)p。starmap(imagedown,〔(url〔0〕,url〔1〕),〕)j1p。close()p。join()defmain():foriinrange(1,3):urlhttps:desk。3gbizhi。comdeskMVindex{}。html。format(i)htmlpicre(url)soupBeautifulSoup(html,lxml)getpiclist(soup)threaddown()multiprocessdown()multipolldown()dateall(datetime。datetime。now()start)。totalseconds()print(f总共{j}张图片,下载总用时:{dateall}s)
  执行结果,速度和process子进程并行差不多(我电脑cpu最大8个进程而已)。五、协程下载
  1:aiohttp和asyncio库
  这里需要用到两个库aiohttp和asyncio。
  aiohttp:可以把这个库当作requests库的替代品,因为requets不支持异步默认,所以这里需要用aiohttp库替代requests库。
  asyncio:asyncio是用来编写并发代码的库,使用asyncawait语法。
  正常的函数在执行时是不会中断的,所以你要写一个能够中断的函数,就需要添加async关键。
  async用来声明一个函数为异步函数,异步函数的特点是能在函数执行过程中挂起,去执行其他异步函数,等到挂起条件(假设挂起条件是sleep(5))消失后,也就是5秒到了再回来执行。asyncio基本流程声明协程函数,函数前加async:asyncdeffunction():创建任务:asyncio。createtask(coro,,nameNone)两种执行任务,简单等待asyncio。wait和并发执行asyncio。gathercoroutineasyncio。wait(aws,,timeoutNone,returnwhenALLCOMPLETED)awaitableasyncio。gather(aws,returnexceptionsFalse)运行asyncio程序:asyncio。run(coro,,debugFalse)Python3。7及以后,不需要显式声明事件循环,可以使用asyncio。run(main())来代替最后的启动操作asyncio。geteventloop()。rununtilcomplete(main())importaiohttpimportasyncioasyncdefimagesdown(filename,imageurl):asyncwithaiohttp。ClientSession()assession:asyncwithsession。get(imageurl,headersheaders)asresp:print(filename,resp。status)ifresp。status200:print(开始下载:filename)withopen(filename,wb)asf:这个地方通过对流的处理,而不是一下子整体读取。一下子整个的读取,会导致批量下载图片的时候,一开始会出现资源浪费,只下载几张图片whileTrue:chunkawaitresp。content。read(1024)ifnotchunk:breakf。write(chunk)print(下载完成:filename)else:print(网络错误,重新下载:filename)returnawaitimagesdown(filename,imageurl)asyncdefaiohttpdown():forurlinpiclist:globaljtask〔asyncio。createtask(imagesdown(url〔0〕,url〔1〕))〕j1awaitasyncio。wait(task)awaitasyncio。gather(task)defmain():foriinrange(1,3):urlhttps:desk。3gbizhi。comdeskMVindex{}。html。format(i)htmlpicre(url)soupBeautifulSoup(html,lxml)getpiclist(soup)loopasyncio。geteventloop()loop。rununtilcomplete(aiohttpdown())asyncio。run(aiohttpdown())dateall(datetime。datetime。now()start)。totalseconds()print(f总共{j}张图片,下载总用时:{dateall}s)
  执行结果:下载完成:pic鸽子飞过欧美性感深V装欧美美女高清壁纸。jpg下载完成:pic穿黄色衣服在路边黄花的摄影电脑壁纸。jpg下载完成:pic棕色色调穿朝鲜传统服饰的韩国高颜值美女。jpg下载完成:pic欧美古城街道摄影的欧美美女。jpg下载完成:pic沙滩上捧着花散步的长发美女电脑壁纸。jpg总共48张图片,下载总用时:18。869591s进程已结束,退出代码0
  48张,只用了18秒左右,协程速度最快。
  2:遇到的问题
  问题一:RuntimeError:Eventloopisclosed。
  asyncio。run()执行报错,所以改成asyncio。geteventloop()。rununtilcomplete(main()),就不报错了,有趣的是,官方是推荐使用asyncio。run()的方法。
  问题二:图片下载速度快,但是大尺寸图片下载不完整。
  跟多线程遇到的问题一致,1M到20M的图片,显示不完全,小的图片则正常。
  尝试解决方法1:
  跟多线程一致的方法,或者判断ContentLength值返回循环下载fos。path。getsize(filename)!int(resp。headers〔ContentLength〕):returnawaitimagesdown(filename,imageurl)
  结果:直接陷入死循环,发现所有下载图片基本不可能和ContentLength一致,即使下载完整。
  尝试解决方法2:
  用块的方式,导入aiofiles包,通过设置块(content。iterchunked)的值尝试循环写入。asyncdefasynchttpdownload(filename,imageurl):asyncwithaiofiles。open(filename,wb)asfd:asyncwithaiohttp。ClientSession()assession:asyncwithsession。get(imageurl)asresp:asyncforchunkinresp。content。iterchunked(1024):awaitfd。write(chunk)
  结果,虽然不完整的减少,但是大尺寸的图,还是有,解决未果。
  用aiohttp虽然代替requests库使用,估计功能还是没有requests库完整及全面,等后续在找解决方法。六、总结
  对于多任务爬虫来说,多线程、多进程、协程这几种方式处理效率的排序为:aiohttp协程多线程多进程。
  但是aiohttp协程难度有点复杂,需要了解,而且本人目前没有解决协程下载大尺寸图片不完整的情况,还需要后续继续学习。
投诉 评论 转载

骑行在路上夏日若是骑行,须在那天明时分。此时最是惬意。和着清风前行,随心所欲哼着歌曲,或流行,或怀旧一些。有时我会听到风在耳边唱歌,信口胡诌句是谁在云里寄相思。然后,天马行空地想象着一段……10朵00后小花就位,谁能成为新版国民妹妹?各有各的门道张子枫疑似恋上软饭渣男的消息一曝光,全民迷惘:有人急得像热锅上的蚂蚁、有人大吼快点逃跑但是无论此事真假,那都是她自己的事毕竟就算是在我们眼皮子底下长大的张子枫,其实……经常有这5个行为,肾脏容易受伤,别等拖到尿毒症肾脏受损大意不得,需了解疾病情况后对症调理,否则有疾病的情况下不断发展,有可能会引发肾衰竭,尿毒症。另外,许多错误的行为会给肾脏带来沉重的负担,如果自己也有,需及时纠正,……睡觉时突然腿抽筋,缺钙导致的?错!这2个原因,别轻易忽视58岁大伯平时注意养生,身体挺好的,但他最近晚上睡觉经常腿抽筋,这令他感到身心俱疲。他担心可能是自己缺钙了,于是他去医院检查,结果却让他大跌眼镜,这跟缺钙毫无关系!夜间腿……曾为虹桥修禊发起人的私园,扬州冶春园景观全景显现,成为文化休始建于清代中期的冶春园是古城扬州一处风雅所在,也是众多散居天南地北扬州人的乡愁。紫牛新闻记者1月17日在扬州北护城河冶春区域采访时看到,北护城河冶春南岸改造提升工程主体完工后,……老游新玩ThisWarofMine这是我的战争游戏体验无意间看到这款由波兰的游戏开发商11BitStudios制作的3d横版战争生存策略游戏,浅谈一下小编的游戏体验玩家需要在战争期间搜集物资、武器、制造工具、酿酒或种植作物,……幼儿园受伤,谁之责?学龄前儿童在幼儿园受伤谁来承担责任这是幼儿园和家长共同关注的问题希望今天的案例能让幼儿园和家长引以为戒多一份警惕多一份保护……安东尼前妻晒照,看不出43岁!风姿依旧,穿着越来越大胆1月2日,NBA球星卡梅隆安东尼的前妻拉拉瓦斯奎兹(LaLa),她在个人社交媒体上晒出了一组美照,其中包括她与几个友人欢聚的画面。不难看出,43岁的拉拉一点都不显老,风姿依旧,……进入冬月,3类伤骨菜尽量少吃,提醒预防骨松,多做这3事对于大多数中老年人来说,进入冬季以后,身体机能会迅速下降。除了要担心三高问题发生外,还要一个疾病也会危害中老年人的身心健康,就是骨质疏松症。而说起骨质疏松,很多人首先会想……卡塔尔接纳了世界各国的140万游客后,要求中国旅客出示核酸报2022年世界杯持续一共持续了28天,作为本届世界杯主办国的卡塔尔接待了140万左右的游客,这些游客来自世界各大洲。事后经过统计,本届世界杯的现场观众总人数高达340万,……python爬虫之多线程threading多进程协程aioh一、单线程常规下载常规单线程执行脚本爬取壁纸图片,只爬取一页的图片。importdatetimeimportreimportrequestsfrombs4importBe……长安区香积寺三河口湿地公园东来的滈河和北来的谲河在香积寺南侧汇合了,从这里起往西边直到汇入沣河,这段河道叫洨河。政府投巨资建设了三河口公园,建设了多座造型各异的桥梁,栈道,亲水步道,还有各类造型的……
区块链论战再起,以太坊V神很感伤叫嚣让中国付出代价,戴琪却成功卡住了美国经济的咽喉索尼a7RV外观曝光,有望在本月下旬发布陌生暗恋者14真爱的发生你真的相信孩子吗?相信相信的力量(十一湖北篇)黄冈二日游推荐一个学习养生宜居的好地方祁雅丽创新引领京都薇薇用智慧探索品牌的新里程碑苏提达又换新风格?穿香槟金礼服裙亮相真美,受到独宠后真变美了火星电竞LPL3号种子争夺战!RNG再战EDG多喝水有哪些好处供销社升维出世,肩负着突围市场内卷困局的历史重任你是否会把坏事变好事?
职务侵占需要什么证据?落地2530万,定位中型SUV,陕西提车,都有哪些推荐呢?枣庄高速征地补偿标准2020是什么?商场购物有正式发票吗手上大量涂改液怎么洗2017最炫吊带裙俏皮与性感并存焦虑症的症状都有哪些美国人爱买什么电车?特斯拉杀疯了八达岭长城导游词东北现在是个巨大的贫困村赛博朋克2077性偶在哪赛博朋克2077性偶去不了玛丽黛佳的眉笔有哪些玛丽黛佳怎么换眉笔芯

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找江西南阳嘉兴昆明铜陵滨州广东西昌常德梅州兰州阳江运城金华广西萍乡大理重庆诸暨泉州安庆南充武汉辽宁