作者:xlzd
鏈接:https://zhuanlan.zhihu.com/p/20432575
來源:知乎
著作權(quán)歸作者所有呢灶。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。
到目前為止彻亲,我們已經(jīng)可以編寫一些反爬蟲機制比較薄弱的網(wǎng)站爬蟲了堕担。不過,到上一篇博客結(jié)束座掘,我們抓到的數(shù)據(jù)依然還是存儲在文本文件中递惋。如此會存在一些不方便,比如不方便數(shù)據(jù)查找溢陪、刪除萍虽、更新,可能在第二次抓取的時候重復(fù)存儲等形真。這里杉编,介紹一下用數(shù)據(jù)庫存儲爬蟲數(shù)據(jù)。
由于爬蟲數(shù)據(jù)的結(jié)構(gòu)變化可能比較頻繁(不同時期網(wǎng)頁展示內(nèi)容可能豐富程度不一樣)咆霜,傳統(tǒng)的SQL型數(shù)據(jù)庫用來存儲的話會比較麻煩邓馒,所以我們更多的是使用NoSQL數(shù)據(jù)庫,這里以MongoDB為例蛾坯。
在數(shù)據(jù)抓取的過程中光酣,我們就應(yīng)該考慮好數(shù)據(jù)的表現(xiàn)形式。在大多數(shù)情況下脉课,爬蟲抓到的數(shù)據(jù)為以下兩種形式之一:
JSON文檔形式(如前一篇的拉勾招聘信息)挂疆,這種格式適用于絕大多數(shù)我們需要文本內(nèi)容的情況,我們將自己需要的數(shù)據(jù)的單條記錄表示為一批自定義key對應(yīng)網(wǎng)頁中特定value的k-v pair下翎,或者這樣的嵌套關(guān)系缤言。
文件形式,這種格式適用于抓取圖片資源视事、文檔資源等在瀏覽器端表現(xiàn)為二進制數(shù)據(jù)或需要下載的鏈接里面的數(shù)據(jù)胆萧。
首先介紹第一種格式吧。以上一篇博客的拉勾網(wǎng)為例,我們最后抓取到的數(shù)據(jù)跌穗,其實最后可以表達為以下形式(這里為了舉例只是展示少量字段):
{"jobTitle":"Python工程師","companyName":"奇虎360科技有限公司","salary":"20k-35k","city":"北京","workYear":"3-5年","positionId":1234566,}
上面關(guān)于JSON格式的描述的最后一句有點繞订晌,具體點的一個例子就是,比如我們抓取某個關(guān)鍵字對應(yīng)的百度搜索結(jié)果蚌吸,那么锈拨,我們得到的數(shù)據(jù)是這樣的(它不只是一個簡單的字符串關(guān)系的kv對,而是value也可能是一個object羹唠,如list或者dict):
{"keyword":"這是我們搜索的關(guān)鍵字","resultCount":"百度搜到了多少條信息","items":[# 我們搜到的信息的集合{# 單條信息"title":"標題","abstract":"信息摘要","releaseTime":"這條信息發(fā)布的時間","url":"這條信息的鏈接",},...],}
那么奕枢,對于上面拉勾招聘的數(shù)據(jù),我們能夠肯定的是佩微,這個招聘的id("positionId"字段)一定不會重復(fù)缝彬,于是,我們在將其存儲到數(shù)據(jù)庫的過程中哺眯,可以用positionId字段來唯一確定一條招聘信息谷浅,也即是主鍵。對應(yīng)到Python代碼奶卓,存儲到MongoDB的代碼示例如下:
importdatetimefrompymongoimportMongoClientMONGO_CONN=MongoClient(host,port)# MongoClient是線程安全的且內(nèi)部維護了一個連接池一疯,所以全局使用一個即可defsave(data):# data 表示需要存儲的單挑記錄(如上面的json,在Python中表現(xiàn)形式為dict)data['_id']=data['positionId']# 理由已經(jīng)解釋data['updateTime']=datetime.datetime.now()# 我們最好記錄下更新的時間# 其中夺姑,database墩邀、collection為你的數(shù)據(jù)庫及集合名稱MONGO_CONN['database']['collection'].update_one(filter={'_id':data['_id']},update={'$set':data},upsert=True)
我們可以通過如上方式將之前存儲到文件的代碼修改為存儲到數(shù)據(jù)庫中。pymongo是一個第三方庫瑟幕,所以你依然需要通過"pip install pymongo"來安裝它磕蒲。pymongo有很多操作MongoDB的API接口,這里不做過多講解只盹,你可以閱讀它的文檔或者在遇到問題的使用通過Google來解決辣往。
對于這樣的內(nèi)容,我們的抓取及存儲方式大概可以概括為:
選擇合適的格式保存
按照一定的規(guī)則確定不會重復(fù)保存兩條一樣的數(shù)據(jù)到庫中
寫爬蟲殖卑,并將存數(shù)據(jù)的接口實現(xiàn)為數(shù)據(jù)庫存儲方式
那么站削,對于存儲為文件的數(shù)據(jù)抓取,我們怎么做會更好一點呢孵稽?
其實還是要分情況考慮問題许起。如果你只是想簡單的抓取一批美女圖片(不管你想要健康或者是不健康的^_^),那么直接下載二進制流之后隨便取個名字存儲就好了菩鲜,比如隨機一串字符园细,或者使用文件內(nèi)容的數(shù)據(jù)摘要(MD5,sha1, ...),又或者是一個uuid接校,只要能確定不重復(fù)就夠了猛频,然后你就可以色瞇瞇地盯著這些圖片***了。
另一種情況下,比如這個文件是與一些如上所述的JSON數(shù)據(jù)掛鉤的鹿寻,比如說上面拉勾招聘的公司logo圖睦柴,這時候怎么考慮存儲問題更好呢?
這種情況下毡熏,我們要考慮到兩個問題:
不要重復(fù)存儲
可以方便地找回文件及其表示的含義
要達到這樣的目的坦敌,我們可以這樣:在得到文件的內(nèi)容后(一般為"requests.get('url').content"的值),我們?nèi)∷恼鎯槲募╩d5重復(fù)概率比較大痢法,建議使用sha1)狱窘,然后在MongoDB中維護一張collection——"fileMetaData",這個集合用于記錄對應(yīng)文件名(sha1)對應(yīng)的文件的content length疯暑、file type训柴、file path等信息哑舒。在招聘信息里增加一個字段“l(fā)ogo”妇拯,其值為fileMetaData中這條數(shù)據(jù)對應(yīng)的id即可(圖方便直接用文件的摘要做id)∠赐遥考慮到另一個問題是飞傀,如果文件數(shù)量較大(單個文件夾存儲數(shù)量有限)汉规,我們還可以將摘要的前幾位截取作為文件夾名。
小結(jié)
這里我們大概了解了如何通過數(shù)據(jù)庫保存我們爬到的數(shù)據(jù),以達到更方便檢索溉跃、更新、刪除的操作袖订。
有朋友私信說希望更新一部分關(guān)于反爬蟲機制的處理相關(guān)的內(nèi)容烂叔,這部分將會在后續(xù)更新,不過在這之前铲咨,先會有一篇關(guān)于較大數(shù)據(jù)量的抓取策略的探討躲胳。