python 爬虫多线程、多进程、协程批量下载图片优质

3次浏览 | 2024-09-20 19:31:19 更新
来源 :互联网
最佳经验

简要回答

大家喜欢换壁纸,但是找起来真的挺麻烦。之前说了简单点的单线程,接下来咱们聊聊多线程和异步下载的事儿,就看看到底遇到什么问题了,怎么解决,还有怎么能快速找到好看的壁纸?

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')

本文地址:https://www.pkoykq.cn/9691.html

发布于 2024-09-20 19:31:19
收藏
分享
海报
3
上一篇:男友洗澡看 A 片被抓包,主角竟是 23 岁女儿,这是怎么回事? 已经没有更多啦

0 条评论

请文明发言哦~

忘记密码?

图形验证码