多線程爬取+MongoDB+Highcharts

第一次上kaggle來(lái)做實(shí)訓(xùn),第一印象界面美觀,向?qū)в押?,難怪有那么多人來(lái)推薦。數(shù)據(jù)集也很豐富,有關(guān)于歐洲足球的,美國(guó)總統(tǒng)競(jìng)選的,計(jì)算機(jī)語(yǔ)言使用調(diào)查的,人力資源分析,歷史上的飛機(jī)事故統(tǒng)計(jì),IMDB電影的數(shù)據(jù)分析,還有些脫敏的金融借貸信息。
找到了Titanics數(shù)據(jù)集,跟著向?qū)У谝淮巫鋈蝿?wù),datacamp中的課程有任務(wù)說(shuō)明,可以根據(jù)提示寫(xiě)代碼,然后提交,錯(cuò)誤還可以根據(jù)提示進(jìn)行修正,直到教會(huì)你為止。感覺(jué)和以前打一個(gè)新游戲的任務(wù)向?qū)Ш芟瘛?/p>

![RC1@DRW[75)(ADBLGPK_)Q.png計(jì)劃年底前搭建一個(gè)數(shù)據(jù)分析平臺(tái),運(yùn)用Python爬蟲(chóng)獲得數(shù)據(jù),存在MongoDB數(shù)據(jù)庫(kù)中,導(dǎo)出數(shù)據(jù)到SAS進(jìn)行數(shù)據(jù)挖掘,結(jié)果用HighCharts或者M(jìn)atplotlib做數(shù)據(jù)可視化展示,最后放在基于Django開(kāi)發(fā)的網(wǎng)站上。
在爬取內(nèi)容上可以先選擇招聘信息,房?jī)r(jià)信息,失信人名單(需要selenium)

初期目標(biāo)先不考慮數(shù)據(jù)挖掘部分,數(shù)據(jù)挖掘需要單獨(dú)研究,把平臺(tái)架構(gòu)完成,只包括爬取,存儲(chǔ)和展示。

需求:

  1. 多線程爬取58同城二手信息以及詳細(xì)內(nèi)容
  2. 存儲(chǔ)到MongoDB
  3. 運(yùn)用highcharts展示

思路

  1. 爬取
    分成兩部分,第一部分先把網(wǎng)站上各個(gè)分類(lèi)各種分頁(yè)的鏈接都爬取下來(lái),保存在數(shù)據(jù)庫(kù)中。第二部分逐一讀取每個(gè)鏈接,訪問(wèn)詳細(xì)頁(yè)信息并且爬取并存儲(chǔ)。

  2. 存儲(chǔ)
    選擇Mongodb數(shù)據(jù)庫(kù)是因?yàn)樗蛡鹘y(tǒng)數(shù)據(jù)庫(kù)相比更加符合OLAP的原則,是面向分析和維度的數(shù)據(jù)庫(kù),可以更好的對(duì)列進(jìn)行操作而不是行記錄,更加適合數(shù)據(jù)量大的分析工作。備份一個(gè)維度為10的20萬(wàn)條記錄大約5-8秒。

  3. 首先展示數(shù)據(jù)可視化無(wú)法在pycharm上進(jìn)行,必須用安裝jupyter來(lái)工作。然后,由于Highcharts是用jquery開(kāi)發(fā)的,所以有固定格式,必須把數(shù)據(jù)庫(kù)中的信息自動(dòng)生成列表形式才能展現(xiàn)。
    實(shí)現(xiàn)一個(gè)用圖表展示按區(qū)域統(tǒng)計(jì)發(fā)帖數(shù)量

代碼和結(jié)果:

  1. 爬取


    666.png

    888.png
  2. 存儲(chǔ)

555.png
777.png

3.展示

333.png
444.png

經(jīng)驗(yàn)與難點(diǎn):

  1. 爬取
    a). 爬取過(guò)程中發(fā)現(xiàn)有些商品已經(jīng)下架,造成信息無(wú)法正確提取,爬取中斷。

為了解決這個(gè)問(wèn)題需要判斷一下頁(yè)面上的一些提示信息,具體如下:
no_longer_exist = '商品已下架' in soup.select('.button_li')[0].text
if no_longer_exist:
print("商品已下架:" + url)
else:
codes to be executed

b). 爬取過(guò)程中發(fā)現(xiàn)其實(shí)這個(gè)網(wǎng)站經(jīng)過(guò)改版,同時(shí)存在兩種類(lèi)型的詳情頁(yè),一種是58轉(zhuǎn)轉(zhuǎn)的,一種是極個(gè)別的老的網(wǎng)頁(yè)。當(dāng)爬到老的網(wǎng)頁(yè)時(shí),由于元素位置和名稱(chēng)和轉(zhuǎn)轉(zhuǎn)結(jié)構(gòu)不一致造成報(bào)錯(cuò)。

為了排除這些錯(cuò)誤,在執(zhí)行代碼
加了try...except異常處理。
try:
codes to be executed
except(IndexError):
print("index error")

c). 當(dāng)爬取中斷的時(shí)候想斷點(diǎn)續(xù)爬,比如想爬10萬(wàn)條,爬了5萬(wàn)條遇到錯(cuò)誤中斷,想避免之前的重復(fù)爬取,而是接著5萬(wàn)條繼續(xù)爬取。

先訪問(wèn)數(shù)據(jù)庫(kù)中的鏈接數(shù)據(jù)列表,得到所有的鏈接全集內(nèi)容。
然后訪問(wèn)詳情頁(yè)中已經(jīng)爬取的鏈接列表。然后分別把他們放到集合中,再將兩個(gè)集合相減,由于集合相減做的是差運(yùn)算,得到的結(jié)果就是那些還沒(méi)有爬取的鏈接內(nèi)容,然后進(jìn)行爬取。
db_urls = [item["url"] for item in url_list.find()]
index_urls = [item["url"] for item in item_info.find()]
x = set(db_urls)
y = set(index_urls)
rest_of_urls = x - y
這邊用了一個(gè)循環(huán)列表表達(dá)式,據(jù)說(shuō)性能是原來(lái)for循環(huán)的10倍,所有的循環(huán)賦值語(yǔ)句都可以使用,是一個(gè)不錯(cuò)的提高性能的小技能。

d). 提高性能,使用多線程進(jìn)行操作

首先創(chuàng)建一個(gè)線程池對(duì)象,并且把處理器參數(shù)設(shè)置成等于PC的CPU個(gè)數(shù),注意過(guò)多或者過(guò)少設(shè)置處理器參數(shù)都不能最大化的優(yōu)化處理速度。然后使用map函數(shù)或者process函數(shù)對(duì)鏈接和詳細(xì)頁(yè)分別進(jìn)行爬取。
使用多線程技術(shù)以后,爬取速度是原來(lái)的200-300%,鏈接列表爬取每小時(shí)8萬(wàn)條,詳情頁(yè)大概每小時(shí)2萬(wàn)條。

如果還想進(jìn)一步提升性能,可以在解析網(wǎng)頁(yè)的時(shí)候不使用BeautifulSoup函數(shù),而是直接用lxml來(lái)解析,速度可以提升8-10倍。但是考慮網(wǎng)站負(fù)載問(wèn)題,速度和安全性相比,肯定還是需要更加安全小心的去爬取。

e). 由于這些二手發(fā)布網(wǎng)站的網(wǎng)站性能較好,沒(méi)有刻意的對(duì)爬蟲(chóng)進(jìn)行封鎖,但是為了安全起見(jiàn)還是應(yīng)該對(duì)Header,cookies以及agent進(jìn)行偽裝?;蛘呖梢赃M(jìn)行隨機(jī)更換proxy代理ip來(lái)訪問(wèn)網(wǎng)站。

headers = {'UserAgent':'Mozilla/5.0 (Windows NT 5.1; rv:37.0) Gecko/20100101 Firefox/37.0'}
proxy_list =
[
'http://117.177.250.151:8081',
'http://111.85.219.250:3129',
'http://122.70.183.138:8118',
]
proxy_ip = random.choice(proxy_list)
proxies = {'http':proxy_ip}

f). 還有些細(xì)節(jié),比如爬取訪問(wèn)量和購(gòu)買(mǎi)數(shù)量的時(shí)候會(huì)帶一些中文字符,而不是純數(shù)字,比如“201次瀏覽”,"【9圖】"等。
為了提取數(shù)字,可以使用
view = soup.select('.look_time')[0].text.split("次瀏覽")[0]
pic_num = title.split('圖')[0].split("【")[1]
這樣在爬取端就解決了數(shù)據(jù)格式的問(wèn)題,比在后期在數(shù)據(jù)庫(kù)端解決相對(duì)容易。

  1. 存儲(chǔ)
    a). 由于擔(dān)心誤操作,把辛辛苦苦哭花了10幾個(gè)小時(shí)爬取的數(shù)據(jù)給更新錯(cuò)誤,所以做任何數(shù)據(jù)庫(kù)操作之前需要對(duì)數(shù)據(jù)庫(kù)進(jìn)行備份。
222.png

b). Mongodb數(shù)據(jù)庫(kù)不是基于SQL語(yǔ)言進(jìn)行查詢的,而是面向?qū)ο蟮牟僮?,所有的操作都需要重新學(xué)習(xí)。
http://www.runoob.com/mongodb/mongodb-tutorial.html
詳情參見(jiàn)上述網(wǎng)頁(yè)

  1. 展示
    a). 首先是需要安裝charts庫(kù)和安裝網(wǎng)頁(yè)編輯器jupyter庫(kù)(這個(gè)東東可以代替pycharm進(jìn)行IDE開(kāi)發(fā)),然后要去配置jupyter服務(wù)器,并且登錄上。
    http://localhost:8888/tree#
    在命令行里輸入:jupyter notebook,自動(dòng)運(yùn)行網(wǎng)頁(yè)編輯器,然后新建一個(gè)python3文件。
    shift+enter是執(zhí)行
99.png

然后在網(wǎng)頁(yè)上可以直接生成圖表

aa.png

b). 由于highcharts是用jquery開(kāi)發(fā),所以有一定的格式需要遵循,意味著要進(jìn)行格式轉(zhuǎn)換。
讀取數(shù)據(jù)庫(kù)文件然后用程序來(lái)自動(dòng)生成。
def data_gen(types):
length = 0

if length <= len(area_index):

if length <= len(area_index):
    for area,times in zip(area_index,post_time):
        data={
            "name":area,
            "data":[times],
            "type":types
        }
        yield data
        length += 1

series = [data for data in data_gen("column")]
charts.plot(series, show='inline',options=dict(title=dict(text='statics')))

這里的難點(diǎn)是yield函數(shù),就是生成器函數(shù),這個(gè)是python比較中特殊的機(jī)制。
http://www.cnblogs.com/tqsummer/archive/2010/12/27/1917927.html
“生成器函數(shù)在Python中與迭代器協(xié)議的概念聯(lián)系在一起。簡(jiǎn)而言之,包含yield語(yǔ)句的函數(shù)會(huì)被特地編譯成生成器。當(dāng)函數(shù)被調(diào)用時(shí),他們返回一個(gè)生成器對(duì)象,這個(gè)對(duì)象支持迭代器接口。函數(shù)也許會(huì)有個(gè)return語(yǔ)句,但它的作用是用來(lái)yield產(chǎn)生值的。

不像一般的函數(shù)會(huì)生成值后退出,生成器函數(shù)在生成值后會(huì)自動(dòng)掛起并暫停他們的執(zhí)行和狀態(tài),他的本地變量將保存狀態(tài)信息,這些信息在函數(shù)恢復(fù)時(shí)將再度有效”
簡(jiǎn)而言之作用就是減少python的內(nèi)存開(kāi)銷(xiāo),更加高效快速地執(zhí)行運(yùn)算操作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容