500lines.crawler源碼分析-基於asyncio和aiohttp寫一個爬蟲

基於asyncio和aiohttp寫一個爬蟲

介紹

這篇文章是對500lines.crawler 這個項目的爬蟲的精簡,只保留最最核心的代碼,來分析asyncio和await的實踐

asyncio和aiohttp是python內置的倆套高性能非同步io網路庫,可以用他們達到一個很大的並發量

源碼分析

Crawler的定義及啟動

Crawler:

class Crawler: def __init__(self, roots, max_tasks, max_tries, loop = None): self.loop = loop or asyncio.get_event_loop() self.roots = roots self.seen_url = set() self.root_domains = set() self.session = aiohttp.ClientSession(loop=self) self.q = Queue() self.max_tasks = max_tasks self.max_tries = max_tries for root in roots: self.add_url(root) def add_url(self, url):# 加入url到隊列 相當於生產者 async def work(self):# 消費者函數 async def fetch(self, url):# 獲取資源的函數 async def process(self, response):# 處理函數 async def crawl(self):# 啟動函數 def close(self):

啟動:

if __name__ == __main__: roots = [www.baidu.com] crawler = Crawler(roots=roots, max_tasks=4, max_tries=5) loop = asyncio.get_event_loop() try: loop.run_until_complete(crawler.crawl()) #最核心的啟動代碼 finally: crawler.close() loop.stop() loop.close()

首先先是Crawler類的初始化:

init 中有如下一句

for root in roots: self.add_url(root)

然後add_url中:

def add_url(self, url): self.seen_url.add(url) self.q.put(url) # 生產完畢加入隊列

其實就是一個生產者把東西生產了出來

跟進到啟動函數craw中,看看他幹了什麼:

async def crawl(self): """"run spider""" workers = [asyncio.Task(self.work(), loop=self.loop) for _ in range(self.max_tasks)] # 此處啟動了消費者work await self.q.join() for w in workers: w.cancel()

啟動了消費者work

work如何消費的呢

async def work(self): while True: try: url = await self.q.get() if url in self.seen_url: break await self.fetch(url) finally: self.q.task_done()

就是 一個很普通的消費者的模板,沒什麼好說的, 其中掛起了 fetch這個耗費時間的網路的獲取操作

fetch怎麼非同步獲取

為了達到非同步io的目的,request這個庫是不能用的,因為await的對象必須是一個awaitable的, 所以 我們使用了aiohttp這個庫, 在有限的嘗試次數內嘗試,完成了發送給process處理

async def fetch(self, url): tries = 0 while tries < self.max_tries:# 在允許的嘗試次數以內 try: response = await self.session.get(url, allow_redirects=False) break finally: pass tries += 1 else: await self.process(response)

process中處理

async def process(self, response): body = await response.read()# 爬蟲的解析操作 return body

process 掛起了aiohttp中非同步讀取的操作後返回要爬的內容,這只是個簡略,為了好理解。

結束了 退出程序

for w in workers: w.cancel()## 取消每一個消費者 crawler.close() loop.stop() loop.close()## 關閉協程和事件循環

感悟

其實這是一個同步的生產者把內容生產完了交給非同步消費者的一個模型,其實生產者應該也能變成非同步的

可能是這個感覺

async def add_url(roots): await asyncio.wait([q.put(i) for root in roots])

也不知道對不對 希望dalao們指點一下

參考資料

【1】.500lines.crawler

推薦閱讀:

Python編程(bbb四):兩個實用的Python的裝飾器
Flask中,用upgrade命令遷移資料庫後,生成的資料庫里沒有數據表怎麼回事?
python作為腳本語言和c/c++ 等語言的優勢和劣勢在哪裡地方?python比較成熟用途在哪裡方面?
Python 開發者的6個必備庫

TAG:Python | 高并发 | 爬虫 |