asyncio+aiohttp異步爬蟲

概念

進程:進程是一個具有獨立功能的程序關於某個數據集合的一次運行活動。進程是操作系統動態執行的基本單元。
線程:一個進程中包含若干線程,當然至少有一個線程,線程可以利用進程所擁有的資源。線程是獨立運行和獨立調度的基本單元。
協程:協程是一種用戶態的輕量級線程。協程無需線程上下文切換的開銷,也無需原子操作鎖定及同步的開銷。
同步:不同程序單元爲了完成某個任務,在執行過程中需靠某種通信方式以協調一致,稱這些程序單元是同步執行的。
異步:爲完成某個任務,不同程序單元之間過程中無需通信協調,也能完成任務的方式,不相關的程序單元之間可以是異步的。
多進程:多進程就是利用 CPU 的多核優勢,在同一時間並行地執行多個任務。多進程模式優點就是穩定性高,因爲一個子進程崩潰了,不會影響主進程和其他子進程,但是操作系統能同時運行的進程數是有限的。
多線程:多線程模式通常比多進程快一點,但是也快不到哪去,而且,多線程模式致命的缺點就是任何一個線程掛掉都可能直接造成整個進程崩潰,因爲所有線程共享進程的內存。

Python 中使用協程最常用的庫莫過於asyncio,然後我們還需要了解一些概念:

event_loop 事件循環

程序開啓一個無限循環,把一些函數註冊到事件循環上,當滿足事件發生的時候,調用相應的協程函數

coroutine 協程

協程對象,指一個使用async關鍵字定義的函數,它的調用不會立即執行函數,而是會返回一個協程對象。協程對象需要註冊到事件循環,由事件循環調用。

task 任務

一個協程對象就是一個原生可以掛起的函數,任務則是對協程進一步封裝,其中包含了任務的各種狀態

future

代表將來執行或沒有執行的任務的結果。它和task上沒有本質上的區別

async/await 關鍵字

python3.5用於定義協程的關鍵字,async定義一個協程,await用於掛起阻塞的異步調用接口。


不知道爲什麼我一看概念這些東西,講的簡單點的還好,一長就容易走神…所以這裏只是簡單複製粘貼了下

Python手冊中關於asyncio的部分
aiohttp手冊

很多網上教的例子都在手冊上有了,所以直接看手冊就行,下面就來根據實例理解理解吧

實例

這個是我爬取站酷網上“瀟湘過客莫念”的雪中悍刀行的插圖,如下所示,通過最基本的requests抓取圖片鏈接,沒有遇到反爬

get_content是返回訪問圖片地址的content數據;downloader是將圖片保存到本地的方法;run是提取目標圖片鏈接並循環訪問,並且記錄整個過程的耗時

import time
import requests


def get_content(link):
    r = requests.get(link)
    content = r.content
    return content


def downloader(img):
    content = get_content(img[1])
    with open('D:\\MY\\雪中\\lists2\\' + str(img[0]) + '.jpg', 'wb') as f:
        f.write(content)
    print('下載成功!' + str(img[0]))


def run():
    start = time.time()  # 記錄起始時間戳
    base_url = 'https://www.zcool.com.cn/work/content/show?p=2&objectId=6455837'
    r = requests.get(base_url)
    image_list = r.json()['data']['allImageList']
    for i, image in enumerate(image_list):
        downloader((i, image['url']))
    end = time.time()  # 獲取結束時間戳
    print('共運行了{}秒'.format(end - start))  # 程序耗時


if __name__ == '__main__':
    run()

運行結果:共運行了99.09104537963867

接着我們來看看通過asyncio+aiohttp的方法的異步爬取,同樣的,代碼分成三個部分(代碼在Python3.6環境運行)

import asyncio
import time
import aiohttp
import requests


async def get_content(link):
    async with aiohttp.ClientSession() as session:
        response = await session.get(link)
        content = await response.read()
        return content


async def downloader(img):
    content = await get_content(img[1])
    with open('D:\\MY\\雪中\\lists1\\' + str(img[0]) + '.jpg', 'wb') as f:
        f.write(content)
    print('下載成功!' + str(img[0]))


def run():
    start = time.time()  # 記錄起始時間戳
    base_url = 'https://www.zcool.com.cn/work/content/show?p=2&objectId=6455837'
    r = requests.get(base_url)
    image_list = r.json()['data']['allImageList']
    loop = asyncio.get_event_loop()
    tasks = [asyncio.ensure_future(downloader((i, image['url']))) for i, image in enumerate(image_list)]
    loop.run_until_complete(asyncio.wait(tasks))
    end = time.time()  # 獲取結束時間戳
    print('共運行了{}秒'.format(end - start))  # 程序耗時


if __name__ == '__main__':
    run()

運行結果:共運行了56.29711127281189秒,跟上面的同步代碼比較,節省了一半左右的時間,這裏的每張圖片大小在幾M

那麼我們來看看代碼是如何執行的

這裏需要注意的是run方法中增加了loop = asyncio.get_event_loop(),就是創建一個event_loop事件循環,這個事件循環叫loop,然後我們要做的就是把協程放到loop事件循環中,協程對象不能直接運行,在註冊事件循環的時候,其實是run_until_complete(coroutine)方法將協程包裝成爲了一個任務task對象,task對象是Future類的子類,保存了協程運行後的狀態,用於未來獲取協程的結果

那麼哪來的協程呢?這裏以async關鍵字定義的方法就是一個協程,這個方法在調用時不會立即被執行,而是返回一個協程對象,協程對象需要註冊到事件循環,由事件循環調用

tasks = [asyncio.ensure_future(downloader((i, image['url']))) for i, image in enumerate(image_list)]

這行代碼的作用實際上就是通過循環生成多個downloader協程,傳入一個元組類型的參數,這個元祖中包含兩個參數,然後通過asyncio.ensure_future()方法返回task任務(一個協程對象就是一個原生可以掛起的函數,任務則是對協程進一步封裝,其中包含了任務的各種狀態,通過asyncio.ensure_future(coroutine)創建task,同樣的可以通過loop.create_task(coroutine)創建task),最終得到一個tasks任務列表

loop.run_until_complete(asyncio.wait(tasks))

而這行代碼說實話我沒有深入的理解,就大概理解爲事件循環執行run_until_complete方法,等到其中的每一個可等待對象都執行完畢,對於其中的每一個對象,去執行downloader協程,運行到await get_content(img[1])時掛起當前的協程,去執行get_content()這個協程,引用了aiohttp裏的ClientSession類,建立 了一個session對象,通過session.get()得到response以及最後的content並最終返回content

然後繼續執行downloader中的保存圖片部分

以上就是用asyncio+aiohttp的簡單的異步爬蟲實例,很明顯相較於一般的爬蟲能節省很多時間啦

參考

* asyncio.wait如何理解
asyncio.wait實現的異步
* python中重要的模塊–asyncio
* 【Python3爬蟲】使用異步協程編寫爬蟲

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