资讯专栏INFORMATION COLUMN

流畅的python示例:异步下载国旗图片

james / 897人阅读

摘要:异步下载国旗图片和国家名数据用来捕获异常保存图片文件负责下载的主函数,由传递国家名是数据。异步获取图片异步获取国家名中的子句在没有异常时会运行这里获取循环是为了在保存图片时不阻塞函数内部维护了一个对象注。

异步下载国旗图片和国家名数据
import aiohttp
import asyncio
import os
import time
import sys

POP20_CC = ("CN IN US ID BR PK NG BD RU JP MX PH VN ET EG DE IR CD FR").split()
BASE_URL = "http://flupy.org/data/flags"
DEST_DIR = "downloads/"

class FetchError(Exception):  #用来捕获异常
    def __init__(self, country_code):
        self.country_code = country_code

def save_flag(img, filename):  #保存图片文件
    path = os.path.join(DEST_DIR, filename)
    with open(path, "wb") as fp:
        fp.write(img)

async def http_get(session, url):  #负责下载的主函数,session由download_many传递
    async with session.get(url) as resp:
        if resp.status == 200:
            ctype = resp.headers.get("Content-type", "").lower() 
            if "json" in ctype or url.endswith(".json"): #国家名是json数据。如果内容类型是json
                data = await resp.json()  #那么就用json()方法获取内容
            else:
                data = await resp.read()  #否则直接获取元数据
            return data
        elif resp.status == 404: #捕获异常
            raise web.HTTPNotFound()
        else:
            raise aiohttp.errors.HttpProcessingError(code=res.sstatus, message=res.reason, headers=res.headers)

async def get_flag(session, cc):  #获取图片
    url = "{}/{cc}/{cc}.gif".format(BASE_URL, cc=cc.lower())
    image = await http_get(session, url)  #这里是Io请求需要异步操作
    return image

async def get_country(session, cc):  #获取国家名
    url = "{}/{cc}/metadata.json".format(BASE_URL, cc=cc.lower())
    metadata = await http_get(session, url) #这里是Io请求需要异步操作
    return metadata["country"]

def show(text):
    print(text + "[OK]  ", end="")
    sys.stdout.flush()  #挨个输出国家名,没有这个刷新缓冲区方法就会最后一下子输出所有国家名。

async def download_one(session, cc):
    try:
        image = await get_flag(session, cc)  #异步获取图片
        country = await get_country(session, cc)  #异步获取国家名
    except web.HTTPNotFound:
        msg = "not found"
    except Exception as exc:
        raise FetchError(cc) from exc
    else:  #try中的else子句在没有except异常时会运行
        country = country.replace(" ", "_")
        filename = "{}-{}.gif".format(cc, country)
        loop = asyncio.get_event_loop()  #这里获取循环是为了在保存图片时不阻塞
        loop.run_in_executor(None, save_flag, image, filename) #run_in_excutor函数内部维护了一个TheardPollExecutor对象[注1]。第一个参数默认为concurrent.futures.Executor实例。
    show(cc)
    return cc

async def download_many(cc_list):
    async with aiohttp.ClientSession() as session:  #获取ClientSession对象
        res = await asyncio.gather(*[asyncio.ensure_future(download_one(session, cc)) for cc in sorted(cc_list)]) #gather函数如果参数为协程对象它会自动排定为一个task,这里我们直接用的ensure_future方法生成了task对象。然后并发的运行他们,返回一个结果聚合值列表。
    return len(res)

def main():
    t0 = time.time()
    loop = asyncio.get_event_loop()
    count = loop.run_until_complete(download_many(POP20_CC))
    loop.close()
    elapsed = time.time() - t0
    msg = "
{} flags download in {:.2f}s"
    print(msg.format(count, elapsed))

if __name__ == "__main__":
    main()

#def a(*x):
    #print(x)
#a([1,2]) --> ([1,2],)
#a(*[1,2]) --> (1,2)
#*号可以将列表或元组里的元素解包,每个元素作为多带带的参数传入


其实,异步库依赖于低层线程(直至内核级线程),但是这些库的用户无需创建线程,也无需知道用到了基础设施中的低层线程。在应用中,我们只需确保没有阻塞的代码,事件循环会在背后处理并发。异步系统能避免用户线程级的开销,这是它能比多线程系统管理更多并发连接的原因。

【注1】The loop.run_in_executor() method can be used with a concurrent.futures.ThreadPoolExecutor to execute blocking code in a different OS thread without blocking the OS thread that the event loop runs in.

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/43818.html

相关文章

  • Java爬虫之下载全世界国家国旗图片

    摘要:介绍本篇博客将继续上一篇博客爬虫之使用的模块爬取各国国旗的内容,将用来实现这个爬虫,下载全世界国家的国旗图片。 介绍   本篇博客将继续上一篇博客:Python爬虫之使用Fiddler+Postman+Python的requests模块爬取各国国旗 的内容,将用Java来实现这个爬虫,下载全世界国家的国旗图片。项目不再过多介绍,具体可以参考上一篇博客。  我们将全世界国家的名称放在一个...

    YancyYe 评论0 收藏0
  • Python爬虫之使用Fiddler+Postman+Pythonrequests模块爬取各国国旗

    摘要:流程作为上述过程的一个演示,我们使用的网址为页面如下在表单中输入德国,跳转后的页面如下我们可以发现,在搜索的结果中,会出现德国这个搜索结果。点击该搜索结果,跳转后的页面如下在这个页面中有我们需要的德国的国旗。 介绍   本篇博客将会介绍一个Python爬虫,用来爬取各个国家的国旗,主要的目标是为了展示如何在Python的requests模块中使用POST方法来爬取网页内容。  为了知道...

    laoLiueizo 评论0 收藏0
  • python并发 1:使用 futures 处理并发

    摘要:标准库中所有阻塞型函数都会释放,允许其他线程运行。如果调用引发异常,那么当从迭代器检索其值时,将引发异常。总结自版就支持线程了,只不过是使用线程的最新方式。类封装了模块的组件,使使用线程变得更加方便。下一篇笔记应该是使用处理并发。 作为Python程序员,平时很少使用并发编程,偶尔使用也只需要派生出一批独立的线程,然后放到队列中,批量执行。所以,不夸张的说,虽然我知道线程、进程、并行、...

    Kyxy 评论0 收藏0
  • python并发2:使用asyncio处理并发

    摘要:是之后引入的标准库的,这个包使用事件循环驱动的协程实现并发。没有能从外部终止线程,因为线程随时可能被中断。上一篇并发使用处理并发我们介绍过的,在中,只是调度执行某物的结果。 asyncio asyncio 是Python3.4 之后引入的标准库的,这个包使用事件循环驱动的协程实现并发。asyncio 包在引入标准库之前代号 Tulip(郁金香),所以在网上搜索资料时,会经常看到这种花的...

    wushuiyong 评论0 收藏0
  • Python基础之使用期物处理并发

    摘要:本文重点掌握异步编程的相关概念了解期物的概念意义和使用方法了解中的阻塞型函数释放的特点。一异步编程相关概念阻塞程序未得到所需计算资源时被挂起的状态。 导语:本文章记录了本人在学习Python基础之控制流程篇的重点知识及个人心得,打算入门Python的朋友们可以来一起学习并交流。 本文重点: 1、掌握异步编程的相关概念;2、了解期物future的概念、意义和使用方法;3、了解Python...

    asoren 评论0 收藏0

发表评论

0条评论

james

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<