aiohttp 基於異步庫的請求替代品


asyncio可以實現單線程的並發IO操作,如果僅用在客戶端,發揮的威力並不大,把asyncio用在服務端,例如WEB服務器,由於HTTP連接就是IO操作,因此可以用單線程+coroutine實現多用戶的高並發支持。

asyncio實現了TCP、UDP、SSL等協議,aiohttp則是基於asyncio實現的HTTP框架。

首先我們定義一個協同程序用來獲取頁面,並打印出來。我們使用asyncio.coroutine將一個方法裝飾成一個協同程序,aiohttp.request是一個協同程序,所以他是一個可讀方法。

@asyncio.coroutine
def print_page(url):
    response = yield from aiohttp.request('GET', url)
    #close斷開的是網頁與服務器的Connection:keep-alive    
    body = yield from response.read_and_close(dcode=True)
    print(body)

如你所見,我們可以使用yield from從另一個協程中調用一個協程。為了從同步代碼中調用一個協程,我們需要一個時間循環,我們可以通過asyncio.get_event_loop()得到一個標准的時間循環,之后使用它的loop.run_until_complete()方法啟動協程,所以,為了使之前的協程運行我們只需要做:

loop = asyncio.get_evemnt_loop()
loop.run_until_complete(print_page('http://xxxxx'))

一個有用的方法是asyncio.wait,通過它可以獲取一個協程的列表,同時返回一個將它們包括在內的單獨的協程,所以我們可以這樣寫:

loop.run_until_complete(asyncio.wait([print_page(url) for url in url_list]))

數據抓取

現在我們已經知道如何做異步請求,因此我們可以寫一個數據抓取器,我們僅僅還需要一些工具來解析HTML

import aiohttp
import asyncio

def get_content(html):
    '''
    處理HTML獲取所需信息
    '''
async def print_magnet(page):
    headers = {'key':'value'}
    cookies = {'cookies_are': 'working'}
    url = 'http://www.cnbligs.com/#p{}'.format(page)
    async with aiohttp.ClientSession(cookies=cookies) as session:
        async with session.get(url, headers=headers) as response:
            content = get_content(await response.text())
            print(await response.text())

loop = asyncio.get_event_loop()
tasks = asyncio.wait([print_magnet(page) for page in range(10)])
loop.run_until_complete(tasks)            
            

為了避免爬蟲一次性的產生過多的請求導致賬號/IP被封可以考慮使Semaphore控制同時的並發量,與我們熟悉的threading模塊中的Semaphore(信號量)用法類似。

import aiohttp
impoer asyncio

NUMBERS = range(12)
URL = 'http://httpbin.org/get?a={}'
sema = asyncio.Semaphore(3)

async def fetch_async(a):
    async with aiohttp.request('GET', URL.format(a)) as res:
        data = await r.json()
    return data['args']['a']
    
async def print_result(a):
    with (await sema):
        r = await fetch_async(a)
        print('fetch({}) = {}'.foemat(a, r))
        

loop = asyncio.get_event_loop()
f = asyncio.wait([print_result(num) for num in NUMBERS])
loop.run_until_complete(f)    

可以到后台看到並發受到了信號量的限制,同一時刻基本只處理三個請求。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM