1. 程式人生 > >【個人專案】基於scrapy-redis的股票分散式爬蟲實現及其股票預測演算法研究

【個人專案】基於scrapy-redis的股票分散式爬蟲實現及其股票預測演算法研究

前言

都說做計算機的,專案實踐是最能帶給人成長的。之前學習了很多的大資料和AI的知識,但是從來沒有自己做過一個既包含大資料又包含AI的專案。後來就決定做了個大資料+AI的分散式爬蟲系統。下面筆者會講述整個專案的架構,以及所用到技術點的些許介紹。

專案介紹

這個專案是筆者的個人專案,是基於scrapy-redis框架的股票分散式爬蟲框架實現。scrapy是個爬蟲框架,但只允許單機的,scrapy-redis是它的補充,能支援分散式的爬蟲爬取。完成爬蟲框架後,我想玩點AI的東西,就設計了兩個演算法,預測股票每日的開盤價是多少。

專案包含兩個部分:
1. 基於大資料知識的分散式爬蟲系統
2.基於人工智慧的預測系統(為什麼是預測呢?因為我爬取的資料是股票資料)

專案的難度適中,有一定的演算法實現(基於資料自己設計的),但總體要求不高。專案主要的特點是,用到的技術點很多,就是那種“這個用到那個用到,但就是不需要精通”,比如用到了Redis,Hadoop,Zookeeper,Hive等等,所以很適合大資料或AI專業的學生練手,因為可以學到很多模組。但對於有經驗的讀者來說,有興趣的話可以研究下預測股票的演算法,或者接著往下做。我實現了兩種演算法,但完成度都不高。

專案用到技術

技術點比較多,包含了大資料的部分框架和部分AI知識。
爬蟲:Python,Scrapy
大資料:Zookeeper,Hadoop,Hive,Redis
AI: 反向傳播神經網路,HTM神經網路,Kmeans演算法,部分數學。

專案github連結

https://github.com/Jiede1/spider-based-on-scrapy_redis-for-share-and-share-prediction-algorithm-search

專案架構

下面是專案的框架圖,包含3個部分。

這裡寫圖片描述

分散式爬蟲框架部分:

由三臺主機組成,這三臺主機組成一個Hadoop叢集,並且叢集的每一臺都安裝了scrapy,scrapy-redis元件,這些都是爬蟲的必要元件(除此之外,Master需要安裝Redis,所有的Slave都會訪問這臺Redis)。scrapy負責整個爬蟲的框架,Redis負責儲存爬蟲產生的資料,scrapy-redis的存在使得三臺機器可以基於Redis實現資料互動,也即爬蟲分散式的體現。

在叢集的Master上,放置了第一部分的爬蟲程式碼,負責基於scrapy的框架,由輸入的一個初始URL開始,通過爬蟲規則,得到爬蟲的目標URL,這裡稱之為download_url,並將之存到Redis。

在Slave1和Slave2上面,放置了爬蟲的第二部分程式碼,負責從Redis中取得download_url,然後下載資料並儲存到HDFS

這裡要介紹下scrapy框架。scrapy是個出名的python爬蟲框架,其實現的爬蟲程式碼由幾部分組成:爬蟲初始URL start_urls,爬蟲處理邏輯parse函式,爬蟲處理資料邏輯pipelines函式,以及爬蟲的各種各樣中介軟體,和擴充套件組成。後兩者的存在都是賦予爬蟲更多的功能,更高的穩定性。

筆者的start_urls是http://quote.eastmoney.com/stocklist.html#sh,東方財富網的頁面。

儲存部分:

資料的儲存筆者選擇HDFS,主要原因有二:
1. HDFS是分散式的。
2. 基於HDFS儲存,在後續的機器學習演算法開發,或許會有更多的API支援,比如Spark支援讀取HDFS為RDD。

但實際上,在我完成程式碼並測試的時候。發現利用HDFS作為儲存引擎並不好,因為資料儲存的功能是實現在爬蟲框架裡的,也就是說爬蟲程式碼在執行的時候,會多次且頻繁對HDFS進行讀寫。這設計到大量的記憶體互動和消耗,HDFS對頻繁的讀寫是非常不友好的,會嚴重拖低爬蟲速度。有多慢呢?就我的資料量來說,如果是儲存到本地,也就幾分鐘跑完了,但如果是儲存到HDFS裡,要跑好幾個小時。那酸爽!

所以,如果有讀者以後也要實現分散式爬蟲的框架,不要利用HDFS作為儲存引擎,而是使用一些分散式資料庫,比如Sequoiadb(國產資料庫,效能比mongodb好),monagodb(比前者出名,更多api支援)。

演算法預測部分

筆者爬取的資料是東方財富網的股票資料(並且是上海股票和深圳股票),資料大概長這樣
這裡寫圖片描述

資料在HDFS儲存也是長這樣,弄清楚了資料的結構,接下來的就是設計相應的演算法來預測了。筆者預測的是“開盤價”這一欄位。

筆者設計了兩個演算法:
1. BP神經網路+MSD相似度
2. HTM演算法+kmeans+MSD相似度

首先筆者先說明,這兩個演算法的效果都略顯尷尬,但演算法2確實還算不錯,但我只是隨便選了一兩條股票來預測然後plot出來看效果,沒有更多的實驗跟進;演算法1簡直就是災難,命中率幾乎為0,但演算法1我當時主要是想瞎搞一下,讀者看看筆者的思路就可,或許對你們有其他的啟發呢,也說不定。

首先介紹下MSD相似度:
傳統的相似度計算,比如餘弦,曼哈頓等等,對於股票資料來說,不是特別適用。判定兩隻股票是否類似,不止要看他們數值向量之間的差距,還需要看他們“形態”之間的差距。所以筆者查看了一些論文,然後發現了MSD相似度—-這種相似度既考慮了兩個序列的數值上的差異,也考慮了他們之間的“形態”上的差距。比如,從視覺上看,是否兩個序列的走勢是一樣的。

MSD的計算方式:
這裡寫圖片描述
其中, L i , L j 是兩條序列,長度都為n。
其實上面的公式等同於:
這裡寫圖片描述
D E u c l i d 是歐式距離,ASD是 L i L j 各維差值之和的絕對值,SAD是 L i L j 之間的曼哈頓距離。

MSD>=0,可大於1。越靠近0,兩條序列越相似。

有關MSD相似度,具體可以看連結:
https://wenku.baidu.com/view/58dfefbc2b160b4e777fcf77.html

還要介紹下HTM演算法
HTM演算法也是一種神經網路演算法,但並沒有BP神經網路或深度學習這樣出名。它的作用主要是用於時間序列的預測,以及異常檢測,並且表現良好。它最大的特色是:能夠記憶曾見過的模式,可以一邊學習,一邊預測,並且能夠自動糾錯,預測錯了能繼續學習,改正錯誤。
HTM演算法有個別名,叫Nupic演算法。其實Nupic是個HTM演算法的大整合,目前開源在Github上。
下面是Nupic的結構:
這裡寫圖片描述
Input的每個神經元對應著一個數值,整個Input就是一個向量。
這裡寫圖片描述
這一張圖是Nupic的單層結構,每一層的結構都如此。具體原理我就不介紹了,有興趣的讀者可以去看我的另外一篇部落格,裡面講述了Nupic演算法的原理。

好了,接下來會講述每一部分的技術要點。

專案分塊講解

分散式爬蟲框架

對於爬蟲系統,最開始的工作就是設計爬取系統,爬取系統包括了爬蟲spider,中介軟體,還有欄位處理的部分。每一部分都需要單獨處理。
這裡寫圖片描述
上圖是分散式爬蟲系統的框架圖,主要包含三個方面的工作:爬蟲,中介軟體的編寫,欄位處理。

爬蟲

爬蟲的工作包含設計爬取策略,這需要根據自己所要爬的資料來決定,比如,筆者爬取的是股票資料,那就應該根據股票網站的規則來爬取。下圖是筆者的爬蟲邏輯
這裡寫圖片描述
爬蟲開始時,首先往Master端的爬蟲程式碼扔進start_url —-http://quote.eastmoney.com/stocklist.html,然後得到目標頁總數以及所有的股票程式碼,然後基於爬蟲邏輯,通過正則表示式組合成最終的download_url,存進Redis。

值得注意的是,在scrapy框架中,原本start_url是在程式碼中加入的,但利用了scrapy-redis後,start_url放在Redis中就行,scrapy框架會自動到Redis讀取start_url

簡單介紹下scrapy-redis
scrapy-redis是scrapy框架的一個改善。以前scrapy框架中,存放帶爬取url的資料結構python自帶的資料結構collection.deque,但這玩意做不到分散式讀取,所以開發者就將之換成了從Redis讀取,並且改寫了scrapy框架中的Scheduler模組,使得框架真正能夠實現分散式運作了。

具體知識可學習如下部落格:
https://m.aliyun.com/jiaocheng/524664.html
https://segmentfault.com/a/1190000014333162?utm_source=channel-hottest
https://blog.csdn.net/hjhmpl123/article/details/53292602

筆者的爬蟲邏輯可以基於Github上Master端程式碼和Slave端程式碼來看,爬蟲邏輯放在這兩個資料夾的Spider目錄下。

關於去重

去重的任務是:對於需要進行儲存到Redis的URL,進行去重檢測,這樣是為了提升效率。其實scrapy框架本身就會對URL進行去重檢測,但筆者還是在程式碼裡實現了一個去重邏輯,其實跟scrapy的去重原理一樣。
這裡寫圖片描述

關於異常處理和速度控制

筆者個人的異常處理非常簡單粗暴。先貼出一段異常處理的程式碼。
這裡寫圖片描述
筆者是在將近所有的處理邏輯的外面,就給它套一個try…except模組,有異常就報錯,而不管異常是什麼。

其實最好的異常處理是,在每個邏輯前面,都加一個異常處理模組,然後列印對應的錯誤資訊(不過這種情況比較適合處理邏輯比較複雜的)。

速度控制的原理是,控制爬蟲的速度,以免訪問過快被網站ban了。
這裡寫圖片描述

中介軟體

在這裡要單獨說說中介軟體的概念。中介軟體是scrapy框架裡的一個概念,scrapy是可配置的,中介軟體就是配置的一部分。中介軟體的存在意義是,裝飾爬蟲的爬取邏輯,使爬蟲更穩健。

常有的爬蟲中介軟體有:
設定自動ip
設定user-agent,模仿不同的瀏覽器訪問。

其實還有其他中介軟體,我用到的就是上面兩個。至於更具體的介紹我就不說了,讀者可以自行百度這些知識,爬蟲中很常見的。

中介軟體的程式碼是在Master/Slave目錄下的middlewares.py

儲存

儲存利用HDFS實現。具體的不再描述。就是hdfs寫入讀入。

演算法預測

預測的序列是股票的開盤價序列。在這一章節,有興趣的讀者,可以不用過多關心演算法結果,而將注意力放在“演算法為什麼要這樣構建”上。演算法一共有兩個。

1.BP神經網路+MSD

之所以選擇BP神經網路,是為了和演算法2的HTM網路比較。HTM是我最近發現的一種預測演算法,適應性非常強。所以想對比測試下。

一般而言,神經網路的輸入就是目標序列本身,通過“輸入過去的序列,輸出當前時刻序列值”這樣的訓練模式,構建好網路。但筆者在這裡決定不這麼幹。

先說下我對股市的主觀想法,我一直假設股市的各只股票之間互相的變動,是有關係的。比如A股票的變動,會導致B,C股票的變動,各只股票組成一個互相聯絡的網路,而這在一定程度上,是可以被量化的。

上面這一段話決定了我如何構建我的演算法。我決定不將目標序列作為網路的輸入,而使用跟目標序列有最高相似度的多條序列作為輸入。比如我打算預測A股票,而又發現A股票跟 [B,C,D] 股的相似度很高,所以就用BCD股作為演算法的輸入,而輸出用目標序列本身。

那問題來了,是怎麼找到相似度最高的20條股票呢?BCD股是怎樣組合成一個輸入的呢?輸出又是怎樣的呢?

演算法利用MSD相似度計算尋找相似股票。對於兩隻股票,如果長度一致,且每一刻的時間對應,那麼,相似度就可求,也就可以找出20條相似度最高的股票。

演算法本身選了20條相似股票,即對於目標序列,演算法會尋找20條與之最為相似的股票作為相似股。之後演算法這樣構建輸入輸出:

輸入:對於這20只股票,選擇第t時刻的各個股票的對應數值,之後,這個數值除以與目標序列的MSD相似度數值,得到一個新的數值。最後,這20個新的數值組成一個長度為20的向量。

輸出:選擇第t+1時刻的目標股票的對應數值作為輸出

比如,A股為與B,C股的MSD相似度分別為0.8,1.2,那麼演算法在第 t 時刻的輸入為: [ V a l B ( t ) / 0.8 , V a l C ( t ) / 1.2 ] ,而輸出為 [ V a l A ( t + 1 ) ]

但有個難題,這個難題也是所有演算法落地時要碰到的問題:股票的長度並不一致。時間也不完全對應。比如,有些股票誕生的早,有些股票已經退股。這就導致了在序列的時間連續性有問題。
這裡寫圖片描述

2.HTM演算法+Kmeans+MSD

其實我將更多的精力放在了第2個演算法,在寫完第一個演算法的時候,我就猜測效果不會好。第1個演算法更多是一種對比的嘗試吧。第2個演算法才是我著重的,主要我是想學習下HTM演算法。

這個演算法也是用相似股票的概念,但這次,筆者利用Kmeans演算法對所有的股票序列進行了聚類。並且將Kmeans演算法中用餘弦距離求相似度的部分換為了MSD相似度。當然,在聚類的同時,還是會有股票時間對應不上的問題,因此就需要對Kmeans演算法進行一定的改動。

改動主要的思想如下:
由於在比較兩條序列的時候,需要兩個序列等長且時間對應,因此毫無疑問需要考慮資料填充,筆者在實現的時候,在對應好時間的基礎上,確實是對序列短的進行了填充,以0填充。

在修改完這點之後,由於多條序列形成的DataFrame存在很多零值,所以在利用kmeans求取相似度的時候,也要有一定的技巧,在此筆者再次修改了kmeans演算法,採用了求取區域性相似度的方法,最終得出倆個序列的相似度。具體請參考程式碼,蠻易看懂的。

我原本的想法是,聚完類後(聚類數為3),然後將各個聚類分別扔給不同的HTM演算法,進行預測(這裡要說下HTM的輸入輸出是怎樣的?HTM接受的是序列中單個數值的輸入,輸出就是它對序列的預測值)。

但理想是豐滿的,現實是骨感的。在聚類的時候,我發現聚類效果不理想。聚類數為3,但最後只得到了兩類。而且兩類之間在數量上差別有點大。但即便這樣,我還是各自利用htm演算法分別做了訓練。(其實勉強來說這樣也可以,因為htm演算法的預測能力非常強勁)
這裡寫圖片描述

演算法結果

首先是神經網路的結果
這裡寫圖片描述

如上,命中率在50%左右,也即非常失敗了,傷心。不過在寫好演算法的時候就沒想著可以達成多高的命中率,而且演算法沒有任何調參,主要是專案時間不太夠了,就算了。有興趣者可以接著往下做。

然後是HTM的
這裡寫圖片描述

我將多條序列湊在一起,一併打印出來了,所以會看到x軸座標怪怪的,圖中有兩條線,一個是原始值,一個是預測值。實際上,預測效果還是蠻好的。

總結

好了,嘰嘰喳喳說完了,可能有點粗糙。這專案大概耗了我一個多月,之後太忙了就不想更進了。先記錄下來,有興趣的人可以試試上手,畢竟分散式的爬蟲還蠻好玩的,可以爬任何想爬的東西,而且在大資料裡也算是比較經典的一個主題。

謝謝大家!