码农技术炒股之路——抓取股票基本信息、实时交易信息、主力动向信息
? ? ? ? 從本節(jié)開(kāi)始,我們開(kāi)始介紹各個(gè)抓取和備份業(yè)務(wù)。(轉(zhuǎn)載請(qǐng)指明出于breaksoftware的csdn博客)
? ? ? ? 因?yàn)槲覀償?shù)據(jù)庫(kù)很多,數(shù)據(jù)庫(kù)中表也很多,所以我們需要一個(gè)自動(dòng)檢測(cè)并創(chuàng)建數(shù)據(jù)庫(kù)和表的功能。在《碼農(nóng)技術(shù)炒股之路——數(shù)據(jù)庫(kù)管理器、正則表達(dá)式管理器》一文中,我們介紹了數(shù)據(jù)庫(kù)管理器幫我們自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù),但是沒(méi)有自動(dòng)創(chuàng)建表的功能。于是我們需要實(shí)現(xiàn)一個(gè)。
class prepare_table():def __init__(self, conn_name, table_template_name):self._conn_name = conn_nameself._table_template_name = table_template_nameself._create_table_format = ""def prepare(self, table_name):self._create_table_if_not_exist(table_name)def _get_table_template(self):file_path = "./conf/table_template/" + self._table_template_name + ".ttpl"if False == os.path.isfile(file_path):LOG_WARNING("can't read %s" %(file_path))returnfobj = open(file_path)try:self._create_table_format = fobj.read()except:self._create_table_format = ""LOG_WARNING("get table_template file error.path: %s" % (file_path))finally:fobj.close()def _create_table_if_not_exist(self, table_name):db_manager = mysql_manager()conn = db_manager.get_mysql_conn(self._conn_name)if False == conn.has_table(table_name):if len(self._create_table_format) == 0:self._get_table_template()if len(self._create_table_format) == 0:returnsql = self._create_table_format % (table_name)data = conn.execute(sql)conn.refresh_tables_info()
? ? ? ? 如果數(shù)據(jù)表不存在,prepare_table類根據(jù)傳入的模板名稱在配置文件中讀取創(chuàng)建表的SQL,然后執(zhí)行。如果存在,則什么都不做。
? ? ? ? 這個(gè)功能非常必要。因?yàn)槊刻於加行鹿僧a(chǎn)生,我們不能每天手工去創(chuàng)建和該股票相關(guān)的表。于是自動(dòng)檢測(cè)并創(chuàng)建可以幫我們省去很多麻煩。
股票基本信息
? ? ? ? 目前我保存的股票基本信息只有股票代碼、股票名稱和所在市場(chǎng)。由于不定期有新股上市,所以這個(gè)信息每天早上要第一個(gè)更新。之后業(yè)務(wù)會(huì)根據(jù)該表獲得所有股票代碼,然后才能進(jìn)行操作。
[update_share_base_info]
type=cron
class=update_stock_base_info
day_of_week=1-5
hour=9
minute=30
second=10
timezone = Asia/Shanghai
? ? ? ? 上面配置意思是每周一到周五,9點(diǎn)30分10秒獲取一次。
? ? ? ? 我們?cè)倏聪聞?chuàng)建表的模板(share_base.ttpl)
CREATE TABLE `%s` (`share_id` char(6) COLLATE utf8_unicode_ci NOT NULL DEFAULT '000000' COMMENT '股票代碼',`share_name` varchar(36) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '股票名稱',`market_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '市場(chǎng) 1 滬市 2 深市',PRIMARY KEY (`share_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='股票基本信息';
? ? ? ? 最后我們看下代碼。
? ? ? ? 在構(gòu)造函數(shù)中,我們指定所用到的數(shù)據(jù)庫(kù)連接配置為stock_db,表模板是share_base,表名是share_base_info
class update_stock_base_info(job_base):def __init__(self):self._regular_split_manager = regular_split_manager()self._db_manager = mysql_manager()self._conn_name = "stock_db"self._prepare_table = prepare_table(self._conn_name, "share_base")self._table_name = "share_base_info"
? ? ? ? 然后在run函數(shù)中檢查或新建表,并完成數(shù)據(jù)抓取和寫(xiě)入
def run(self):data = self._get_data()self._prepare_table.prepare(self._table_name)self._save_data(data)LOG_INFO("run update_stock_base_info")
? ? ? ? 數(shù)據(jù)我們從東方財(cái)富網(wǎng)抓取
def _get_data(self):url = r"http://nufm.dfcfw.com/EM_Finance2014NumericApplication/JS.aspx/JS.aspx?type=ct&st=(FFRank)&sr=1&p=1&ps=3500&js=var%20mozselQI={pages:(pc),data:[(x)]}&token=894050c76af8597a853f5b408b759f5d&cmd=C._AB&sty=DCFFITAM&rt=49461817"res = fetch_data.get_data(fetch_data.query_http(url))return res
? ? ? ? 然后通過(guò)正則表達(dá)式管理器,通過(guò)指定名稱的策略,將結(jié)果分解出來(lái)
def _save_data(self, data):data_array = self._regular_split_manager.get_split_data(data, "string_comma_regular")
? ? ? ? 然后枚舉上面結(jié)果,將相應(yīng)坐標(biāo)數(shù)據(jù)和數(shù)據(jù)庫(kù)中字段關(guān)聯(lián),最后寫(xiě)入表中
for item in data_array:share_market_type = item[0]share_id = item[1]share_name = item[2]if len(share_id) > 0 and len(share_name) > 0:share_info = {"share_id":share_id, "share_name":share_name, "market_type":share_market_type}conn = self._db_manager.get_mysql_conn(self._conn_name)conn.insert_onduplicate(self._table_name, share_info, ["share_id"])
? ? ? ? 有了之前介紹的一系列管理器,我們便通過(guò)不到40行代碼把數(shù)據(jù)抓取并入庫(kù)。我們看下抓取結(jié)果
股票實(shí)時(shí)交易信息
? ? ? ? 股票實(shí)時(shí)交易信息是保存在一個(gè)叫做daily_temp的數(shù)據(jù)庫(kù)中
class update_today_trade_info(job_base):def __init__(self):self._db_manager = mysql_manager()self._conn_name = "daily_temp"self._base_conn_name = "stock_db"self._prepare_table = prepare_table(self._conn_name, "today_trade_info")self._pre_share_count = 0self._share_ids_str_list = []
? ? ? ? 使用today_trade_info模板去準(zhǔn)備數(shù)據(jù)庫(kù)
CREATE TABLE `%s` (`seq_id` bigint(64) NOT NULL AUTO_INCREMENT COMMENT '自增ID',`share_id` char(6) CHARACTER SET latin1 NOT NULL DEFAULT '000000',`share_name` varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '股票名稱',`today_open` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '今日開(kāi)盤(pán)價(jià)',`yesterday_close` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '昨天收盤(pán)價(jià)',`cur` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '當(dāng)前價(jià)格',`today_high` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '今日最高',`today_low` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '今日最低',`compete_buy_price` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '競(jìng)買價(jià)格(買一價(jià)格)',`compete_sale_price` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '競(jìng)賣價(jià)格(賣一價(jià)格)',`trade_num` bigint(64) NOT NULL DEFAULT '0' COMMENT '成交的股票數(shù)',`trade_price` float(32,2) NOT NULL DEFAULT '0.00' COMMENT '成交的金額',`buy_1_num` bigint(64) NOT NULL DEFAULT '0' COMMENT '買一數(shù)量',`buy_1_price` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '買一價(jià)格',`buy_2_num` bigint(64) NOT NULL DEFAULT '0' COMMENT '買二數(shù)量',`buy_2_price` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '買二價(jià)格',`buy_3_num` bigint(64) NOT NULL DEFAULT '0' COMMENT '買三數(shù)量',`buy_3_price` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '買三價(jià)格',`buy_4_num` bigint(64) NOT NULL DEFAULT '0' COMMENT '買四數(shù)量',`buy_4_price` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '買四價(jià)格',`buy_5_num` bigint(64) NOT NULL DEFAULT '0' COMMENT '買五數(shù)量',`buy_5_price` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '買五價(jià)格',`sale_1_num` bigint(64) NOT NULL DEFAULT '0' COMMENT '賣一數(shù)量',`sale_1_price` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '賣一價(jià)格',`sale_2_num` bigint(64) NOT NULL DEFAULT '0' COMMENT '賣二數(shù)量',`sale_2_price` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '賣二價(jià)格',`sale_3_num` bigint(64) NOT NULL DEFAULT '0' COMMENT '賣三數(shù)量',`sale_3_price` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '賣三價(jià)格',`sale_4_num` bigint(64) NOT NULL DEFAULT '0' COMMENT '賣四數(shù)量',`sale_4_price` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '賣四價(jià)格',`sale_5_num` bigint(64) NOT NULL DEFAULT '0' COMMENT '賣五數(shù)量',`sale_5_price` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '賣五價(jià)格',`time_date_str` varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '更新時(shí)間(日期)',`time_str` varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '更新時(shí)間(時(shí)間)',`empty` int(16) NOT NULL DEFAULT '0' COMMENT '空占位',PRIMARY KEY (`seq_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='今日交易數(shù)據(jù)';
? ? ? ? 因?yàn)槲覀円獙⒁惶斓臄?shù)據(jù)放在一個(gè)表中,也就是說(shuō)每天一張表。于是表要使用日期加以區(qū)分
def run(self):date_info = time.strftime('%Y_%m_%d')table_name = "trade_info_%s" % (date_info)self._prepare_table.prepare(table_name)
? ? ? ? 下一步是數(shù)據(jù)獲取。
ids_str_list = self._get_all_share_ids()for ids_str in ids_str_list:data = self._get_data(ids_str)self._parse_data_and_insert_db(table_name, data)LOG_INFO("run update_today_trade_info")
? ? ? ? 這次我們通過(guò)新浪的接口獲取數(shù)據(jù)。由于該接口不能支持一次性拉取三千多支股票信息,所以在拉取前,我需要對(duì)拉取的股票代碼進(jìn)行切片。這次我們要使用到股票基本信息表的數(shù)據(jù)來(lái)進(jìn)行數(shù)據(jù)組裝。
def _get_all_share_ids(self):db_manager = mysql_manager()conn = db_manager.get_mysql_conn(self._base_conn_name)count_info = conn.select("share_base_info", ["count(*)"],{})count = int(count_info[0][0])if count == self._pre_share_count:return self._share_ids_str_list#share_ids = conn.select("share_base_info", ["share_id", "market_type"],{"share_id":["601375", "="]})share_ids = conn.select("share_base_info", ["share_id", "market_type"],{})ids = []for item in share_ids:market_type = item[1]pre = ""if 1 == market_type:pre = "sh"elif 2 == market_type:pre = "sz"else:continuenew_id = pre + item[0]ids.append(new_id)id_count = len(ids)count_per = 500page = id_count/count_perids_str_list = []for index in range(page+1):ids_list = ids[index*count_per:(index+1)*count_per]ids_str = "," . join(ids_list)ids_str_list.append(ids_str)self._share_ids_str_list = ids_str_listself._pre_share_count = countreturn self._share_ids_str_list
? ? ? ? 為了盡量減少不同批次股票同步時(shí)間的差異,需要每次盡量請(qǐng)求多的數(shù)據(jù)。我是按500個(gè)一批進(jìn)行切片的。
? ? ? ? 拉取數(shù)據(jù)的邏輯很簡(jiǎn)單,就是發(fā)起一次請(qǐng)求
def _get_data(self, ids):url_pre = "http://hq.sinajs.cn/list="url = url_pre + idsres = fetch_data.get_data(fetch_data.query_http(url))res = res.decode("gbk").encode("utf-8")return res
? ? ? ? 最后我們將數(shù)據(jù)通過(guò)正則表達(dá)式管理器按照hq_sinajs_cn_list策略進(jìn)行拆分。并通過(guò)指定相應(yīng)位和數(shù)據(jù)庫(kù)字段的關(guān)系,將其插入到表中
def _parse_data_and_insert_db(self, table_name, data):ret_array = fetch_data.get_data(fetch_data.regular_split("hq_sinajs_cn_list", data))if 0 == len(ret_array):LOG_WARNING("hg_sinajs_cn_list regular %s empty data" %(data))returndata_array = []into_db_columns = ["share_id","share_name","today_open","yesterday_close","cur","today_high","today_low","compete_buy_price","compete_sale_price","trade_num","trade_price","buy_1_num","buy_1_price","buy_2_num","buy_2_price","buy_3_num","buy_3_price","buy_4_num","buy_4_price","buy_5_num","buy_5_price","sale_1_num","sale_1_price","sale_2_num","sale_2_price","sale_3_num","sale_3_price","sale_4_num","sale_4_price","sale_5_num","sale_5_price","time_date_str","time_str","empty"]columns_count = len(into_db_columns)for item in ret_array:if len(item) != columns_count:LOG_INFO("%s length is not match for column length %d" %(str(item), columns_count))continuedata_array.append(item)if 0 == len(data_array):returndb_manager = mysql_manager()conn = db_manager.get_mysql_conn(self._conn_name)conn.insert_data(table_name ,into_db_columns, data_array)
股票主力動(dòng)向信息
? ? ? ? 主力動(dòng)向信息的獲取和實(shí)時(shí)交易信息獲取是類似的。我只列出區(qū)別部分。
? ? ? ? 主力動(dòng)向使用的是today_market_maker模板創(chuàng)建數(shù)據(jù)表
CREATE TABLE `%s` (`seq_id` bigint(64) NOT NULL AUTO_INCREMENT COMMENT '自增ID',`market_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '1 滬市 2 深市',`share_id` char(6) CHARACTER SET latin1 NOT NULL DEFAULT '' COMMENT '股票代碼',`share_name` varchar(36) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '股票名稱',`price` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '股票價(jià)格',`up_percent` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '漲幅',`market_maker_net_inflow` float(64,2) NOT NULL DEFAULT '0.00' COMMENT '主力凈流入',`market_maker_net_inflow_per` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '主力凈流入占比',`huge_inflow` float(64,2) NOT NULL DEFAULT '0.00' COMMENT '超大單凈流入',`huge_inflow_per` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '超大單凈流入占比',`large_inflow` float(64,2) NOT NULL DEFAULT '0.00' COMMENT '大單凈流入',`large_inflow_per` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '大單凈流入占比',`medium_inflow` float(64,2) NOT NULL DEFAULT '0.00' COMMENT '中單凈流入',`medium_inflow_per` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '中單凈流入占比',`small_inflow` float(64,2) NOT NULL DEFAULT '0.00' COMMENT '小單凈流入',`small_inflow_per` float(16,2) NOT NULL DEFAULT '0.00' COMMENT '小單凈流入占比',`time_str` varchar(64) CHARACTER SET latin1 NOT NULL DEFAULT '' COMMENT '時(shí)間',PRIMARY KEY (`seq_id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='主力行為統(tǒng)計(jì)';
? ? ? ? 這次數(shù)據(jù)是從東方財(cái)富網(wǎng)抓取的。一個(gè)不錯(cuò)的消息是,該接口支持三千多支股票同時(shí)抓取,于是避免了之前要做切片的問(wèn)題
def _get_data(self):date_info = time.strftime('%Y-%m-%d')url_fomart = "http://nufm.dfcfw.com/EM_Finance2014NumericApplication/JS.aspx/JS.aspx?type=ct&st=(BalFlowMain)&sr=-1&p=3&ps=%d&js=var%%20vDUaFOen={pages:(pc),date:%%22%s%%22,data:[(x)]}&token=894050c76af8597a853f5b408b759f5d&cmd=C._AB&sty=DCFFITA&rt=49430148"url = format(url_fomart % (3500, date_info))res = fetch_data.get_data(fetch_data.query_http(url))return res
? ? ? ? 唯一不好的是部分沒(méi)有的數(shù)據(jù)是使用-符號(hào)表示,這對(duì)我們正則分析提出了挑戰(zhàn)。但是我們可以靈活處理,在正則拆分前將其替換成0
data = self._get_data()data = data.replace("-,", "0,")self._parse_data_and_insert_db(table_name, data)LOG_INFO("run update_today_market_maker_info")
? ? ? ? 最后將拆分后的數(shù)據(jù)與表中字段關(guān)聯(lián),插入表中即可
def _parse_data_and_insert_db(self, table_name, data):data_array = fetch_data.get_data(fetch_data.regular_split("string_comma_regular", data))db_manager = mysql_manager()conn = db_manager.get_mysql_conn(self._conn_name)into_db_columns = ["market_type", "share_id", "share_name", "price", "up_percent", "market_maker_net_inflow", "market_maker_net_inflow_per", "huge_inflow", "huge_inflow_per", "large_inflow", "large_inflow_per", "medium_inflow", "medium_inflow_per", "small_inflow", "small_inflow_per", "time_str"]conn.insert_data(table_name ,into_db_columns, data_array)
? ? ? ? 實(shí)時(shí)交易和主力動(dòng)向數(shù)據(jù)都是30秒抓取一次,這兒只列出主力動(dòng)向的任務(wù)配置。它分為上午和下午,由于存在整點(diǎn)問(wèn)題,所以上午和下午的配置還是有點(diǎn)區(qū)別的
[update_today_market_maker_info_am_part1]
type=cron
class=update_today_market_maker_info
day_of_week=1-5
hour=9
minute=30-59
second = */30
timezone = Asia/Shanghai[update_today_market_maker_info_am_part2]
type=cron
class=update_today_market_maker_info
day_of_week=1-5
hour=10
second = */30
timezone = Asia/Shanghai[update_today_market_maker_info_am_part3]
type=cron
class=update_today_market_maker_info
day_of_week=1-5
hour=11
minute=0-30
second = */30
timezone = Asia/Shanghai
[update_today_market_maker_info_pm_1]
type=cron
class=update_today_market_maker_info
day_of_week=1-5
hour=13-14
second = */30
timezone = Asia/Shanghai[update_today_market_maker_info_pm_2]
type=cron
class=update_today_market_maker_info
day_of_week=1-5
hour=15
minute=0
second=30
timezone = Asia/Shanghai
總結(jié)
以上是生活随笔為你收集整理的码农技术炒股之路——抓取股票基本信息、实时交易信息、主力动向信息的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 码农技术炒股之路——任务管理器
- 下一篇: 码农技术炒股之路——实时交易信息、主力动