提高爬虫效率aiohttp
眾所周知,Requests 庫一個優(yōu)秀的 HTTP 庫,通過它可以非常簡單地發(fā)起 HTTP 請求。不過,這個庫所執(zhí)行的網(wǎng)絡(luò)請求都是同步。當爬蟲程序進程獲得 CPU 的時間片時,如果程序在進行 I/O 操作(例下載圖片),在這段 IO 執(zhí)行的時間里,CPU 處于空閑中,這樣會造成 CPU 的計算能力就被浪費了。
如果 CPU 能將等待時間利用起來,那么爬蟲效率就提高了。那就需要對程序進行改造,將 I/O 同步操作變成異步操作。本文內(nèi)容是介紹一個強大的異步 I/O 操作的庫 —— aiohttp。
1 aiohttp 安裝
安裝 aiohttp 可以通過 pip 方式安裝,在終端中執(zhí)行安裝命令即可。
pip install aiohttp2 aiohttp 介紹
說到 aiohttp ,不得不說下 asyncio 。asyncio 是 Python 3.4 版本引入的標準庫。它工作模式是單線程并發(fā),使用協(xié)同執(zhí)行 I/O 操作。asyncio 的編程模型就是一個消息循環(huán)。我們從 asyncio 模塊中直接獲取一個 EventLoop 的引用,然后把需要執(zhí)行的協(xié)程扔到 EventLoop 中執(zhí)行,就實現(xiàn)了異步 IO。
使用 asyncio 實現(xiàn)一個異步函數(shù) hello() 的例子:
import asyncio@asyncio.coroutine # 修飾符,等同于 asyncio.coroutine(hello()) def hello():print("Hello world!")# 異步調(diào)用asyncio.sleep(1):r = yield from asyncio.sleep(1)print("Hello again!")# 獲取EventLoop: loop = asyncio.get_event_loop() # 執(zhí)行coroutine loop.run_until_complete(hello()) loop.close()而 aiohttp 則是基于 asyncio 實現(xiàn)的 HTTP 框架。 aiohttp 全稱是 Async http client/server framework。翻譯成中文是異步 HTTP 的客戶端/服務(wù)器框架。從名字中,我們可知 aiohttp 是分為服務(wù)器端和客戶端,專門異步處理 HTTP 的請求。
3 async/await 語法
前面我們講到異步 I/O 的用法,但是聲明異步函數(shù)比較繁瑣,還需要依賴 yield 語法。在 Python 3.5 中,引入了 async/await 關(guān)鍵字,使得異步回調(diào)的寫法更加直觀和人性化。
在函數(shù) def 之前增加關(guān)鍵字async,表示這個函數(shù)是異步函數(shù)。相當于替代語法@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!")最后執(zhí)行異步函數(shù),還是需要用到 EventLoop 引用,然后利用協(xié)程執(zhí)行異步函數(shù)。最終的代碼如下:
import asyncioasync 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()運行結(jié)果如下:Hello world! >> 會暫停一秒鐘 Hello again!4 aiohttp 基本用法
我們使用 aiohttp 以 GET 方式向httpbin.org網(wǎng)站發(fā)起一個 HTTP 請求。因為是 aiohttp 是異步處理 HTTP 請求。所以還必須遵循 Python 的異步函數(shù)語法,即需使用 async/await 語法。
使用 aiohttp 發(fā)起一個 HTTP 請求,具體編寫可以分為以下幾步:
1)使用 async 定義異步函數(shù)
2)通過 aiohttp.ClientSession 獲取一個 session 對象
3)用該 session 對象以 GET、POST、PUT 等方式去請求網(wǎng)頁
4)最后獲取 EventLoop 引用,執(zhí)行異步函數(shù)。
aiohttp 支持自定義 headers、設(shè)置超時時間、設(shè)置代理、自定義 cookie 等。
import asyncio import aiohttpurl = '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', }# 定義異步函數(shù) main() async def main():# 獲取 session 對象async with aiohttp.ClientSession() as session:# post 方式請求 httbinasync 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())關(guān)于 aiohttp 更多用法,可以執(zhí)行閱讀官網(wǎng)文檔。說句實話,aiohttp 跟 Requests 的用法大同小異。如果你已經(jīng)學會了 Requests 庫,很快就能掌握 aiohttp 的用法。
相關(guān)例子
同步
異步
import asyncio from datetime import datetimeimport aiohttp from lxml import etree headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit""/537.36 (KHTML, like Gecko) ""Chrome/72.0.3626.121 Safari/537.36"}async def get_movie_url():req_url = "https://movie.douban.com/chart"async with aiohttp.ClientSession(headers=headers) as session:async with session.get(url=req_url, headers=headers) as response:result = await response.text()result = etree.HTML(result)return result.xpath("//*[@id='content']/div/div[1]/div/div/table/tr/td/a/@href")async def get_movie_content(movie_url):async with aiohttp.ClientSession(headers=headers) as session:async with session.get(url=movie_url, headers=headers) as response:result = await response.text()result = etree.HTML(result)movie = dict()name = result.xpath('//*[@id="content"]/h1/span[1]//text()')author = result.xpath('//*[@id="info"]/span[1]/span[2]//text()')movie["name"] = namemovie["author"] = authorreturn movieif __name__ == '__main__':start = datetime.now()loop = asyncio.get_event_loop()movie_url_list = loop.run_until_complete(get_movie_url())tasks = [get_movie_content(url) for url in movie_url_list]movies = loop.run_until_complete(asyncio.gather(*tasks))print(movies)print("異步用時為:{}".format(datetime.now() - start))E:\venv\spider\Scripts\python.exe E:/python_project/filetest/aio_douban.py [{'name': ['小丑 Joker'], 'author': ['托德·菲利普斯']}, {'name': ['好萊塢往事 Once Upon a Time... in Hollywood'], 'author': ['昆汀·塔倫蒂諾']}, {'name': ['愛爾蘭人 The Irishman'], 'author': ['馬丁·斯科塞斯']}, {'name': ['準備好了沒 Ready or Not'], 'author': ['馬特·貝蒂內(nèi)利-奧爾平', ' / ', '泰勒·吉勒特']}, {'name': ['82年生的金智英 82?? ???'], 'author': ['金度英']}, {'name': ['克勞斯:圣誕節(jié)的秘密 Klaus'], 'author': ['塞爾希奧·巴勃羅斯', ' / ', '卡洛斯·馬丁內(nèi)斯·洛佩斯']}, {'name': ['寄生蟲 ???'], 'author': ['奉俊昊']}, {'name': ['騾子 The Mule'], 'author': ['克林特·伊斯特伍德']}, {'name': ['別告訴她 The Farewell'], 'author': ['王子逸']}, {'name': ['犯罪現(xiàn)場 犯罪現(xiàn)場'], 'author': ['馮志強']}] 異步用時為:0:00:02.230956Process finished with exit code 0總結(jié)
以上是生活随笔為你收集整理的提高爬虫效率aiohttp的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LaTex常用的一些语法【超实用-新手入
- 下一篇: cocos creator 2.3.2火