每日新聞(爬取新聞)是一款python爬蟲之抓取網(wǎng)頁新聞標(biāo)題與鏈接的工具,每天上班想看看新聞,于是Python打包了一個(gè)可以一鍵點(diǎn)擊查看新聞的exe,現(xiàn)在目的就是爬取有關(guān)這個(gè)關(guān)鍵詞的網(wǎng)頁文章,如題目、媒體、日期、內(nèi)容、url。
軟件說明
利用python寫爬蟲的人越來越多,這也表明了用python寫爬蟲相比其它語言用起來更方便一些。很多新聞網(wǎng)站都沒有反爬蟲的策略,所以爬取新聞網(wǎng)站的數(shù)據(jù)就更加方便。
異步爬蟲實(shí)現(xiàn)的流程
1 新聞源列表
本文要實(shí)現(xiàn)的異步爬蟲是一個(gè)定向抓取新聞網(wǎng)站的爬蟲,所以就需要管理一個(gè)定向源列表,這個(gè)源列表記錄了很多我們想要抓取的新聞網(wǎng)站的url,這些url指向的網(wǎng)頁叫做hub網(wǎng)頁,它們有如下特點(diǎn):
它們是網(wǎng)站首頁、頻道首頁、最新列表等等;
它們包含非常多的新聞頁面的鏈接;
它們經(jīng)常被網(wǎng)站更新,以包含最新的新聞鏈接;
它們不是包含新聞內(nèi)容的新聞頁面;
Hub網(wǎng)頁就是爬蟲抓取的起點(diǎn),爬蟲從中提取新聞頁面的鏈接再進(jìn)行抓取。Hub網(wǎng)址可以保存在MySQL數(shù)據(jù)庫中,運(yùn)維可以隨時(shí)添加、刪除這個(gè)列表;爬蟲定時(shí)讀取這個(gè)列表來更新定向抓取的任務(wù)。這就需要爬蟲中有一個(gè)循環(huán)來定時(shí)讀取hub網(wǎng)址。
2 網(wǎng)址池
異步爬蟲的所有流程不能單單用一個(gè)循環(huán)來完成,它是多個(gè)循環(huán)(至少兩個(gè))相互作用共同完成的。它們相互作用的橋梁就是“網(wǎng)址池”(用asyncio.Queue來實(shí)現(xiàn))。
這個(gè)網(wǎng)址池就是我們比較熟悉的“生產(chǎn)者-消費(fèi)者”模式。
一方面,hub網(wǎng)址隔段時(shí)間就要進(jìn)入網(wǎng)址池,爬蟲從網(wǎng)頁提取到的新聞鏈接也有進(jìn)入到網(wǎng)址池,這是生產(chǎn)網(wǎng)址的過程;
另一方面,爬蟲要從網(wǎng)址池中取出網(wǎng)址進(jìn)行下載,這個(gè)過程是消費(fèi)過程;
兩個(gè)過程相互配合,就有url不斷的進(jìn)進(jìn)出出網(wǎng)址池。
3 數(shù)據(jù)庫
這里面用到了兩個(gè)數(shù)據(jù)庫:MySQL和Leveldb。前者用于保存hub網(wǎng)址、下載的網(wǎng)頁;后者用于存儲(chǔ)所有url的狀態(tài)(是否抓取成功)。
從網(wǎng)頁提取到的很多鏈接可能已經(jīng)被抓取過了,就不必再進(jìn)行抓取,所以他們?cè)谶M(jìn)入網(wǎng)址池前就要被檢查一下,通過leveldb可以快速查看其狀態(tài)。
3. 異步爬蟲的實(shí)現(xiàn)細(xì)節(jié)
前面的爬蟲流程中提到兩個(gè)循環(huán):
循環(huán)一:定時(shí)更新hub網(wǎng)站列表
async defloop_get_urls(self,):print('loop_get_urls() start')while 1:
await self.get_urls()#從MySQL讀取hub列表并將hub url放入queue
await asyncio.sleep(50)
循環(huán)二: 抓取網(wǎng)頁的循環(huán)
async defloop_crawl(self,):print('loop_crawl() start')
last_rating_time=time.time()
asyncio.ensure_future(self.loop_get_urls())
counter=0while 1:
item=await self.queue.get()
url, ishub=item
self._workers+= 1counter+= 1asyncio.ensure_future(self.process(url, ishub))
span= time.time() -last_rating_timeif span > 3:
rate= counter /spanprint('\tloop_crawl2() rate:%s, counter: %s, workers: %s' % (round(rate, 2), counter, self._workers))
last_rating_time=time.time()
counter=0if self._workers >self.workers_max:print('====== got workers_max, sleep 3 sec to next worker =====')
await asyncio.sleep(3)
更新說明
已更新排版好的exe