12-案例:并发下载器

目标

  • 能够使用协程实现网络图片下载

1. 并发下载多张图片

image-20180824231441470

2. 实现效果

yield_download

3. 核心方法

  • urllib.request.urlopen() —— 打开网址并返回对应的内容(二进制流)
# 导入urllib模块
import urllib.request
import gevent


def download_img(img_url, filename):
    try:
        # 打开url
        response = urllib.request.urlopen(img_url)
        # 创建文件
        with open(filename, "wb") as img_file:
            # 通过循环不断读取数据
            while True:
                # 将读取到的数据保存到变量中
                img_data = response.read(1024)
                # 如果读取成功,则写数据到文件中
                if img_data:
                    # 写数据
                    img_file.write(img_data)
                else:
                    break
    except Exception as e:
        print("文件 %s 下载失败! %s" % (file_name,str(e)))
    else:
        print("图片 %s 下载完成!" % filename)


def main():
    # 定义变量保存要下载的图片地   址
    img_url1 = "http://img.mp.itc.cn/upload/20170716/8e1b835f198242caa85034f6391bc27f.jpg"
    img_url2 = "http://img.mp.sohu.com/upload/20170529/d988a3d940ce40fa98ebb7fd9d822fe2.png"
    img_url3 = "http://image.uczzd.cn/11867042470350090334.gif?id=0&from=export"

    # 开启协程 调用下载方法
    gevent.joinall([
        gevent.spawn(download_img, img_url1, "1.gif"),
        gevent.spawn(download_img, img_url2, "2.gif"),
        gevent.spawn(download_img, img_url3, "3.gif")
    ])

# 主入口
if __name__ == '__main__':
    main()

运行结果

图片 1.gif 下载完成!
图片 2.gif 下载完成!
图片 3.gif 下载完成!

Process finished with exit code 0
通过图片下载的顺序可以看出,下载的顺序依然是顺序下载,并没有体现出多个协程并发下载,这个原因是因为,gevent并没有识别我们程序中的耗时操作,所以我们应该对 gevent 打补丁才可以

image-20180618170455718

4. 打补丁,识别耗时操作

# 导入模块
from gevent import monkey
# 调用patch_all() 方法
monkey.patch_all()

注意,尽可能把 from gevent import monkey 放到第一行

image-20180618171936812

运行结果:

 /home/teacher/PycharmProjects/day08/04-多线程下载图片.py:9: MonkeyPatchWarning: Monkey-patching ssl after ssl has already been imported may lead to errors, including RecursionError on Python 3.6. Please monkey-patch earlier. See https://github.com/gevent/gevent/issues/1016

  monkey.patch_all()

图片 2.gif 下载完成!

图片 3.gif 下载完成!

图片 1.gif 下载完成!

Process finished with exit code 0

我们可以看到,下载的顺序不再是1、2、3了,可能变成无序了,主要原因就是开启协程后多个协程同时工作!注意,此处可能会报报一个警告,不影响程序运行

警告的问题见:https://github.com/gevent/gevent/issues/1016

5. 并发下载视频

from gevent import monkey
import gevent
import urllib.request

#有IO才做时需要这一句
monkey.patch_all()

def my_downLoad(file_name, url):
    print('GET: %s' % url)
    resp = urllib.request.urlopen(url)
    data = resp.read()

    with open(file_name, "wb") as f:
        f.write(data)

    print('%d bytes received from %s.' % (len(data), url))

gevent.joinall([
        gevent.spawn(my_downLoad, "1.mp4", 'http://oo52bgdsl.bkt.clouddn.com/05day-08-%E3%80%90%E7%90%86%E8%A7%A3%E3%80%91%E5%87%BD%E6%95%B0%E4%BD%BF%E7%94%A8%E6%80%BB%E7%BB%93%EF%BC%88%E4%B8%80%EF%BC%89.mp4'),
        gevent.spawn(my_downLoad, "2.mp4", 'http://oo52bgdsl.bkt.clouddn.com/05day-03-%E3%80%90%E6%8E%8C%E6%8F%A1%E3%80%91%E6%97%A0%E5%8F%82%E6%95%B0%E6%97%A0%E8%BF%94%E5%9B%9E%E5%80%BC%E5%87%BD%E6%95%B0%E7%9A%84%E5%AE%9A%E4%B9%89%E3%80%81%E8%B0%83%E7%94%A8%28%E4%B8%8B%29.mp4'),
])

上面的url可以换为自己需要下载视频、音乐、图片等网址