Python3 基於asyncio的新聞爬蟲思路

c5e1d11c967e25f84a778876b0c8e077032.jpg

Python寫爬蟲是很是方便的,爬取的目標不一樣,實現的方式也有很大不一樣。新聞爬蟲的方便之處是,新聞網站幾乎沒有反爬蟲策略,很差的地方是你想要爬取的新聞網站很是很是多。這個時候,效率就是你首要考慮的問題。
同步循環的效率在這裏相形見絀,你須要的是異步IO實現一個高效率的爬蟲。python

Python3.5開始,加入了新的語法,async和await這兩個關鍵字,asyncio也成了標準庫,這對於咱們寫異步IO的程序來講就是如虎添翼,讓咱們垂手可得的實現一個定向抓取新聞的異步爬蟲。mysql

  1. 異步爬蟲依賴的模塊

asyncio: 標準異步模塊,實現python的異步機制;
uvloop:一個用C開發的異步循環模塊,大大提升異步機制的效率;
aiohttp: 一個異步http請求的模塊,用於下載網頁;
urllib.parse: 解析url網站的模塊;
logging: 記錄爬蟲日誌;
leveldb: Google的Key-Value數據庫,用以記錄url的狀態;
farmhash: 對url進行hash計算做爲url的惟一標識;
sanicdb: 對aiomysql的封裝,更方便的進行數據庫mysql操做;sql

  1. 異步爬蟲實現的流程

2.1 新聞源列表數據庫

本文要實現的異步爬蟲是一個定向抓取新聞網站的爬蟲,因此就須要管理一個定向源列表,這個源列表記錄了不少咱們想要抓取的新聞網站的url,這些url指向的網頁叫作hub網頁,它們有以下特色:運維

它們是網站首頁、頻道首頁、最新列表等等;
它們包含很是多的新聞頁面的連接;
它們常常被網站更新,以包含最新的新聞連接;
它們不是包含新聞內容的新聞頁面;
Hub網頁就是爬蟲抓取的起點,爬蟲從中提取新聞頁面的連接再進行抓取。Hub網址能夠保存在MySQL數據庫中,運維能夠隨時添加、刪除這個列表;爬蟲定時讀取這個列表來更新定向抓取的任務。這就須要爬蟲中有一個循環來定時讀取hub網址。異步

2.2 網址池async

異步爬蟲的全部流程不能單單用一個循環來完成,它是多個循環(至少兩個)相互做用共同完成的。它們相互做用的橋樑就是「網址池」(用asyncio.Queue來實現)。函數

這個網址池就是咱們比較熟悉的「生產者-消費者」模式。oop

一方面,hub網址隔段時間就要進入網址池,爬蟲從網頁提取到的新聞連接也有進入到網址池,這是生產網址的過程;網站

另外一方面,爬蟲要從網址池中取出網址進行下載,這個過程是消費過程;

兩個過程相互配合,就有url不斷的進進出出網址池。

2.3 數據庫

這裏面用到了兩個數據庫:MySQL和Leveldb。前者用於保存hub網址、下載的網頁;後者用於存儲全部url的狀態(是否抓取成功)。

從網頁提取到的不少連接可能已經被抓取過了,就沒必要再進行抓取,因此他們在進入網址池前就要被檢查一下,經過leveldb能夠快速查看其狀態。

  1. 異步爬蟲的實現細節

前面的爬蟲流程中提到兩個循環:

循環一:定時更新hub網站列表

async def loop_get_urls(self,):
    print('loop_get_urls() start')
    while 1:
        await self.get_urls() # 從MySQL讀取hub列表並將hub url放入queue
        await asyncio.sleep(50)

循環二: 抓取網頁的循環

async def loop_crawl(self,):
    print('loop_crawl() start')
    last_rating_time = time.time()
    asyncio.ensure_future(self.loop_get_urls())
    counter = 0
    while 1:
        item = await self.queue.get()
        url, ishub = item
        self._workers += 1
        counter += 1
        asyncio.ensure_future(self.process(url, ishub))
        span = time.time() - last_rating_time 
        if self._workers > self.workers_max:
            print('got workers_max, sleep 3 sec to next worker')
            await asyncio.sleep(3)
  1. asyncio 要點:

讀讀asyncio的文檔就能夠知道它的運行流程,這裏分享一下使用時注意到的地方。

(1)使用loop.run_until_complete(self.loop_crawl())來啓動整個程序的主循環;

(2)使用asyncio.ensure_future() 來異步調用一個函數,它至關於多進程的fork,gevent的spawn(),具體能夠參考上述代碼。

文章來源於:猿人學網站的python教程

版權申明:若沒有特殊說明,文章皆是猿人學原創,沒有猿人學受權,請勿以任何形式轉載。

相關文章
相關標籤/搜索