Python實現(xiàn)股票數(shù)據(jù)接口

這篇文章主要介紹如何使用Python爬取股票數(shù)據(jù)和實現(xiàn)數(shù)據(jù)接口歼冰。

源碼下載地址:https://github.com/Lonely7th/TsSpiderServer

1.定時抓取和解析數(shù)據(jù)

這次我們抓取的目標是網(wǎng)易財經(jīng)的股票板塊靡狞,我們首先分析一下鏈接http://quotes.money.163.com/trade/lsjysj_603088.html?year=2018&season=1。按照鏈接的格式隔嫡,我們拼接好股票代碼甸怕、年份和季度:

url = "http://quotes.money.163.com/trade/lsjysj_" + key + ".html?year=" + year + "&season=" + season

拼接好鏈接后,使用requests庫獲取頁面的內(nèi)容:

requests.get(url)
self.parse_pager(content.content, item["code"])

考慮到網(wǎng)絡(luò)請求可能會失敗腮恩,我們在請求失敗時設(shè)置多次重新請求(最多8次)梢杭,如果多次請求后仍然失敗,則將請求的相關(guān)內(nèi)容存儲到error_logs中:

  # 請求失敗后重新請求(最多8次)
        max_try = 8
        for tries in range(max_try):
            try:
                content = requests.get(url)
                self.parse_pager(content.content, item["code"])
                break
            except Exception:
                if tries < (max_try - 1):
                    sleep(2)
                    continue
                else:
                    add_error_logs("crawl_error", "501", key)

獲取到頁面內(nèi)容后秸滴,我們先來分析頁面結(jié)構(gòu)(圖1)武契,我們需要的數(shù)據(jù)大概是以這樣的格式存在的:tr標簽表示股票某一天的行情,tr標簽下的td標簽表示當前行情的詳細數(shù)據(jù):

圖1

使用BeautifulSoup庫對頁面進行解析缸榛,soup.select("div.inner_box tr")會以列表的形勢返回div.inner_box下的所有tr標簽:

soup = bs4.BeautifulSoup(content, "lxml")
parse_list = soup.select("div.inner_box tr")

[x.string for x in item.select("td")]會將tr標簽下的內(nèi)容組合成一個數(shù)組data吝羞,這個數(shù)組就是我們要抓取的數(shù)據(jù):

data = [x.string for x in item.select("td")]

每次解析頁面時,我們都會從數(shù)據(jù)庫中取出當前股票已經(jīng)存在的數(shù)據(jù)内颗,用于判斷待插入數(shù)據(jù)是否已經(jīng)存在數(shù)據(jù)庫中钧排。這樣做可以及時補全數(shù)據(jù),并且避免數(shù)據(jù)重復(fù)插入均澳。

 if price["cur_timer"]not in timer_list:
     self.dm.add_tk_item(key, price)

由于股票數(shù)據(jù)是頻繁變動的恨溜,這就要求我們定時對數(shù)據(jù)進行更新,這里我們編寫一個定時器來實現(xiàn)定時更新數(shù)據(jù)的功能:

timer = threading.Timer(time_interval, fun_timer) 
timer.start()

我們設(shè)置每天16點更新數(shù)據(jù):

if (hour =="16" or hour =="20")and minute =="00":
     dc = ENDataCrawl()
     dc.start_crawl()
     sleep(time_interval)
     rm = RedisManager()
     rm.update_data()

2.存儲數(shù)據(jù)到MongoDB

這里我們使用MongoDB來存儲數(shù)據(jù)找前,MongoDB作為一個面向文檔存儲的數(shù)據(jù)庫糟袁,操作起來相對比較簡單和容易。在編寫代碼之前躺盛,我們需要先進行安裝 MongoDB安裝教程项戴,此外python操作MongoDB需要用到pymongo庫,命令行下輸入pip install pymongo安裝即可槽惫。

安裝完成后周叮,我們開始編寫MongoDB相關(guān)的代碼,新建DBManager類用于管理數(shù)據(jù)庫相關(guān)操作:

class DBManager:
    def __init__(self, table_name):
        # 指定端口和地址
        self.client = MongoClient(mod_config.get_config("database", "dbhost"), int(mod_config.get_config("database", "dbport")))
        # 選擇數(shù)據(jù)庫
        self.db = self.client[mod_config.get_config("database", "dbname")]
        self.table = self.db[table_name]

在DBManager類中界斜,我們最常用到的有add_tk_item方法仿耽,這個方法會根據(jù)tk_code(股票代碼),將最新的數(shù)據(jù)插入到price_list中各薇。

def add_tk_item(self, tk_code, price_item):
        return self.table.update_one({'code': tk_code}, {"$push": {"price_list": price_item}})*

以及find_by_id方法项贺,這個方法會根據(jù)tk_code查詢相應(yīng)的股票信息。當我們需要對Cursor進行長時間循環(huán)遍歷時,應(yīng)該將no_cursor_timeout設(shè)置為true开缎。

def find_by_id(self, tk_code, request={}):
       if tk_code:
           request["code"] = tk_code
           return self.table.find_one(request)
       else:
           # 數(shù)據(jù)量較大時避免CursorNotFoundException
           return self.table.find({}, no_cursor_timeout=True)*

3.緩存數(shù)據(jù)到Redis

為了提升響應(yīng)速度棕叫,我們使用Redis對數(shù)據(jù)進行緩存,redis作為一個key-value存儲系統(tǒng)啥箭,具有極高的性能谍珊。跟之前一樣我們需要先安裝Redis Redis安裝教程,然后為python安裝redis庫急侥,使用pip install redis命令砌滞。

接下來我們創(chuàng)建RedisManager類用于管理Redis的相關(guān)操作:

class RedisManager:
    def __init__(self):
        self.pool = redis.ConnectionPool(host=mod_config.get_config("redis", "redis_host"), port=mod_config.get_config("redis", "redis_port"), decode_responses=True)
        self.r = redis.Redis(connection_pool=self.pool)

update_data方法用于將MongoDB的數(shù)據(jù)同步到Redis,每次系統(tǒng)執(zhí)行完爬取業(yè)務(wù)后都會調(diào)用該方法:

    def update_data(self):
        # 將mongodb中的數(shù)據(jù)同步到redis中
        dm = DBManager("tk_details")
        code_list = dm.find_by_id("")
        for item in code_list:
            try:
                code = item["code"][:6]
                _result = dm.find_by_id(item["code"])
                sorted_result = sorted(_result["price_list"], cmp=cmp_datetime, key=operator.itemgetter("cur_timer"))
                self.r.set(code, sorted_result)
            except Exception:
                add_error_logs("redis_error", "501", item["code"])
                continue

4.配置Nginx和數(shù)據(jù)接口

由于我們只有一個簡單的數(shù)據(jù)接口坏怪,所以選擇使用Nginx贝润,Nginx 作為一個高性能的 Web 和反向代理服務(wù)器,具有簡潔高效铝宵,占用資源少等優(yōu)點打掘。考慮到很多開發(fā)者習(xí)慣在Windows下調(diào)試代碼鹏秋,我們先在Windows系統(tǒng)中安裝Nginx windows下安裝nginx(Windows下Nginx是以應(yīng)用的形式運行的尊蚁,這可能也是很多人不愿意在Windows下運行Nginx的原因)。

配置好Nginx后我們開始編寫數(shù)據(jù)接口侣夷,start_api_tkdata方法會開啟一個監(jiān)聽横朋,用于響應(yīng)Nginx的請求:

def start_api_tkdata(): 
    WSGIServer(myapp, bindAddress=(mod_config.get_config("server", "server_host"),         int(mod_config.get_config("server", "tk_data_port")))).run()

myapp方法每次收到請求時,都會對請求的格式和參數(shù)進行校驗百拓,校驗通過后則從Redis中獲取數(shù)據(jù)以json格式返回琴锭。

start_response('200 OK', [('Content-Type', 'text/plain')])
result_json["data"] =str(result).replace("u'", "'")
result_json["tk_code"] =str(list_query[i +1])
return [json.dumps(result_json)]

編寫完數(shù)據(jù)接口后,我們在本機啟動Nginx衙传,在瀏覽器中輸入http://127.0.0.1:9002/tkdata?code=600008决帖,可以看到如下結(jié)果(圖2):

圖2

到此為止,我們的股票爬蟲和數(shù)據(jù)接口就已經(jīng)完成了蓖捶,我們還可以在現(xiàn)有的基礎(chǔ)上做一些優(yōu)化地回,例如:

1.爬取數(shù)據(jù)時使用多線程和多進程。

2.添加更多的數(shù)據(jù)接口俊鱼,添加均線刻像、Macd、Boll等指標數(shù)據(jù)亭引,這些數(shù)據(jù)可以由收盤價計算得到绎速。

3.添加數(shù)據(jù)檢測和日志管理模塊皮获,如果你打算將這套系統(tǒng)用在生產(chǎn)環(huán)境中焙蚓,這些模塊是必須要有的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市购公,隨后出現(xiàn)的幾起案子萌京,更是在濱河造成了極大的恐慌,老刑警劉巖宏浩,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件知残,死亡現(xiàn)場離奇詭異,居然都是意外死亡比庄,警方通過查閱死者的電腦和手機求妹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來佳窑,“玉大人制恍,你說我怎么就攤上這事∩翊眨” “怎么了净神?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長溉委。 經(jīng)常有香客問我鹃唯,道長,這世上最難降的妖魔是什么瓣喊? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任坡慌,我火速辦了婚禮,結(jié)果婚禮上型宝,老公的妹妹穿的比我還像新娘八匠。我一直安慰自己,他們只是感情好趴酣,可當我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布梨树。 她就那樣靜靜地躺著,像睡著了一般岖寞。 火紅的嫁衣襯著肌膚如雪抡四。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天仗谆,我揣著相機與錄音指巡,去河邊找鬼。 笑死隶垮,一個胖子當著我的面吹牛藻雪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播狸吞,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼勉耀,長吁一口氣:“原來是場噩夢啊……” “哼指煎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起便斥,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤至壤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后枢纠,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體像街,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年晋渺,在試婚紗的時候發(fā)現(xiàn)自己被綠了镰绎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡木西,死狀恐怖跟狱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情户魏,我是刑警寧澤驶臊,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站叼丑,受9級特大地震影響关翎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鸠信,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一纵寝、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧星立,春花似錦爽茴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至劲装,卻和暖如春胧沫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背占业。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工绒怨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谦疾。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓南蹂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親念恍。 傳聞我的和親對象是個殘疾皇子六剥,可洞房花燭夜當晚...
    茶點故事閱讀 43,472評論 2 348