美文网首页程序员
python从yield到asyncio<第四章>

python从yield到asyncio<第四章>

作者: lpj24 | 来源:发表于2017-09-26 16:40 被阅读203次

    通过前三章讲述了python中yield到asyncio的历程, 今天再通过一个http下载的例子加深一下影响, 同时为后面的非阻塞做一个铺垫

    show me code (也可参考流畅的python 18-5例子)
    # -*- coding: utf-8 -*-
    
    # 读取当当网的图书
    import requests
    import aiohttp
    import asyncio
    from bs4 import BeautifulSoup
    import time
    import os
    from concurrent.futures import ThreadPoolExecutor
    # 子生成器
    @asyncio.coroutine
    def get_image(img_url):
        yield from asyncio.sleep(1)
        resp = yield from aiohttp.request('GET', img_url)
        image = yield from resp.read()
        return image
    
    
    def save_image(img, img_url):
        with open(os.path.join('./img_file', img_url.split('/')[-1]), 'wb') as f:
            f.write(img)
    
    
    @asyncio.coroutine
    def download_one(img_url):
        image = yield from get_image(img_url)
        save_image(image, img_url)
    
    if __name__ == '__main__':
        images_list = [
            'http://img3m0.ddimg.cn/67/4/24003310-1_b_5.jpg'
            'http://img3m2.ddimg.cn/43/13/23958142-1_b_12.jpg',
            'http://img3m0.ddimg.cn/60/17/24042210-1_b_5.jpg',
            'http://img3m4.ddimg.cn/20/11/23473514-1_b_5.jpg',
            'http://img3m4.ddimg.cn/40/14/22783504-1_b_1.jpg',
            'http://img3m7.ddimg.cn/43/25/23254747-1_b_3.jpg',
            'http://img3m9.ddimg.cn/30/36/23368089-1_b_2.jpg',
            'http://img3m1.ddimg.cn/77/14/23259731-1_b_0.jpg',
            'http://img3m2.ddimg.cn/33/18/23321562-1_b_21.jpg',
            'http://img3m3.ddimg.cn/2/21/22628333-1_b_2.jpg',
            'http://img3m8.ddimg.cn/85/30/23961748-1_b_10.jpg',
            'http://img3m1.ddimg.cn/90/34/22880871-1_b_3.jpg',
            'http://img3m2.ddimg.cn/62/27/23964002-1_b_6.jpg',
            'http://img3m5.ddimg.cn/84/16/24188655-1_b_3.jpg',
            'http://img3m6.ddimg.cn/46/1/24144166-1_b_23081.jpg',
            'http://img3m9.ddimg.cn/79/8/8766529-1_b_0.jpg']
        start = time.time()
        loop = asyncio.get_event_loop()
        to_do_tasks = [download_one(img) for img in images_list]
    
        res, _ = loop.run_until_complete(asyncio.wait(to_do_tasks))
        print(len(res))
        print(time.time() - start)
    
    代码解读
    1. 对书本中的例子进行了改动, 创建一个images_list, 里面存储的是当当网一个页面所有书籍的图片地址, 我们要把这些图片下载下来存到本地, 大家可以根据以下代码获取图片地址
    # -*- coding: utf-8 -*-
    # 需要安装requests和BeautifulSoup库
    import requests
    from bs4 import BeautifulSoup
    def get_dangdang():
        img_list = []
        url = 'http://category.dangdang.com/pg' + str(1) + '-cp01.54.06.00.00.00.html'
        result = requests.get(url)
        html = result.text
        soup = BeautifulSoup(html, "html.parser")
        source = soup.find_all('a', attrs={"class": "pic"})
    
        for index, text in enumerate(source):
            if index == 0:
                img_list.append(text.find('img').get('src'))
            else:
                img_list.append(text.find('img').get('data-original'))
        print(img_list)
        print(len(img_list))
    
    1. 对于main函数的中的代码如果看过前面几张不会陌生, 同样的事件循环, 这里要提一下书中提到的期物, 协程, 任务, 粗略的理解, 协程asyncio.wait()把协程函数转换成任务添加到事件循环, 任务完成之后返回future对象, future提供了获取对象信息的api, 大致这样吧(线程的高度抽象接口concurrent.futures也是返回的future对象)。

    3.创建子生成器, get_image()

    • sleep()一下是因为, 图片太小, 速度太快, 这里模拟一下耗时的请求
    • aiohttp.request()请求会有Unclosed client session的警告, 参见https://github.com/aio-libs/aiohttp/issues/2036, 建议使用
      with aiohttp.ClientSession上下文的方式自动关闭, 这里不影响结果我们就按书上的来做
    • yield from resp.read() 非阻塞式读取图片二进制数据返回给get_image函数的委派生成器

    4.sav_image保存图片到本地

    5.创建download_one协程用于下载每一张图片, 然后存储到本地

    6.终端返回执行结果耗时1.12s左右, 图片成功存储

    没有对比就没有伤害, 大家应该使用concurrent.futures多线程下载对比一下, 这里就不写代码了, 我这边测试多线程要慢一些

    上面的代码虽然比多线程要快一下,但是代码是有问题的, 有些环境下可能还不如多线程, 下一章继续讨论一下这个问题
    python从yield到asyncio<第五章>

    python从yield到asyncio<第一章>
    python从yield到asyncio<第二章>
    python从yield到asyncio<第三章>

    相关文章

      网友评论

        本文标题:python从yield到asyncio<第四章>

        本文链接:https://www.haomeiwen.com/subject/citsextx.html