python 爬虫多线程、多进程、协程批量下载图片优质
简要回答
大家喜欢换壁纸,但是找起来真的挺麻烦。之前说了简单点的单线程,接下来咱们聊聊多线程和异步下载的事儿,就看看到底遇到什么问题了,怎么解决,还有怎么能快速找到好看的壁纸?
import datetime
import re
import requests
from bs4 import BeautifulSoup
start = datetime.datetime.now()
j = 0
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36'}
def pic_re(url):
re = requests.get(url=url,headers=headers)
return re.text
def pic_download(soup):
list = soup.find(class_='contlistw mtw').find_all('li')
for item in list:
global j
pic_name = item.find('img')['alt']
pic_url = item.find('img')['lazysrc'].replace('.278.154.jpg','')
pic_type = re.sub(r'h.*\d+.','', pic_url)
#print(pic_name,pic_type,pic_url)
filename = '{}.{}'.format(pic_name, pic_type)
print('开始下载:'+filename)
with open(filename, 'wb') as f:
f.write(requests.get(pic_url,headers=headers).content)
print(filename+' 下载完成')
j += 1
def main():
for i in range(1,2):
url = 'https://desk.3gbizhi.com/deskMV/index_{}.html'.format(i)
html=pic_re(url)
soup=BeautifulSoup(html,'lxml')
pic_download(soup)
date_all = (datetime.datetime.now() - start).total_seconds()
print(f'总共{j}张图片,下载总用时:{date_all}s')
if __name__ == '__main__':
main()
开始下载:站在油菜花地的小清新美女背影.jpg
站在油菜花地的小清新美女背影.jpg 下载完成
开始下载:穿大花袖子连衣裙的印度美女近照摄影.jpg
穿大花袖子连衣裙的印度美女近照摄影.jpg 下载完成
总共24张图片,下载总用时:485.885113s
进程已结束,退出代码0
一、单线程执行的局限性
pic_list = []
def get_pic_list(soup):
list = soup.find(class_='contlistw mtw').find_all('li')
for item in list:
global j
pic_name = item.find('img')['alt']
pic_url = item.find('img')['lazysrc'].replace('.278.154.jpg','')
pic_type = re.sub(r'h.*\d+.','', pic_url)
filename = 'pic\{}.{}'.format(pic_name, pic_type)
pic_list.append([filename,pic_url])
呢个单线程爬网页图片慢得要死特别是遇到一堆图的情况下,就像爬山一样费劲。举个例子来说,想爬个墙纸网站,可能得慢慢等到天黑才能看到结果。这也太慢了,网络差的时候简直让人抓狂。而且这个单线程还只能做一件事,比如一张图片下载失败,整项任务都得歇菜。
def image_down(filename,image_url):
re = requests.get(image_url,headers=headers)
print('开始下载:' + filename)
with open(filename, 'wb') as f:
f.write(re.content)
print(filename + ' 下载完成')
def thread_down():
t_list = []
for url in pic_list:
global j
t = threading.Thread(target=image_down,
kwargs={'filename': url[0], 'image_url': url[1]})
t_list.append(t)
t.start()
j += 1
for t in t_list:
t.join()
单线程弄起来简单极了,就是没啥并发功能,有些图可能下载不全,甚至什么都看不到!看到那些只剩一边黑乎乎的图片,真的好郁闷!想快点解决这个问题的话,咱们就得想想办法,让壁纸下载速度快些了。
import datetime
import re
import requests
from bs4 import BeautifulSoup
import threading
start = datetime.datetime.now()
j = 0
pic_list = []
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36'}
def pic_re(url):
re = requests.get(url=url,headers=headers)
return re.text
def get_pic_list(soup):
list = soup.find(class_='contlistw mtw').find_all('li')
for item in list:
global j
pic_name = item.find('img')['alt']
pic_url = item.find('img')['lazysrc'].replace('.278.154.jpg','')
pic_type = re.sub(r'h.*\d+.','', pic_url)
filename = 'pic\{}.{}'.format(pic_name, pic_type)
pic_list.append([filename,pic_url])
def image_down(filename,image_url):
re = requests.get(image_url,headers=headers)
print('开始下载:' + filename)
with open(filename, 'wb') as f:
f.write(re.content)
print(filename + ' 下载完成')
def thread_down():
t_list = []
for url in pic_list:
global j
t = threading.Thread(target=image_down,
kwargs={'filename': url[0], 'image_url': url[1]})
t_list.append(t)
t.start()
j += 1
for t in t_list:
t.join()
def main():
for i in range(1,24):
url = 'https://desk.3gbizhi.com/deskMV/index_{}.html'.format(i)
html=pic_re(url)
soup=BeautifulSoup(html,'lxml')
get_pic_list(soup)
thread_down()
date_all = (datetime.datetime.now() - start).total_seconds()
print(f'总共{j}张图片,下载总用时:{date_all}s')
if __name__ == '__main__':
main()
二、多线程下载的优势
开始下载:pic\超清4K长发少女,高清到毛孔都看的见强烈推荐.png
pic\超清4K长发少女,高清到毛孔都看的见强烈推荐.png 下载完成
开始下载:pic\超高清长发清纯学生妹街拍电脑背景.jpg
pic\超高清长发清纯学生妹街拍电脑背景.jpg 下载完成
开始下载:pic\图书馆的气质少女高清头像壁纸图片-真8K壁纸推荐.png
pic\图书馆的气质少女高清头像壁纸图片-真8K壁纸推荐.png 下载完成
总共533张图片,下载总用时:182.8669s
进程已结束,退出代码0
现在可真是科技飞速发展!下载啥的大家都爱用多线程,这样办事儿速度就提上去了。比如说,你想要个漂亮的桌面壁纸,就让好几条线程一起出发,各自忙活,这样任务就能迅速搞定。不过,虽然多线程快,但有时候也会出点儿小状况,比如下载失败,结果图片显示不完全。
image = Image.open(BytesIO(re.content)
如果你在同时下好几张图片时出现“图片被剪裁”的情况,别急,这只是下得还不够完整而已。这时候,咱们就得用点异常捕捉绝活儿把问题解决掉,然后再试着重下那个打了折的图片。虽然现在下载速度快多了,但处理这些残次品还是挺麻烦的,真让人头疼。
三、多进程的高效执行
from PIL import Image
from io import BytesIO
def image_down(filename,image_url):
re = requests.get(image_url,headers=headers)
if re.status_code == 200:
try:
print('开始下载:' + filename)
image = Image.open(BytesIO(re.content))
image.save(filename)
print(filename + ' 下载完成')
except OSError:
count = 1
print('图片不完整,重新下载')
if count <= 5:
return image_down(filename, image_url)
else:
print(filename + ' 下载失败')
count += 1
else:
count = 1
print('网络错误,重新下载')
if count <= 5:
return image_down(filename,image_url)
else:
print(filename+' 下载失败')
count += 1
你肯定猜不到!多线程和多进程都能提升爬虫的速度。多进程就能真正把电脑多核的优势利用上了,比如任务不多的时候,通过Process类分配任务,瞬间搞定!而且我们还可以开好几个子程序,这样就能一边下一边看,速度嗖嗖地上去了!
说起Pool这个程序设计里的绝佳助手,你得说好在它有俩实用办法——apply_async和map。如果你想任务快狠准,那就选apply_async,有点难以控制,不过速度可观;想要安全稳定,那就用map,等所有任务完成后再给出结果。这两个方法各有千秋,看你需要哪类任务。亲测过之后,我发现apply_async的速度实际上和Process子进程并行起来差不多,真是让人惊喜!
p = Process(target=run_proc, args=('test',))
p.start()
p.join()
四>、异步下载的创新
aiohttp和requests就像是两个编程大咖,前者厉害之处在于,你在下载文件时也可以做其他事情,不干等!想要编写一个能暂停的异步函数?别忘了加上async关键字哟~
from multiprocessing import Process
def multiprocess_down():
p_list = []
for url in pic_list:
global j
p = Process(target=image_down,
kwargs={'filename': url[0], 'image_url': url[1]})
p_list.append(p)
p.start()
j += 1
for p in p_list:
p.join()
...
...
def main():
for i in range(1,3):
url = 'https://desk.3gbizhi.com/deskMV/index_{}.html'.format(i)
html=pic_re(url)
soup=BeautifulSoup(html,'lxml')
get_pic_list(soup)
#thread_down()
multiprocess_down()
date_all = (datetime.datetime.now() - start).total_seconds()
print(f'总共{j}张图片,下载总用时:{date_all}s')
原来asyncio.run()老是搞不定,我就换个招式试了一把,结果发现asyncio.get_event_loop().run_until_complete(main())效果不错,速度快了不少!不过大图片下载就像多线程那样,有时候会显示不全,这时候就要看看Content-Length这个值再决定要不要继续下载。
开始下载:pic\LED大屏幕前的欧美美女超清桌面壁纸下载.jpg
pic\LED大屏幕前的欧美美女超清桌面壁纸下载.jpg 下载完成
开始下载:pic\手捧窗帘的欧美时尚模特高清壁纸.jpg
pic\手捧窗帘的欧美时尚模特高清壁纸.jpg 下载完成
开始下载:pic\站在油菜花地的小清新美女背影.jpg
pic\站在油菜花地的小清新美女背影.jpg 下载完成
总共48张图片,下载总用时:172.714426s
进程已结束,退出代码0
五、解决下载不全的问题
老实说,我试过好几次下图片,老是发现Content-Length和实际文件大小对不上,心里挺别扭的。不过,咱们可不能因为这点儿小问题就泄气!后来我想到了一招,那就是分段下载。装上aiofiles这个软件后,设置好每次下载的大小,然后一点点往里加,结果还真行得通!
这次下载真是让我深有体会,每种下载方法都有优缺点,得根据实际情况来调整才行。虽然有点累人,但看到那些赏心悦目的壁纸图片,心情瞬间就好了许多!
from multiprocessing import Pool
def multipoll_down():
p = Pool(multiprocessing.cpu_count())
for url in pic_list:
global j
p.apply_async(image_down,[url[0],url[1]])
#p.starmap(image_down, [(url[0], url[1]),])
j += 1
p.close()
p.join()
def main():
for i in range(1, 3):
url = 'https://desk.3gbizhi.com/deskMV/index_{}.html'.format(i)
html = pic_re(url)
soup = BeautifulSoup(html, 'lxml')
get_pic_list(soup)
# thread_down()
# multiprocess_down()
multipoll_down()
date_all = (datetime.datetime.now() - start).total_seconds()
print(f'总共{j}张图片,下载总用时:{date_all}s')
推荐阅读
0 条评论
最新文章
-
python 爬虫多线程、多进程、协程批量下载图片2024-09-20 19:31:19
-
男友洗澡看 A 片被抓包,主角竟是 23 岁女儿,这是怎么回事?2024-09-20 16:39:16
-
AV 对电影的影响:没有 AV,你们还会看这种电影吗?2024-09-20 16:10:15
-
个子矮的女生适合穿什么衣服?娇小女神们的视觉增高术2024-09-20 14:41:46
-
91女神娇喘3p第三届滨海国际(微)电影节参赛影片2024-09-20 14:36:41