python 爬虫系统_实战干货:从零快速搭建自己的爬虫系统
近期由于工作原因,需要一些數(shù)據(jù)來(lái)輔助業(yè)務(wù)決策,又無(wú)法通過(guò)外部合作獲取,所以使用到了爬蟲(chóng)抓取相關(guān)的數(shù)據(jù)后,進(jìn)行分析統(tǒng)計(jì)。在這個(gè)過(guò)程中,也看到很多同學(xué)爬蟲(chóng)相關(guān)的文章,對(duì)基礎(chǔ)知識(shí)和所用到的技術(shù)分析得很到位,只是缺乏快速的實(shí)戰(zhàn)系統(tǒng)搭建指導(dǎo)。
本文將簡(jiǎn)單歸納網(wǎng)頁(yè)爬蟲(chóng)所需要的基礎(chǔ)知識(shí),著重于實(shí)現(xiàn)一套完整可用的小型網(wǎng)頁(yè)爬取、分析系統(tǒng),方便大家在有需要時(shí),能夠快速搭建系統(tǒng),以用到實(shí)踐中去。
關(guān)于網(wǎng)頁(yè)爬蟲(chóng)的定義和用途,想必做技術(shù)的都有所了解,這里就不再贅述。目前, 大家使用爬蟲(chóng)的目的除搜索引擎屬于無(wú)差別爬取外,其他多用于垂直領(lǐng)域或特定網(wǎng)站內(nèi)容的爬取,本文以特定網(wǎng)站內(nèi)容爬取作為切入點(diǎn),當(dāng)然,也可以應(yīng)用于垂直領(lǐng)域。
一套合格的網(wǎng)頁(yè)爬取、分析系統(tǒng),大致分為:網(wǎng)頁(yè)抓取、網(wǎng)頁(yè)分析與鏈接發(fā)現(xiàn)、任務(wù)去重與調(diào)度、數(shù)據(jù)預(yù)處理與存儲(chǔ)、防反爬蟲(chóng)策略、進(jìn)度展示等幾個(gè)重要方面。下邊逐一做簡(jiǎn)單歸納介紹。
一、基礎(chǔ)知識(shí)
(1)網(wǎng)頁(yè)爬取
網(wǎng)頁(yè)讀取,即讀取給定網(wǎng)頁(yè)的完整內(nèi)容,包含異步加載的內(nèi)容,也就是完整地呈現(xiàn)到瀏覽器窗口的內(nèi)容。
隨著智能手機(jī)的普及,網(wǎng)頁(yè)普遍分為 PC 端 和 移動(dòng)設(shè)備端,由于不同端的網(wǎng)速、流量、設(shè)備速度、屏幕大小等原因,移動(dòng)設(shè)備端多采用異步加載的方式來(lái)優(yōu)化用戶體驗(yàn),timeline 類型的無(wú)縫翻頁(yè)就是最佳的例子。這導(dǎo)致常用的 python requests, python urlib, wget, curl 等獲取到的網(wǎng)頁(yè)內(nèi)容不完整,只有網(wǎng)頁(yè)的骨架而無(wú)內(nèi)容,內(nèi)容需要等待 JS 異步加載。
這種問(wèn)題的解決,我們一般使用帶 JS 執(zhí)行引擎的瀏覽器驅(qū)動(dòng)來(lái)執(zhí)行網(wǎng)頁(yè)內(nèi)的異步加載 JS,解決異步加載問(wèn)題。常見(jiàn)的解決方案是 selenium 自動(dòng)化瀏覽器測(cè)試組件配合 chromedriver 或 firfoxdriver 這些有界面瀏覽器來(lái)使用,如果是 linux 服務(wù)器命令行下,則可配合 phantomjs 這款無(wú)界面瀏覽器。
python selenium 安裝:pip install selenium
這里附上簡(jiǎn)單的應(yīng)用示例代碼:from selenium import webdriver
browser = webdriver.Chrome() # 使用 ChromeDriver,需要安裝
browser.get("http://www.baidu.com")
browser.find\_element\_by\_id("kw").send\_keys("selenium")
browser.find\_element\_by\_id("su").click()
dir( browser ) # 查看所有屬性和方法
print browser.page\_source # 網(wǎng)頁(yè)源碼
browser.quit()
(2)網(wǎng)頁(yè)分析與鏈接發(fā)現(xiàn)
網(wǎng)頁(yè)分析,即將爬取到的網(wǎng)頁(yè)內(nèi)容進(jìn)行分析,提取需要的內(nèi)容。**鏈接發(fā)現(xiàn)**,即提取該網(wǎng)頁(yè)中需要進(jìn)一步爬取的 URI 地址,或者利用網(wǎng)頁(yè)內(nèi)信息構(gòu)建 URI 地址。
網(wǎng)頁(yè)分析所針對(duì)的內(nèi)容,大致分為:結(jié)構(gòu)化內(nèi)容(如 HTML 和 JSON)、半結(jié)構(gòu)化內(nèi)容(如一條含 JSON 的 JS 語(yǔ)句),非結(jié)構(gòu)化內(nèi)容(如純 txt)。(嚴(yán)格意義上說(shuō),結(jié)構(gòu)化內(nèi)容為固定的類似數(shù)據(jù)庫(kù)二維表一樣的內(nèi)容,這里僅針對(duì)網(wǎng)頁(yè)內(nèi)容做適當(dāng)?shù)姆诸愓{(diào)整)
針對(duì) HTML ,推薦使用 **pyquery** 進(jìn)行分析。pyquery 的使用非常簡(jiǎn)單,用于爬蟲(chóng)時(shí)也無(wú)需用到高級(jí)特性,常用方法如下例所示:from pyquery import PyQuery as pq
web = pq( "http://www.qq.com" )
print web("title").text() # 打印標(biāo)題
print web("span#guess").text() # 打印 WWWQQCOM 標(biāo)簽區(qū)域的文本
print web("span.undis").text() # 打印 騰訊網(wǎng) 標(biāo)簽區(qū)域的文本
print web('a.qqlogo').attr('href') # 打印 騰訊網(wǎng) 的連接內(nèi)容
針對(duì) JSON,可使用 python 原生的 **json** 模塊進(jìn)行分析。
針對(duì)半結(jié)構(gòu)化的內(nèi)容,則需要特定的分析,一般格式固定,如添加定長(zhǎng)的前綴和后綴,但此處無(wú)法通用,針對(duì)性強(qiáng),比如含有 JSON 內(nèi)容,只能固定暴力地將其提取出來(lái)再分析。
(3)任務(wù)去重與調(diào)度
主要是防止網(wǎng)頁(yè)的重復(fù)抓取,比如 A 中包含了 B 的地址,B 中又包含了返回 A 的地址,如果不做去重,則容易造成爬蟲(chóng)在 A 和 B 間死循環(huán)的問(wèn)題。但同時(shí)也要注意去重的時(shí)間窗口,無(wú)限期的去重將導(dǎo)致網(wǎng)頁(yè)內(nèi)容無(wú)法重新爬取被更新。調(diào)度是從系統(tǒng)特性的角度出發(fā),網(wǎng)頁(yè)爬取的主要耗時(shí)是在 網(wǎng)絡(luò)交互,等待一個(gè)網(wǎng)址進(jìn)行 DNS 解析、請(qǐng)求、返回?cái)?shù)據(jù)、異步加載完成等,需要幾秒甚至更長(zhǎng)的時(shí)間。小批量任務(wù)情況下,簡(jiǎn)單地使用多線程(thread)、多進(jìn)程(subprocess)都可以解決問(wèn)題,python 2.7,也可以使用 twitter 開(kāi)源的 tornado 框架內(nèi)的 coroutine 模塊做協(xié)程,python 3.4 本身也提供了異步 async 關(guān)鍵字。
(4)數(shù)據(jù)存儲(chǔ)與預(yù)處理、防反爬蟲(chóng)策略、進(jìn)度展示
數(shù)據(jù)預(yù)處理,即篩掉無(wú)用的內(nèi)容,并格式化有用數(shù)據(jù),降低存儲(chǔ)的壓力和數(shù)據(jù)大小,也方便后期分析處理。一般網(wǎng)頁(yè)抓取時(shí),需要的是展現(xiàn)在用戶面前的文字和圖片信息,而網(wǎng)頁(yè)內(nèi)的 css 樣式表、js 代碼等則不那么關(guān)心,這時(shí),同樣推薦使用 pyquery 進(jìn)行數(shù)據(jù)提取,簡(jiǎn)直方便好用(不過(guò) pyquery 存在一些小 bug,標(biāo)簽解析在特定情況下易被 '>' 打斷)。防反爬蟲(chóng)則可以搜索下"防爬蟲(chóng)""反爬蟲(chóng)"等關(guān)鍵字,看下實(shí)現(xiàn)原理,如果目標(biāo)網(wǎng)站有,進(jìn)行針對(duì)性破解即可,一般采用隨機(jī) User-Agent 和 降頻等策略都可以繞過(guò),掛代理?yè)Q IP 就會(huì)麻煩一些,不過(guò)大多數(shù)瀏覽器驅(qū)動(dòng)也都支持。
(5)數(shù)據(jù)展示
這是額外的說(shuō)明,爬取到數(shù)據(jù)后,進(jìn)行數(shù)據(jù)統(tǒng)計(jì)分析之后,是要用來(lái)輔助決策的,要展示給老板或產(chǎn)品看的,如何直觀地將成果展示出來(lái)呢?這時(shí)推薦使用 JS 的 Highcharts 組件進(jìn)行數(shù)據(jù)展示。github 上有 Highcharts 的 python 封裝,但使用起來(lái)比較麻煩,學(xué)習(xí)還需要耗費(fèi)不少時(shí)間,這里封裝了幾個(gè)常用圖表形式的簡(jiǎn)易 python 接口,如果需要其他類型的圖,按照 highcharts 的文檔進(jìn)行和已有代碼稍加擴(kuò)展即可擴(kuò)充,簡(jiǎn)單易用。(代碼整理上傳后貼鏈接)
二、常見(jiàn)爬蟲(chóng)實(shí)現(xiàn)
基礎(chǔ)知識(shí)介紹完后,我們來(lái)搭建實(shí)際的系統(tǒng)。不管是自己動(dòng)手,還是使用做好的框架或者產(chǎn)品,都需要知道自己的目的是什么,要達(dá)到什么樣的目的,如果想加深知識(shí)學(xué)習(xí),那無(wú)疑自己動(dòng)手做一套是最合適的,如果是需要快速完成工作,最好是使用現(xiàn)成的框架或產(chǎn)品。
(1)自己動(dòng)手
如果想自己開(kāi)發(fā)一個(gè)的話,作者也是支持的,簡(jiǎn)單開(kāi)發(fā)將基礎(chǔ)組件聯(lián)動(dòng)起來(lái),也可以完成任務(wù),雖然坑比較多,尤其是異常環(huán)節(jié)處理以及編碼問(wèn)題的解決。但話說(shuō)回來(lái),經(jīng)驗(yàn)不就是從踩過(guò)的坑中學(xué)習(xí)的嗎?如果想在這方面有所作為,自己寫或仿寫都是必不可少的學(xué)習(xí)途徑。
由于自己開(kāi)發(fā)的起點(diǎn)層次有很多,最底層的可以從自己建 TCP 鏈接解析 http 協(xié)議開(kāi)始,也可以從利用已有 http 開(kāi)發(fā)庫(kù)開(kāi)始(求別說(shuō)最底層應(yīng)該從寫操作系統(tǒng)或協(xié)議棧開(kāi)始。。。)。
常見(jiàn)的使用 python 開(kāi)發(fā)爬蟲(chóng)的**套路**:
**subrpocess/thread 做多進(jìn)程任務(wù)分發(fā) requests/selenium 網(wǎng)頁(yè)抓取 pyquery 網(wǎng)頁(yè)分析加鏈接生成 db 或 shelve 做結(jié)果存儲(chǔ) 自定義數(shù)據(jù)統(tǒng)計(jì)分析 matlab/highcharts 做報(bào)表圖。**
其中,網(wǎng)頁(yè)獲取可以用 urllib 來(lái)替代,pyquery 也可以用 beautifulsoup 或正則來(lái)替代,但這兩者都不推薦,用起來(lái)比 requests pyquery 麻煩。
db 常用的就是 sqlite,shelve 可以用來(lái)存儲(chǔ) python 對(duì)象,如果你的數(shù)據(jù)分析也是 python 腳本實(shí)現(xiàn),shelve 無(wú)疑可以降低不少解析時(shí)間。
matlab 做報(bào)表圖是畫(huà)報(bào)表后生成圖片格式。這里也建議使用 highcharts 來(lái)做報(bào)表,只是 highcharts 生成的結(jié)果是展示成網(wǎng)頁(yè)形式,動(dòng)態(tài)渲染。
在常見(jiàn)的**報(bào)表知會(huì)**場(chǎng)景中大致分為兩種:1、發(fā)定期郵件看走勢(shì);2、網(wǎng)頁(yè)展示。如果需要定期郵件,公司內(nèi)部有提供從 server 發(fā)送郵件/rtx 的工具,可以找運(yùn)維要一下。但是該工具限制無(wú)法直接發(fā)送圖片,通過(guò)將郵件做成 html 格式,將圖片轉(zhuǎn)為 base64 內(nèi)嵌進(jìn) html 即可。
那么如何將 **highcharts 生成的報(bào)表導(dǎo)出圖片**呢?新版本的 highcharts 有提供接口,但并不是很好用,因?yàn)槟愕膱?bào)表也不僅僅是一個(gè)圖,多個(gè)圖還要手工拼裝,根據(jù)郵件客戶端的不同,有可能展示的樣式也會(huì)有變化。 這里我們?nèi)匀豢梢允褂?phantomjs 來(lái)完成,原理就是使用瀏覽器對(duì)渲染后的頁(yè)面進(jìn)行整頁(yè)截圖。實(shí)現(xiàn)的原理也比較簡(jiǎn)單,使用 js 代碼,控制瀏覽器直接以圖片形式渲染網(wǎng)頁(yè),之后保存。由于該需求反響強(qiáng)烈,phantomjs 官網(wǎng)也提供了解決方案:http://phantomjs.org/screen-capture.html,即下載 rasterize.js,按照下面命令來(lái)執(zhí)行截圖。
這個(gè)命令的含義是使用 phantomjs 運(yùn)行 rasterize.js 渲染 my_html.html 并將結(jié)果保存到 tmp.png 中。
$ phantomjs rasterize.js ./my\_html.html ./tmp.png
生成截圖的過(guò)程中有**可能遇到的坑**,在這里也提一下,希望后來(lái)的同學(xué)不會(huì)再因?yàn)檫@個(gè)問(wèn)題浪費(fèi)時(shí)間:首先,控制 phantomjs 進(jìn)行截圖的時(shí)候,有可能截圖不完整,這是因?yàn)榫W(wǎng)頁(yè)有一個(gè)動(dòng)畫(huà)繪制的過(guò)程(如 highcharts 圖表頁(yè)),可以修改 rasterize.js 內(nèi)設(shè)置的默認(rèn) 200ms 的超時(shí)渲染時(shí)間到 5000ms 甚至更長(zhǎng),保證網(wǎng)頁(yè)加載完后再截圖。另外,在公司環(huán)境下,爬蟲(chóng)多部署在 server 端的 linux 系統(tǒng)下,服務(wù)器系統(tǒng)很少安裝字體文件,如果截圖出的內(nèi)容中文字缺失或跟本地預(yù)覽樣式不符,一般就是這個(gè)問(wèn)題了。
(2) scrapy
如果到百度或者谷歌上搜 python 爬蟲(chóng)關(guān)鍵字的話,你肯定會(huì)看到有不少人推薦使用 scrapy。scrapy 是不錯(cuò)的爬蟲(chóng)庫(kù),或者說(shuō)是爬蟲(chóng)框架,著重實(shí)現(xiàn)了上述的 網(wǎng)頁(yè)爬取、任務(wù)去重調(diào)度功能,也提供網(wǎng)頁(yè)內(nèi)容分析,不過(guò)是 xpath 的形式。其他方面,如 結(jié)果存儲(chǔ) 和 進(jìn)度展示 需要開(kāi)發(fā)者自己完成,也沒(méi)有提供簡(jiǎn)便的頻控防反爬蟲(chóng)策略的功能。
(3) pyspider
pyspider,是近幾年國(guó)人開(kāi)發(fā)的一款爬蟲(chóng)產(chǎn)品,之所以提升到產(chǎn)品級(jí)別,是因?yàn)樵摽蚣芴峁┝讼喈?dāng)完善的爬蟲(chóng)全流程的功能。從網(wǎng)頁(yè)爬取,到內(nèi)容分析,再到頻控,定時(shí)刷新,數(shù)據(jù)存儲(chǔ),分布式部署等,做得可圈可點(diǎn),且相當(dāng)易用,也是本文重點(diǎn)推薦的系統(tǒng)。
pyspider 簡(jiǎn)單的二次開(kāi)發(fā)接口,同時(shí)自帶了一個(gè)頁(yè)面開(kāi)發(fā)調(diào)試器。在實(shí)際的應(yīng)用中,配合 phantomjs 進(jìn)行頁(yè)面渲染獲取動(dòng)態(tài)加載數(shù)據(jù)非常方便。
這里的我們先看使用方法,體驗(yàn)一下 pyspider 的強(qiáng)大和易用,再來(lái)介紹該框架的架構(gòu)和實(shí)現(xiàn)方法。
安裝:
pip install pyspider
運(yùn)行:
pyspider
如果已安裝 phantomjs,則可使用 $ pyspider all 來(lái)配合使用。
訪問(wèn)、開(kāi)發(fā):
Create project 后,直接點(diǎn)擊 project ,即可進(jìn)入**頁(yè)面式的開(kāi)發(fā)調(diào)試環(huán)境**,非常方便。
我們以 douyu 的一個(gè)簡(jiǎn)單例子來(lái)介紹下**二次開(kāi)發(fā)代碼的含義**
之后點(diǎn)擊右上角 save 后,返回首頁(yè),修改 project status 和 rate/burst 后, 點(diǎn)擊 run 即可執(zhí)行:
關(guān)于rate/burst,這里是**采用令牌桶做的頻控**,這里設(shè)置 0.1/5 的含義是:rate = 0.1 每秒發(fā)起 0.1 個(gè)請(qǐng)求,即 10s 一個(gè)請(qǐng)求,耗費(fèi)一個(gè)令牌;burst = 5,最多并發(fā)發(fā)起 5 個(gè)請(qǐng)求,即耗費(fèi) 5 個(gè)令牌,那么也意味著并發(fā)后,第 6 個(gè)請(qǐng)求,要等待 50s。
另外,pyspider 安裝完即可用,默認(rèn)采用 sqlite 作為數(shù)據(jù)庫(kù),單機(jī)部署,使用本機(jī)的 phantomjs 和 xmlrpc。單機(jī)性能不足以支撐時(shí),也可以支持各模塊的分布式部署。如果需要分布式部署,就需要了解下 pyspider 的架構(gòu)情況,和基本的實(shí)現(xiàn)原理。
下面來(lái)簡(jiǎn)要地看一下pyspider 的架構(gòu):
從圖中可以看出,pyspider 主要的構(gòu)成模塊為 調(diào)度器 scheduler,網(wǎng)頁(yè)爬取 fetcher,數(shù)據(jù)預(yù)處理與鏈接發(fā)現(xiàn) processor,output 到數(shù)據(jù)庫(kù),還有 web 頁(yè)面的進(jìn)度監(jiān)控、運(yùn)營(yíng)。
結(jié)合上述談到的爬蟲(chóng)幾大塊,淺顯地看一下 pyspider 的實(shí)現(xiàn):
webui部分,使用 flask 模塊實(shí)現(xiàn)。
fetcher部分使用 tornado 的 gen 模塊內(nèi)的 coroutine 做協(xié)程,當(dāng) fetch_type = 'js' 的時(shí)候則鏈接 phantomjs 進(jìn)行數(shù)據(jù)的爬取,否則直接異步爬取。
processor處理階段,提供了 pyquery 解析對(duì)象 repsonse.doc,也可以直接訪問(wèn)頁(yè)面源碼進(jìn)行解析,鏈接發(fā)現(xiàn)需要用戶自己完成,pyquery 也提供了方便的接口 reponse.doc('a') 即可篩選出所有的 標(biāo)簽對(duì)象。
任務(wù)調(diào)度,pyspider 采用數(shù)據(jù)庫(kù)來(lái)存儲(chǔ)需要的任務(wù),taskid = md5sum( URL ) 為 primary key 保存每個(gè)任務(wù)鏈接上次執(zhí)行的時(shí)間以及更新時(shí)間,以此方式去重和篩選出可執(zhí)行的任務(wù),放入內(nèi)部執(zhí)行隊(duì)列中,由 fetcher 提取執(zhí)行。表結(jié)構(gòu)如下:CREATE TABLE `taskdb\_douyu\_pro` (
taskid PRIMARY KEY,
project,
url, status,
schedule, fetch, process, track,
lastcrawltime, updatetime
);
CREATE INDEX `status\_taskdb\_douyu\_pro\_index` ON `taskdb\_douyu\_pro` (status);
**數(shù)據(jù)存儲(chǔ)**,也是可配置的,但幾十萬(wàn)鏈接的小量,還是可以用 sqlite 來(lái)存儲(chǔ)。表結(jié)構(gòu)如下:CREATE TABLE `resultdb\_douyu\_pro` (
taskid PRIMARY KEY,
url,
result,
updatetime
);
三、一些遺留的問(wèn)題及小技巧
1、pyspider 使用 phantomjs 抓取頁(yè)面時(shí)發(fā)現(xiàn),當(dāng)請(qǐng)求量較大,會(huì)存在 phantomjs 有大量鏈接未關(guān)閉,從而停止響應(yīng)。沒(méi)有深入定位具體原因,采用暴力定時(shí)重啟 phantomjs 的方式來(lái)解決了,有遇到的同學(xué)可以深入定位一下。
2、另外,selenium phantomjs 是可以通過(guò)執(zhí)行 js 代碼來(lái)操作瀏覽器動(dòng)作的,所以遇到翻頁(yè)自動(dòng)加載的情況,可以尋找頁(yè)內(nèi)的 more 元素傳送 click() 事件。
3、如果目標(biāo)網(wǎng)站量較少,不妨試一下手機(jī)端的站點(diǎn),一般手機(jī)端站點(diǎn)為了優(yōu)化用戶體驗(yàn),都提供了異步加載功能,提供異步加載,則很大可能是使用 ajax 進(jìn)行 json 明文形式的查詢和結(jié)果返回,可以通過(guò) chrome 的 F12 或 safari 的響應(yīng)式設(shè)計(jì)模式,記錄請(qǐng)求 timeline,直接定位到網(wǎng)站自身提供的 restAPI 查詢接口,要比解析網(wǎng)頁(yè)事半功倍了。
總結(jié)
以上是生活随笔為你收集整理的python 爬虫系统_实战干货:从零快速搭建自己的爬虫系统的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 多目标粒子群优化算法_基于粒子群优化的投
- 下一篇: python3.7安装wordcloud