想提高爬蟲效率?aiohttp 瞭解下

極客猴發表於2018-08-08

本文原創釋出於微信公眾號「極客猴」,歡迎關注第一時間獲取更多原創分享

對於爬蟲程式,我們往往會很關注其爬蟲效率。影響爬蟲效率有幾個因素有,是否使用多執行緒,I/O 操作,是否同步執行等。其中 I/O 操作、同步執行是最影響爬蟲效率的。

眾所周知,Requests 庫一個優秀的 HTTP 庫,通過它可以非常簡單地發起 HTTP 請求。不過,這個庫所執行的網路請求都是同步。當爬蟲程式程式獲得 CPU 的時間片時,如果程式在進行 I/O 操作(例下載圖片),在這段 IO 執行的時間裡,CPU 處於空閒中,這樣會造成 CPU 的計算能力就被浪費了。

如果 CPU 能將等待時間利用起來,那麼爬蟲效率就提高了。那就需要對程式進行改造,將 I/O 同步操作變成非同步操作。本文內容是介紹一個強大的非同步 I/O 操作的庫 —— aiohttp

1 aiohttp 介紹

說到 aiohttp ,不得不說下 asyncio 。asyncio 是 Python 3.4 版本引入的標準庫。它工作模式是單執行緒併發,使用協同執行 I/O 操作。asyncio 的程式設計模型就是一個訊息迴圈。我們從 asyncio 模組中直接獲取一個 EventLoop 的引用,然後把需要執行的協程扔到 EventLoop 中執行,就實現了非同步 IO。

使用 asyncio 實現一個非同步函式 hello() 的例子:

import asyncio

@asyncio.coroutine # 修飾符,等同於 asyncio.coroutine(hello())
def hello():
    print("Hello world!")
    # 非同步呼叫asyncio.sleep(1):
    r = yield from asyncio.sleep(1)
    print("Hello again!")

# 獲取EventLoop:
loop = asyncio.get_event_loop()
# 執行coroutine
loop.run_until_complete(hello())
loop.close()
複製程式碼

而 aiohttp 則是基於 asyncio 實現的 HTTP 框架。 aiohttp 全稱是 Async http client/server framework。翻譯成中文是非同步 HTTP 的客戶端/伺服器框架。從名字中,我們可知 aiohttp 是分為伺服器端和客戶端,專門非同步處理 HTTP 的請求。

2 aiohttp 安裝

安裝 aiohttp 可以通過 pip 方式安裝,在終端中執行安裝命令即可。

pip install aiohttp
複製程式碼

3 async/await 語法

前面我們講到非同步 I/O 的用法,但是宣告非同步函式比較繁瑣,還需要依賴 yield 語法。在 Python 3.5 中,引入了 async/await 關鍵字,使得非同步回撥的寫法更加直觀和人性化。

在函式 def 之前增加關鍵字async,表示這個函式是非同步函式。相當於替代語法@asyncio.coroutine。具體例子例如:

async def hello():
    print("Hello World!")
複製程式碼

另外使用 await 替換了 yield from, 表示這部分操作為非同步操作。

async def hello():
    print("Hello World!")
    r = await asyncio.sleep(1)
    print("Hello again!")
複製程式碼

最後執行非同步函式,還是需要用到 EventLoop 引用,然後利用協程執行非同步函式。最終的程式碼如下:

import asyncio

async def hello():
    print("Hello world!")
    r = await asyncio.sleep(1)
    print("Hello again!")

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    tasks = [hello(), ]
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()
複製程式碼

執行結果如下:

Hello world!
>> 會暫停一秒鐘
Hello again!
複製程式碼

4 aiohttp 基本用法

我們使用 aiohttp 以 GET 方式向httpbin.org網站發起一個 HTTP 請求。因為是 aiohttp 是非同步處理 HTTP 請求。所以還必須遵循 Python 的非同步函式語法,即需使用 async/await 語法。

使用 aiohttp 發起一個 HTTP 請求,具體編寫可以分為以下幾步: 1)使用 async 定義非同步函式 2)通過 aiohttp.ClientSession 獲取一個 session 物件 3)用該 session 物件以 GET、POST、PUT 等方式去請求網頁 4)最後獲取 EventLoop 引用,執行非同步函式。

import asyncio
import aiohttp

# 定義非同步函式 main()
async def main():
    # 獲取 session 物件
      async with aiohttp.ClientSession() as session:
        # get 方式請求 httbin  async with session.get('http://httpbin.org/get') as response:
            print(response.status)
            print(await response.text())

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
複製程式碼

aiohttp 支援自定義 headers、設定超時時間、設定代理、自定義 cookie 等。

import asyncio
import aiohttp

url = 'http://httpbin.org/post'
headers = {
    'User-agent': "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36",
}

data = {
    'data': 'person data',
}

# 定義非同步函式 main()
async def main():
    # 獲取 session 物件
    async with aiohttp.ClientSession() as session:
        # post 方式請求 httbin
        async with session.post(url=url, headers=headers, data=data) as response:
            print(response.status)
            print(await response.text())

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
複製程式碼

關於 aiohttp 更多用法,可以執行閱讀官網文件。說句實話,aiohttp 跟 Requests 的用法大同小異。如果你已經學會了 Requests 庫,很快就能掌握 aiohttp 的用法。


本文首發於微信公眾號,原文地址是 想提高爬蟲效率?aiohttp 瞭解下。隨時歡迎轉載文章, 轉載請聯絡號主開通白名單,尊重作者的原創。本人微信公眾號「極客猴」,每週分享 Python 原創乾貨。涉及網路爬蟲、資料分析、web 開發等方向。

想提高爬蟲效率?aiohttp 瞭解下

相關文章