webpack4.0各個(gè)擊破(3)—— Assets篇
1097
2025-04-05
∟文章首發(fā)于我的個(gè)人博客:Python教程
Python寫爬蟲是非常方便的,爬取的目標(biāo)不同,實(shí)現(xiàn)的方式也有很大不同。新聞爬蟲的方便之處是,新聞網(wǎng)站幾乎沒有反爬蟲策略,不好的地方是你想要爬取的新聞網(wǎng)站非常非常多。這個(gè)時(shí)候,效率就是你首要考慮的問題。
同步循環(huán)的效率在這里相形見絀,你需要的是異步IO實(shí)現(xiàn)一個(gè)高效率的爬蟲。
Python3.5開始,加入了新的語法,async和await這兩個(gè)關(guān)鍵字,asyncio也成了標(biāo)準(zhǔn)庫,這對(duì)于我們寫異步IO的程序來說就是如虎添翼,讓我們輕而易舉的實(shí)現(xiàn)一個(gè)定向抓取新聞的異步爬蟲。
1. 異步爬蟲依賴的模塊
asyncio: 標(biāo)準(zhǔn)異步模塊,實(shí)現(xiàn)python的異步機(jī)制;
uvloop:一個(gè)用C開發(fā)的異步循環(huán)模塊,大大提高異步機(jī)制的效率;
aiohttp: 一個(gè)異步http請(qǐng)求的模塊,用于下載網(wǎng)頁;
urllib.parse: 解析url網(wǎng)站的模塊;
logging: 記錄爬蟲日志;
leveldb: Google的Key-Value數(shù)據(jù)庫,用以記錄url的狀態(tài);
farmhash: 對(duì)url進(jìn)行hash計(jì)算作為url的唯一標(biāo)識(shí);
sanicdb: 對(duì)aiomysql的封裝,更方便的進(jìn)行數(shù)據(jù)庫mysql操作;
2. 異步爬蟲實(shí)現(xiàn)的流程
2.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.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)址池。
2.3 數(shù)據(jù)庫
這里面用到了兩個(gè)數(shù)據(jù)庫:MySQL和Leveldb。前者用于保存hub網(wǎng)址、下載的網(wǎng)頁;后者用于存儲(chǔ)所有url的狀態(tài)(是否抓取成功)。
從網(wǎng)頁提取到的很多鏈接可能已經(jīng)被 ? zhua取過了,就不必再進(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?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)
循環(huán)二: 抓取網(wǎng)頁的循環(huán)
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?span?>?3: ??????????????????rate?=?counter?/?span?????????????????? ??????????????????print('\tloop_crawl2()?rate:%s,?counter:?%s,?workers:?%s'?%?(round(rate,?2),?counter,?self._workers)) ??????????????????last_rating_time?=?time.time() ??????????????????counter?=?0 ??????????????if?self._workers?>?self.workers_max:?????????????????? ??????????????print('======?got?workers_max,?sleep?3?sec?to?next?worker?=====') ??????????????????await?asyncio.sleep(3)
4. asyncio 要點(diǎn):
讀讀asyncio的文檔就可以知道它的運(yùn)行流程,這里分享一下使用時(shí)注意到的地方。
(1)使用loop.run_until_complete(self.loop_crawl())來啟動(dòng)整個(gè)程序的主循環(huán);
(2)使用asyncio.ensure_future() 來異步調(diào)用一個(gè)函數(shù),它相當(dāng)于多進(jìn)程的fork,gevent的spawn(),具體可以參考上述代碼。
Python
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。