20 | 揭祕 Python 協程

1. 一個爬蟲的例子

import time

def crawl_page(url):
    print('crawling {}'.format(url))
    sleep_time = int(url.split('_')[-1])
    time.sleep(sleep_time)
    print('OK {}'.format(url))

def main(urls):
    for url in urls:
        crawl_page(url)

%time main(['url_1', 'url_2', 'url_3', 'url_4'])

########## 輸出 ##########

crawling url_1
OK url_1
crawling url_2
OK url_2
crawling url_3
OK url_3
crawling url_4
OK url_4
Wall time: 10 s

scrawl_page 爲函數休眠數秒,休眠時間取決於url最後的那個數字。

main()函數執行,調取craw_page()函數進行網絡通信,經過若干秒等待後收到結果,然後執行下一個。

它也佔用了不少時間,五個頁面分別用了1秒到4秒的時間 ,加起來一共用了10秒,這樣效率低,所以要優化,一個簡單的思路就是併發化,用協程來寫。

import asyncio

async def crawl_page(url):
    print('crawling {}'.format(url))
    sleep_time = int(url.split('_')[-1])
    await asyncio.sleep(sleep_time)
    print('OK {}'.format(url))

async def main(urls):
    for url in urls:
        await crawl_page(url)

%time asyncio.run(main(['url_1', 'url_2', 'url_3', 'url_4']))

########## 輸出 ##########

crawling url_1
OK url_1
crawling url_2
OK url_2
crawling url_3
OK url_3
crawling url_4
OK url_4
Wall time: 10 s

import asyncio這個庫包含了大部分我們實現協程所需的魔法工具

async修飾詞聲明異步函數,於是這裏的craw_page和main都變成了異步函數,而調用異步函數,我們便可得到一個協程對象(coroutine object).

執行協程有多個方法,常用的三種:

await執行的效果,和python正常執行一樣的,也就是說程序會阻塞在這,進入被調用的協程函數,執行完畢後再繼續,而這也是await的字面意思,代碼中await asynio.sleep(sleep_time)會在這裏休息若干秒,await crawl_page(url)則會執行craw_page()函數。

asyncio.create_task()來創建任務,最後用asynicio.run來運行。

import asyncio

async def crawl_page(url):
    print('crawling {}'.format(url))
    sleep_time = int(url.split('_')[-1])
    await asyncio.sleep(sleep_time)
    print('OK {}'.format(url))

async def main(urls):
    tasks = [asyncio.create_task(crawl_page(url)) for url in urls]
    for task in tasks:
        await task

%time asyncio.run(main(['url_1', 'url_2', 'url_3', 'url_4']))

########## 輸出 ##########

crawling url_1
crawling url_2
crawling url_3
crawling url_4
OK url_1
OK url_2
OK url_3
OK url_4
Wall time: 3.99 s

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章