為了搞清楚asynico模塊的具體作用,那我們要先明白一些基本概念。一般在爬蟲里面,為了加快速度,我們可以使用多進(jìn)程、多線程、協(xié)程,這篇文章詳細(xì)解讀一下這三者的區(qū)別的區(qū)別。在一個(gè)主程序運(yùn)行的時(shí)候,會(huì)有一個(gè)主進(jìn)程產(chǎn)生,但是進(jìn)程是不能處理任務(wù)的,要處理具體任務(wù),例如發(fā)送請(qǐng)求,讀寫文件,它就要產(chǎn)生一個(gè)主線程幫它完成,而在任務(wù)處理的時(shí)候,這里繼續(xù)以發(fā)送請(qǐng)求為例,很多時(shí)候請(qǐng)求發(fā)送出去了,但回復(fù)并不是及時(shí)的,這時(shí)候線程會(huì)卡在這里,等待回復(fù)之后再處理下一個(gè)URL,這個(gè)時(shí)候就出現(xiàn)了協(xié)程。如果在發(fā)送請(qǐng)求的時(shí)候,線程并不等待服務(wù)器的回復(fù),而是直接發(fā)送第二個(gè)URL的請(qǐng)求,第三個(gè),第四個(gè)......直到第一個(gè)請(qǐng)求回復(fù)了,那么再回來處理第一個(gè)請(qǐng)求的的回復(fù),這就是協(xié)程的工作原理,充分利用線程的工作效率,也沒有多線程切換的開銷,所以在處理IO操作時(shí)協(xié)程非常高效。
asynico模塊就是支持異步IO的,是在Python3.4之后才有的模塊,功能相當(dāng)強(qiáng)大,但是目前它不支持發(fā)送http請(qǐng)求,只支持tcp請(qǐng)求,如果要發(fā)送http請(qǐng)求,就要自己再tcp基礎(chǔ)之上封裝自己的http請(qǐng)求,當(dāng)然這啃不動(dòng)不用我們自己寫啊,誰叫我們用了Python呢,早就有人為我們封裝了這個(gè)模塊,那就是aiohttp,我們直接用就好了。下面是一個(gè)簡(jiǎn)單的事例代碼,python3.4版本的。
import aiohttp,asyncio
@asyncio.coroutine#這里是協(xié)程的固定用法,需要用該裝飾器
def downing_url(url):
print('downing',url)
response = yield from aiohttp.request('GET', url)
print(url, response)
response.close()
tasks = [downing_url('http://www.cnblogs.com/'), downing_url('http://www.chouti.com/')]
event_loop = asyncio.get_event_loop()#事件循環(huán),也就是監(jiān)聽是否遇到IO阻塞
results = event_loop.run_until_complete(asyncio.gather(*tasks))#把任務(wù)添加進(jìn)去,也就是執(zhí)行的函數(shù)
event_loop.close()#關(guān)閉事件循環(huán)
執(zhí)行結(jié)果如圖

在看崔大神的課程時(shí),發(fā)現(xiàn)Python3.5對(duì)于協(xié)程有了更好的封裝,代碼如圖
import aiohttp
async def downing_url(url):
print('downing',url)
response = await aiohttp.request('GET', url)
print(url, response)
response.close()
tasks = [downing_url('http://www.cnblogs.com/'), downing_url('http://www.chouti.com/')]
event_loop = asyncio.get_event_loop()#事件循環(huán)
results = event_loop.run_until_complete(asyncio.gather(*tasks))#把任務(wù)添加進(jìn)去,也就是執(zhí)行的函數(shù)
event_loop.close()#關(guān)閉事件循環(huán)
我們也可以為請(qǐng)求添加代理或者請(qǐng)求頭
async def downing_url(url):
async with aiohttp.ClientSession() as session:
print('downing',url)
async with session.get(url,proxy='http://121.193.143.249:80') as html:
text = await html.text()
print(text)
tasks = [downing_url('http://ip.chinaz.com/getip.aspx'), downing_url('http://ip.chinaz.com/getip.aspx')]
event_loop = asyncio.get_event_loop()#事件循環(huán)
results = event_loop.run_until_complete(asyncio.gather(*tasks))
event_loop.close()#關(guān)閉事件循環(huán)
詳細(xì)的信息請(qǐng)移步官方文檔http://aiohttp.readthedocs.io/en/stable/
這里在函數(shù)面前直接添加async以及用await替代yield from,看著就更合理化了。
如果用asynico結(jié)合requests模塊,就要復(fù)雜一點(diǎn)了,下面兩段代碼是網(wǎng)上摘錄老男孩武沛齊老師博客的
import asyncio
import requests
@asyncio.coroutine
def fetch_async(func, *args):
loop = asyncio.get_event_loop()
future = loop.run_in_executor(None, func, *args)
response = yield from future
print(response.url, response.content)
tasks = [
fetch_async(requests.get, 'http://www.cnblogs.com/wupeiqi/'),
fetch_async(requests.get, 'http://dig.chouti.com/pic/show?nid=4073644713430508&lid=10273091')
]
loop = asyncio.get_event_loop()
results = loop.run_until_complete(asyncio.gather(*tasks))
loop.close()
異步模塊twsted
from twisted.web.client import getPage
from twisted.internet import reactor
REV_COUNTER = 0
REQ_COUNTER = 0
def callback(contents):
print(contents,)
global REV_COUNTER
REV_COUNTER += 1
if REV_COUNTER == REQ_COUNTER:
reactor.stop()
url_list = ['http://www.bing.com', 'http://www.baidu.com', ]
REQ_COUNTER = len(url_list)
for url in url_list:
deferred = getPage(bytes(url, encoding='utf8'))
deferred.addCallback(callback)
reactor.run()