Python爬蟲開發(fā)(三):數(shù)據(jù)存儲以及多線程

0×00 介紹

本文我們就兩個方面來討論如何改進我們的爬蟲:數(shù)據(jù)存儲和多線程办素,當(dāng)然我承認這是為我們以后要討論的一些東西做鋪墊。

本人對于Python學(xué)習(xí)創(chuàng)建了一個小小的學(xué)習(xí)圈子沮焕,為各位提供了一個平臺,大家一起來討論學(xué)習(xí)Python叫搁。歡迎各位到來Python學(xué)習(xí)群:960410445一起討論視頻分享學(xué)習(xí)。Python是未來的發(fā)展方向瓢剿,正在挑戰(zhàn)我們的分析能力及對世界的認知方式,因此悠轩,我們與時俱進间狂,迎接變化,并不斷的成長哗蜈,掌握Python核心技術(shù)前标,才是掌握真正的價值所在坠韩。

目的:通常我們需要對爬蟲捕捉的數(shù)據(jù)進行分析距潘,處理,再次利用或者格式化只搁,顯然我們不能只是把爬蟲捕捉到的數(shù)據(jù)在內(nèi)存中處理音比,然后打印在屏幕上。在本章氢惋,我將介紹幾種主流的數(shù)據(jù)存儲方法洞翩。爬蟲處理數(shù)據(jù)的能力往往是決定爬蟲價值的決定性因素,同時一個穩(wěn)定的存儲數(shù)據(jù)的方法也絕對是一個爬蟲的價值體現(xiàn)焰望。

另外骚亿,采用多開線程的爬蟲,創(chuàng)造多個并行線程協(xié)調(diào)工作也絕對是提高爬蟲效率熊赖,降低失敗率的好辦法来屠。

0×01 引導(dǎo)

我們就接下來要講的部分做一個簡單的引導(dǎo),關(guān)于數(shù)據(jù)存儲方式:

1震鹉、?存儲索引或者直接下載數(shù)據(jù)

2俱笛、CSV

3、MySQL

關(guān)于線程:

如果讀者并不會python的線程處理传趾,可以參考這篇文章迎膜。

分為函數(shù)式和類包裝,這兩個方法進行線程處理浆兰。

0×02 數(shù)據(jù)存儲:存儲索引或者直接下載數(shù)據(jù)

關(guān)于這一點我覺得沒有必要做深入的解釋磕仅,因為這一點我們在前幾篇文章中或多或少都有接觸:比如制作sitemap:這里存儲了整個網(wǎng)站你需要的鏈接,比如抓取freebuff文章生成.docx文檔的這一節(jié)簸呈,這些其實都屬于本節(jié)所說的數(shù)據(jù)存儲方式榕订。那么就本節(jié)而言,我再介紹一個例子蝶棋,爬取一個freebuf商品列表區(qū)域所有的圖片(聽起來還是挺有趣的吧P读痢?)

步驟1:了解網(wǎng)站結(jié)構(gòu)

步驟2:編寫腳本

步驟3:測試

首先我們需要了解一下我們的目標(為了避免廣告嫌疑玩裙,這里還是以freebuf作為目標吧)

審查元素發(fā)現(xiàn)下面的div標簽包含了單個的商品信息兼贸,

Div(class=nall-news)->div(class=col-sm6col-md-lg-4mall-product-list)->div(class=photo)->a->img

這樣我們就輕松加愉快地找到了img所在的地方段直,那么根據(jù)這些,我們可以指定簡單的方案:獲取商品的所在的標簽溶诞,然后由于商品標簽的一致性鸯檬,我們可以一層一層索引下去找到圖片的位置,當(dāng)然有個不保險的辦法就是螺垢,獲取的直接獲取img喧务,(幸運的是,在這個例子中只存在一個img標簽)枉圃,我們測試從簡功茴,節(jié)約時間,那么一兩分鐘我們就寫出了自己的腳本:

importurllibfrombs4importBeautifulSoupimportreurl ='http://shop.freebuf.com/'print"prepare&reading to read theweb"data = urllib.urlopen(url).read()printdataprint"parsing ... ... ... "soup = BeautifulSoup(data)#<div class="col-sm-6 col-md-4col-lg-4 mall-product-list">itemlist =soup.findAll(name='div',attrs={'class':'col-sm-6 col-md-4 col-lg-4mall-product-list'})foriteminitemlist:printitem.img

這樣我們就在自己的debug I/O看到了打印出的九個img標簽:

然后我們用以前學(xué)到的技能孽亲,就足夠把這些圖片dump下來了坎穿,

完善腳本!

importurllibfrombs4importBeautifulSoupimportreurl ='http://shop.freebuf.com/'print"prepare&reading to read theweb"data = urllib.urlopen(url).read()printdataprint"parsing ... ... ... "soup = BeautifulSoup(data)#<div class="col-sm-6 col-md-4col-lg-4 mall-product-list">itemlist = soup.findAll(name='div',attrs={'class':'col-sm-6col-md-4 col-lg-4 mall-product-list'})foriteminitemlist:"""

為了適配圖片的格式返劲,我們這里這樣處理玲昧。

不過不是絕對的,某些時候這樣做就不合適:

"""???print item.img['src'][-4:]"""

urlretrieve這個方法是我們以前接觸過的篮绿,用于下載圖片孵延,還可以下載整個頁面:

"""urllib.urlretrieve(url=item.img['src'],filename=item.img['alt']+item.img['src'][-4:])

然后我們可以看一下成果,這樣做的好處就是避免下來一大堆無關(guān)的圖片亲配,(有些時候我們下載整站尘应,然后提取圖片會發(fā)現(xiàn)各種圖片混在一起了,那樣確實煩得很):

效果可以說是還不錯吧弃榨,當(dāng)然我懶并沒有把圖片建立文件夾存起來菩收。

0×03 數(shù)據(jù)存儲:CSV

CSV(comma-separated values),是目前比較流行的一種文件存儲格式。被Excel和很多的應(yīng)用程序支持鲸睛。CSV文件存儲的例子如下:

Fruit,cost

Apple,1.00

Banana,0.30

Pear,1.25

看起來就是表格的壓縮版娜饵,其實真的沒有什么奇怪的,這個很簡單的對吧官辈?當(dāng)然箱舞,大家都能想到這種方法存儲表格再好不過了。不過筆者在這里建議:如果你只有一個table要處理拳亿,復(fù)制粘貼應(yīng)該是比這樣快晴股,如果一堆table要處理,或者是要從各種數(shù)據(jù)中挑選出表格肺魁,然后組合成一張新表电湘,這樣無疑可以加快你的速度。

那么我們就舉一個例子來介紹一個下我們下一個例子。一定是一個有趣的體驗:

作為上一個例子的拓展:我們腰身成一個.csv文件寂呛,存儲每個商品的名稱和需要的金幣數(shù)怎诫。

我們觀察一下具體的金幣位置,商品信息都在哪里贷痪?筆者相信大家已經(jīng)看到了幻妓,那么接下來我們得先整理一下獲取info的辦法:

->div(class=info)

僅僅一步我們就可以得到信息位置。

Div(class=info)->h4->商品信息

Div(class=info)->p->strong->商品價格

那么我們這就很簡單了劫拢,對不對肉津?

在使用csv模塊時,打開一個文件然后把文件描述符傳入csv的writer然后寫入row舱沧,但是由于我們這里存在中文妹沙,要注意要utf-8處理一下,否則報錯或者是中文沒法正常顯示:

importurllibfrombs4importBeautifulSoupimportcsv?csvFile = open('items.csv','w+')?url ='http://shop.freebuf.com/'print"prepare&reading to read theweb"data = urllib.urlopen(url).read()printdataprint"parsing ... ... ... "soup = BeautifulSoup(data)#<div class="col-sm-6 col-md-4col-lg-4 mall-product-list">itemlist =soup.findAll(name='div',attrs={'class':'col-sm-6 col-md-4 col-lg-4mall-product-list'})?writer = csv.writer(csvFile)writer.writerow(('name','price'))foriteminitemlist:#name#print item.find(name="h4").stringname = item.find(name='h4').string#price#print item.find(name='strong').stringprice = item.find(name='strong').string???writer.writerow((name.encode('utf-8','ignore'),price.encode('utf-8','ignore')))?csvFile.close()print"success to create the csvfile"

那么最后我們得到了.csv文件狗唉,這個文件可以被excel打開的初烘,在我的linux中涡真,已經(jīng)被識別成了csv:

但是這里要注意中文編碼問題分俯。具體的解決辦法限于篇幅我就不介紹了。

0×04 MySQL

我們在這一部分改造上面的例子哆料,把MySQL整合在爬蟲中缸剪,至于MySQL的安裝配置我就不再解釋了,我們簡要梳理一下MySQL的py使用過程:

0东亦、提前建立好數(shù)據(jù)庫scraping杏节,建立好表items

1、引入pymysql庫

2典阵、通過數(shù)據(jù)庫參數(shù)建立一個鏈接conn,

3奋渔、Cur.conn.cursor()

4、Cur.execute(query)

5壮啊、如果需要的話還需要使用Cur.commit()

6嫉鲸、Cur.close()

7、Conn.close()

數(shù)據(jù)庫建立:CREATE DATABASE `scraping` DEFAULTCHARACTER SET utf8 COLLATE utf8_general_ci;

創(chuàng)建表:CREATE TABLE item(namevarchar(255),price int(11));

插入:INSERT INTO item (name,price) VALUES(‘test’,4);

刪除:DELETE FROM item WHRER name=’test’

稍微修改一下上面的代碼就可以簡單適配MySQL了歹啼。

importurllibfrombs4importBeautifulSoupimportpymysql?conn =pymysql.connect(host="127.0.0.1",user='root',passwd='toor',db='mysql')cur = conn.cursor()cur.execute('USE scraping')url ='http://shop.freebuf.com/'print"prepare&reading to read theweb"data = urllib.urlopen(url).read()printdataprint"parsing ... ... ... "soup = BeautifulSoup(data)#<div class="col-sm-6 col-md-4col-lg-4 mall-product-list">itemlist =soup.findAll(name='div',attrs={'class':'col-sm-6 col-md-4 col-lg-4mall-product-list'})foriteminitemlist:#name#print item.find(name="h4").stringname = item.find(name='h4').string#price#print item.find(name='strong').stringprice = item.find(name='strong').string???query ='insert item(name,price) value('+"\'"+name.encode('utf-8','ignore') +"\',"+price.encode('utf-8','ignore') +');'cur.execute(query)???conn.commit()print"success to update thedatabase"print"preparing to read the data fromdatabase!"query ="Select * from item where1;"cur.execute(query)printcur.fetchall()?cur.close()conn.close()

當(dāng)然上面的代碼只是展示最簡單的用法而已玄渗,我們還需要弄清楚的是編碼問題,數(shù)據(jù)庫還需要配置狸眼,要知道畢竟數(shù)據(jù)庫的使用如果處理不好的話藤树,也是一個不大不小的問題。

0×06 嵌入式數(shù)據(jù)庫BerkeleyDB(BDB)

關(guān)于介紹我不想太官方拓萌,那么簡單來說岁钓,筆者對BDB的認識如下:

BDB是oracle的一個輕量型嵌入式數(shù)據(jù)庫,只支持key-value數(shù)據(jù)形式存儲,介于內(nèi)存數(shù)據(jù)庫和硬盤數(shù)據(jù)庫之間屡限,是單寫入多讀取的相當(dāng)好的解決方案降宅。

BDB的python接口,bsddb模塊囚霸,可以把BDB的數(shù)據(jù)庫讀寫操作作為一個數(shù)組來進行腰根。查閱python2手冊可以找到這個模塊,非常易于使用拓型。

筆者在這里建議额嘿,使用BDB來存儲url。筆者可以提出一個比較可行的方案劣挫,一個url的md5值作為key册养,url的值作為value,讀寫直接操作數(shù)據(jù)庫压固,簡化url管理球拦。

0×05 多線程

我們回顧sitemap爬蟲的時候,我們發(fā)現(xiàn)帐我,爬取一個相對比較小的網(wǎng)站(一百頁左右)的時候大概用了2分40秒(加上為了避免頻繁請求設(shè)置的延時)坎炼,顯然對于我們來說這是不夠的,我們當(dāng)然有很多辦法來加速拦键,但是筆者在這里并不建議修改源代碼中的url請求等待時間谣光。我們僅僅多開線程執(zhí)行就可以達到我們預(yù)期的效果,這是基本所有類似程序都會采用的方法芬为,但是實際的使用的時候萄金,可能會有各種問題:

1、同步問題

2媚朦、線程池管理

……

高效穩(wěn)定的線程管理是編寫多線程程序或者腳本的基礎(chǔ)氧敢。

預(yù)備知識:

Python的線程模塊Threading模塊,【鏈接

Python自制線程池:線程池是解決并發(fā)問題的有力武器询张,在多線程爬蟲設(shè)計中孙乖,我們?nèi)匀豢梢允褂眠@樣的方法改裝一下

理論:

我們采用多線程爬蟲的時候,要清楚這個過程:每個爬蟲爬到的數(shù)據(jù)都要匯總到一起瑞侮,然后處理的圆,然后再分配新任務(wù)到空閑的爬蟲。然后根據(jù)這樣的過程我們可以想到這樣的過程好像和master-slave模式類似半火,如果大家沒有接觸過這個東西也沒關(guān)系越妈,簡單來說,就是包工頭和工人的關(guān)系钮糖,包工頭負責(zé)整個小項目的統(tǒng)籌和任務(wù)派發(fā)梅掠,工人負責(zé)埋頭苦干酌住。

然后根據(jù)這樣的需求,我們初步設(shè)計一下這個多線程爬蟲系統(tǒng)應(yīng)該是怎么樣的酪我。

在開始之前,我們首先需要明白一個manager的最基礎(chǔ)職能:

1且叁、維護任務(wù)隊列

2都哭、派發(fā)任務(wù)

3、處理子線程返回的數(shù)據(jù)

這樣我們可以初步設(shè)想一下逞带,再主線程的循環(huán)中進行所有的操作欺矫。那么,我們就意識到了這個manager的重要性了展氓。那么就按照我們現(xiàn)在的想法穆趴,我們來整理以下這個多線程爬蟲的設(shè)計思路:

按著這個思路,筆者實現(xiàn)了兩套多線程爬蟲遇汞,一套是簡易未妹,不穩(wěn)定的版本,一套是相對穩(wěn)定的版本空入。為什么不是直接看第二套比較完整的呢络它?顯然第一個版本簡易不穩(wěn)定,但是易于大家理解架構(gòu)执庐,第二個版本相對穩(wěn)定酪耕,但是讀起來可能有點痛苦。

具體的代碼在Github轨淌。

因為單個腳本太長了300+行。

第二個版本看尼,為了項目管理方便也為了貼合我自己的習(xí)慣递鹉,我就使用了vs13作為開發(fā)和管理工具,然后代碼現(xiàn)在托管在github上藏斩,項目目錄如下:

參見Github鏈接躏结,如果讀者喜歡這個項目可以隨意fork或者賞star,筆者將會更有力氣維護這個多線程爬蟲狰域。當(dāng)然這是一個未完成的項目媳拴,但是現(xiàn)在功能基本是完整的,可以實現(xiàn)自己自定義線程數(shù)穩(wěn)定爬取固定域名下的網(wǎng)站sitemap兆览,如果需要爬取內(nèi)容的話屈溉,需要使用者自己去定義worker的分析部分,url處理我已經(jīng)替大家基本寫完了抬探,這個項目的目的實際就是作為一個scraper platform存在子巾,因此取名scraplat帆赢,但是筆者真的水平有限,趕著這篇文章之前完成了基本功能线梗,在今后的一段時間內(nèi)這個項目還會不斷完善椰于,實現(xiàn)動態(tài)網(wǎng)頁爬取,自動化網(wǎng)頁測試等高級接口仪搔。如果對爬蟲技術(shù)感興趣的讀者可以長期關(guān)注以下這個項目瘾婿,也歡迎大家在留言區(qū)寫下自己想要實現(xiàn)的功能。

0×06 總結(jié)與下章預(yù)告

本章我們討論了數(shù)據(jù)存儲和多線程爬蟲的實現(xiàn)烤咧,如果大家明白原理以后憋他,就可以自己設(shè)計出自己的多線程爬蟲甚至是分布式爬蟲。到現(xiàn)在位置髓削,我們手中的爬蟲才算是像模像樣竹挡。

但是這還是不夠,直到現(xiàn)在為止立膛,我們只能處理靜態(tài)的網(wǎng)頁揪罕,如果想要處理動態(tài)加載的網(wǎng)頁,(不知道有沒有好事的讀者曾經(jīng)試過爬取淘寶商品頁面宝泵,試過的朋友會發(fā)現(xiàn)傳統(tǒng)的方案是沒有辦法處理淘寶商品頁面的)好啰。還有我們有時候希望我們的爬蟲能真正的進入互聯(lián)網(wǎng),自由爬行儿奶,這些都是我們渴望解決的問題框往。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市闯捎,隨后出現(xiàn)的幾起案子椰弊,更是在濱河造成了極大的恐慌,老刑警劉巖瓤鼻,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秉版,死亡現(xiàn)場離奇詭異,居然都是意外死亡茬祷,警方通過查閱死者的電腦和手機清焕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來祭犯,“玉大人喜命,你說我怎么就攤上這事唱歧。” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵喳篇,是天一觀的道長焊切。 經(jīng)常有香客問我逻澳,道長,這世上最難降的妖魔是什么镰吵? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮挂签,結(jié)果婚禮上疤祭,老公的妹妹穿的比我還像新娘。我一直安慰自己饵婆,他們只是感情好勺馆,可當(dāng)我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著侨核,像睡著了一般草穆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上搓译,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天悲柱,我揣著相機與錄音,去河邊找鬼些己。 笑死豌鸡,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的段标。 我是一名探鬼主播涯冠,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼逼庞!你這毒婦竟也來了蛇更?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤赛糟,失蹤者是張志新(化名)和其女友劉穎派任,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體虑灰,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡吨瞎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了穆咐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡字旭,死狀恐怖对湃,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情遗淳,我是刑警寧澤拍柒,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站屈暗,受9級特大地震影響拆讯,放射性物質(zhì)發(fā)生泄漏脂男。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一种呐、第九天 我趴在偏房一處隱蔽的房頂上張望宰翅。 院中可真熱鬧,春花似錦爽室、人聲如沸汁讼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嘿架。三九已至,卻和暖如春啸箫,著一層夾襖步出監(jiān)牢的瞬間耸彪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工忘苛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蝉娜,地道東北人。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓柑土,卻偏偏與公主長得像蜀肘,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子稽屏,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容

  • pyspark.sql模塊 模塊上下文 Spark SQL和DataFrames的重要類: pyspark.sql...
    mpro閱讀 9,448評論 0 13
  • 線程 操作系統(tǒng)線程理論 線程概念的引入背景 進程 之前我們已經(jīng)了解了操作系統(tǒng)中進程的概念狐榔,程序并不能單獨運行坛增,只有...
    go以恒閱讀 1,635評論 0 6
  • 爬蟲爬取的數(shù)據(jù)要經(jīng)過數(shù)據(jù)存儲步驟存儲在磁盤上收捣,對一些數(shù)據(jù)量較小的項目,數(shù)據(jù)可以暫時以磁盤文件的形式存儲庵楷,如果數(shù)據(jù)量...
    zhile_doing閱讀 251評論 0 0
  • 最近由于優(yōu)化搜索功能罢艾,要把之前工程內(nèi)廢棄的UISearchDisplayController換成UISearchC...
    he15his閱讀 4,166評論 1 5