這篇文章主要介紹如何使用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ù):
使用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):
到此為止,我們的股票爬蟲和數(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)境中焙蚓,這些模塊是必須要有的。