01-3easyutils/__init__.py及stock.py

1.1.3-玩轉(zhuǎn)Python3金融API應(yīng)用-easyutils/init.py及stock.py和各種坑

init.py

看看easyutils文件夾內(nèi)的init.py文件源代碼:

<pre data-reader-unique-id="4" style="font-family: -apple-system-ui-monospaced, Menlo; font-size: 0.87em; line-height: 1.45em; max-width: 100%; white-space: pre-wrap;"># coding:utf8
from .timeutils import *
from .stock import *
version = '0.1.6' </pre>

也就是從自己模塊引入了timeutils子模塊的全部內(nèi)容绿店,又從自己模塊引入了stock子模塊的全部內(nèi)容骗爆。其版本是0.1.6版继榆。注意引用自己的子模塊時前面加上了一個“.”。

stock.py

stock.py子模塊源代碼詳解

這個子模塊的內(nèi)部內(nèi)容全部和股票相關(guān)嚷闭。讓我們把這一部分的代碼分成多個部分來看掩浙。

<pre data-reader-unique-id="26" style="font-family: -apple-system-ui-monospaced, Menlo; font-size: 0.87em; line-height: 1.45em; max-width: 100%; white-space: pre-wrap;"># coding:utf8

import re import datetime import requests import io </pre>

這個部分很基礎(chǔ),顯示了編碼用utf-8(python3程序很多時候不是太需要寫這一行)鲤脏,引入了正則表達式模塊re们颜,日期時間模塊datetime,常用爬蟲模塊requests以及處理輸入輸出流(I/O, Input/Output)的模塊io猎醇。

<pre data-reader-unique-id="46" style="font-family: -apple-system-ui-monospaced, Menlo; font-size: 0.87em; line-height: 1.45em; max-width: 100%; white-space: pre-wrap;">def get_stock_type(stock_code): """判斷股票ID對應(yīng)的證券市場
匹配規(guī)則
['50', '51', '60', '90','110'] 為 sh
['00', '13', '18', '15','16', '18', '20', '30', '39', '115'] 為 sz
['5', '6', '9'] 開頭的為 sh窥突, 其余為 sz
:param stock_code:股票ID, 若以 'sz', 'sh'開頭直接返回對應(yīng)類型,否則使用內(nèi)置規(guī)則判斷
:return 'sh' or'sz'"""
assert type(stock_code) isstr, 'stock code need str type'
if stock_code.startswith(('sh', 'sz')): return stock_code[:2] if stock_code.startswith(('50', '51', '60', '90', '110', '113', '132', '204')): return 'sh'
if stock_code.startswith(('00', '13', '18', '15', '16', '18', '20', '30', '39','115', '1318')): return 'sz'
if stock_code.startswith(('5','6', '9', '7')): return 'sh'
return 'sz' </pre>

這個函數(shù)定義的內(nèi)容就是前文提到的get_stock_type(stock_code)了硫嘶。這是用來判斷用戶提供的股票代碼(stock_code參數(shù))在哪個交易所上市的函數(shù)阻问。

從函數(shù)體中可以看到,注釋內(nèi)容之后先有一個assert xxx is yyy這樣的判斷句音半。在這個函數(shù)體中则拷,assert命令用于判斷stock_code的類型是不是str的贡蓖。如果不是的話曹鸠,返回'stock code need str type',也就是告訴使用者你輸入的stock_code不是字符串格式斥铺。assert命令很有意思彻桃,值得大家學(xué)習(xí)。

隨后的連續(xù)if判斷就是用來檢測輸入的股票代碼以判斷其所在的交易所了晾蜘。利用字符串的函數(shù)startwith來檢測stock_code的開始部分(用tuple傳入檢測的多種可能)邻眷,然后根據(jù)注釋中所提的內(nèi)容去安排返回什么值。

看到這里剔交,你可能就會有一定程度自己的思考了肆饶。比如,這里的if判斷或許中間寫成elif的判斷更好岖常。因為這里只是判斷一個stock_code的類別驯镊,而且if…elif…else的模式只要有一個成功了,后面的就會舍棄,理論上可以讓程序運行快一點板惑。

<pre data-reader-unique-id="171" style="font-family: -apple-system-ui-monospaced, Menlo; font-size: 0.87em; line-height: 1.45em; max-width: 100%; white-space: pre-wrap;">def get_code_type(code): """ 判斷代碼是屬于那種類型橄镜,目前僅支持 ['fund', 'stock']
:return str 返回code類型, fund 基金 stock 股票 """
if code.startswith(('00','30', '60')): return 'stock'
return 'fund' </pre>

這個函數(shù)定義的是get_code_type(code),即判斷證券是什么類別的金融資產(chǎn)冯乘。當(dāng)前僅支持股票’stock’和基金’fund’的區(qū)分洽胶。從函數(shù)體可以看到,如果代碼開頭是00裆馒、30或60的是股票姊氓,其余的代碼都是基金。**不過這里并沒有做到對板塊代碼喷好、B股他膳、新三板等證券的代碼進行區(qū)分,它會認(rèn)為B股也是基金……所以這里應(yīng)默認(rèn)是A股的股票绒窑。這一點的BUG只有看源代碼才能知道棕孙! **

<pre data-reader-unique-id="207" style="font-family: -apple-system-ui-monospaced, Menlo; font-size: 0.87em; line-height: 1.45em; max-width: 100%; white-space: pre-wrap;">def get_all_stock_codes(): """獲取所有股票ID""" all_stock_codes_url ='http://www.shdjt.com/js/lib/astock.js' grep_stock_codes =re.compile('~(\d+)`')
response =requests.get(all_stock_codes_url)
stock_codes =grep_stock_codes.findall(response.text) return stock_codes </pre>

這里是定義get_all_stock_codes(),也就是獲取所有的滬深兩市股票些膨、基金和板塊指數(shù)等代碼蟀俊。我運行這個代碼之后發(fā)現(xiàn)返回的列表并沒包括轉(zhuǎn)債的數(shù)據(jù)等。想自己運行代碼订雾,就把這一段拷貝到自己電腦上的python3里面并直接執(zhí)行就好了肢预。從函數(shù)體中可以看出,這里使用了爬蟲方法和正則表達式去提取想要的信息洼哎,最后返回的是“所有股票”代碼的列表烫映。

首先給出了爬取的網(wǎng)頁http://www.shdjt.com/js/lib/astock.js,網(wǎng)頁來自散戶大家庭這個網(wǎng)站的js網(wǎng)頁噩峦,這是通過抓包分析出來的锭沟。抓包方式請百度,主要通過各瀏覽器的開發(fā)者工具或者fiddler類專門的抓包工具识补。然后函數(shù)體描述了正則表達式匹配的樣式族淮。至于正則表達式,這是一個十分豐富且有用的內(nèi)容凭涂,可以直接百度進行學(xué)習(xí)祝辣。然后就用到requests.get(url)的方式來抓取網(wǎng)頁內(nèi)容,最后通過re模塊的findall功能對抓取到的網(wǎng)頁文本中的證券數(shù)字代碼進行分離切油,最終的stock_codes就是所有股票代碼匯集成的一個列表蝙斜。

截至2018年3月13日,stock_codes這個列表里面有5032個證券代碼澎胡,這就說明這里面的內(nèi)容除了00孕荠、30和60開頭的股票外绢片,還有B股、板塊指數(shù)等其他股票或其他非個股內(nèi)容岛琼。所以如果直接拿著這個列表里面的內(nèi)容去應(yīng)用get_code_type(code)函數(shù)的話底循,是可能會出錯的。

<pre data-reader-unique-id="240" style="font-family: -apple-system-ui-monospaced, Menlo; font-size: 0.87em; line-height: 1.45em; max-width: 100%; white-space: pre-wrap;">def round_price_by_code(price, code): """ 根據(jù)代碼類型[股票槐瑞,基金] 截取制定位數(shù)的價格
:param price: 證券價格
:param code: 證券代碼
:return: str 截斷后的價格的字符串表示 """
if isinstance(price, str): return price

typ = get_code_type(code) if typ == 'fund': return'{:.3f}'.format(price) return'{:.2f}'.format(price)  </pre>

這個函數(shù)定義round_price_by_code(price,code)熙涤,也就是針對不同的資產(chǎn)選取價格的小數(shù)位數(shù)。傳入兩個參數(shù)困檩,一個是price證券價格祠挫,另一個是code證券代碼。在國內(nèi)A股市場悼沿,股票都是2位小數(shù)等舔,而基金是3位小數(shù)的。這也是之前將證券代碼區(qū)分為股票或者基金的意義吧糟趾。

根據(jù)函數(shù)體的內(nèi)容慌植,函數(shù)首先判斷傳輸進來的價格(price)是不是str格式,如果是义郑,函數(shù)直接返回字符串的price蝶柿。如果不是字符串格式,則會跳過這個部分非驮。這里如果傳入的價格是字符串交汤,直接返回字符串形式的price可能不太好。萬一傳入的price位數(shù)出現(xiàn)較多的非正常情況劫笙,結(jié)果返回可能就不太正常了芙扎。

然后函數(shù)體通過get_code_type(code)內(nèi)容判斷當(dāng)前傳入的代碼是股票還是基金。這里如果只傳入A股部分的證券代碼就不會判斷出錯填大。判斷結(jié)果(即’stock’或’fund’)會賦值給typ這個變量戒洼。

函數(shù)體最后的部分是根據(jù)傳入的code類型(股票或者基金)來確定保留的小數(shù)位數(shù)。如果是基金栋盹,保留3位施逾;如果是不是敷矫,保留2位例获。這里保留位數(shù)用的是format命令,四舍五入曹仗,可以自行百度了解其功能榨汤。另外str(round(price , 2))也能起到相同的效果。

<pre data-reader-unique-id="280" style="font-family: -apple-system-ui-monospaced, Menlo; font-size: 0.87em; line-height: 1.45em; max-width: 100%; white-space: pre-wrap;">def get_ipo_info(only_today=False): import pyquery
   # 為了顯示方便換下行(肖西耶注)
response =requests.get('''http://vip.stock.finance.sina.com.cn/corp/go.php
         /vRPD_NewStockIssue/page/1.phtml''',headers={'accept-encoding': 'gzip, deflate, sdch'})
html =response.content.decode('gbk')

html_obj =pyquery.PyQuery(html)  
table_html =html_obj('#con02-0').html() import pandas as pd  
df =pd.read_html(io.StringIO(table_html), skiprows=3, converters={ '證券代碼': str, '申購代碼': str}  
)[0] if only_today:  
    today =datetime.datetime.now().strftime('%Y-%m-%d')  
    df = df[df['上網(wǎng)發(fā)行日期↓'] ==today] return df  </pre>

這里定義函數(shù)get_ipo_info(only_today=False)怎茫,初始參數(shù)only_today是False值收壕,通過閱讀函數(shù)體發(fā)現(xiàn)妓灌,這里only_today參數(shù)是用于確定爬取上網(wǎng)發(fā)行日期的日期是不是僅限于今天。如果這里函數(shù)不給任何參數(shù)蜜宪,那么就默認(rèn)為only_today為False虫埂,那么就會爬取全表所有將或已經(jīng)上網(wǎng)發(fā)行的股票IPO信息。關(guān)于股票IPO及打新的相關(guān)信息請自行百度圃验。

首先導(dǎo)入pyquery模塊掉伏。這個模塊能應(yīng)用CSS選擇器(selector)來獲取所需要的信息。類似CSS選擇器功能的模塊有l(wèi)xml澳窑,這是應(yīng)用Xpath來工作的模塊斧散。關(guān)于CSS和Xpath的語法,請各位百度進行學(xué)習(xí)摊聋,這和學(xué)習(xí)正則表達式一樣鸡捐,需要一段時間來記憶。

然后就是爬蟲套路requests.get麻裁,網(wǎng)址是新浪的:

http://vip.stock.finance.sina.com.cn/corp/go.php/vRPD_NewStockIssue/page/1.phtml

這里用了headers來獲取網(wǎng)頁信息箍镜。然而有的時候你直接把這個函數(shù)拿來嘗試的話,你會發(fā)現(xiàn)程序卡住了煎源。這說明一定是程序的問題嗎鹿寨?不一定!我此前有時候直接運行requests.get(url, headers)的話會卡住薪夕,曾經(jīng)一度百思不得其解脚草。后來我才想到我現(xiàn)在在美國……通過訪問國內(nèi)的云服務(wù)器,發(fā)現(xiàn)在位于國內(nèi)的云服務(wù)器上果然可以輕松打開這個程序原献。這就說明馏慨,這里如果不對源代碼進行測試的話,很可能我在用這個模塊的時候?qū)υ庥龅目D束手無策姑隅⌒戳ィ——因此在實際應(yīng)用這個包的時候,我不會使用這個函數(shù)讲仰。當(dāng)然慕趴,當(dāng)我回國之后我就可以使用這個函數(shù)了。

相較于上面的情況鄙陡,當(dāng)我回國之后冕房,如果我需要登錄境外網(wǎng)站時,很可能會遇到和現(xiàn)在一樣的問題(被墻了)趁矾。屆時可能我可能就需要搞一個位于境外的云服務(wù)器了耙册,或者選擇能夠直接打開的網(wǎng)址獲取數(shù)據(jù)……

接下來函數(shù)體對獲取的網(wǎng)頁內(nèi)容用'gbk'方式進行解碼,將網(wǎng)頁內(nèi)容解碼出中文毫捣。一些情況下详拙,bytes類型的字節(jié)流在python3中使用gbk是能解讀出中文的帝际,而utf-8是做不到的。

然后饶辙,利用pyquery的PyQuery類將html解析為css語法可以操作的內(nèi)容蹲诀,再通過css語法提取出網(wǎng)頁中的表格。(請自行學(xué)習(xí)css語法)

此時弃揽,引入pandas模塊并命名為pd侧甫。pandas模塊可以用來專業(yè)處理表格。利用pd.read_html蹋宦,配合io模塊的StringIO命令披粟,跳過3行,提取出證券代碼和申購代碼冷冗。提出的內(nèi)容是一個列表守屉。將列表序號為0的項賦值給df。

如果一開始傳入函數(shù)的only_today為False(無論是你賦值的False還是一開始沒有傳入?yún)?shù))蒿辙,那么函數(shù)體就會直接返回df拇泛。否則,當(dāng)only_today為True時思灌,函數(shù)體會獲取函數(shù)當(dāng)時運行的日期并通過strftime函數(shù)改成'%Y-%m-%d'格式(即類似’2018-03-13’)瑰艘,然后提取出df中關(guān)于函數(shù)體當(dāng)天的網(wǎng)上IPO信息啃炸,賦值給df最終返回钞瀑。

如果你能打開這個新浪的網(wǎng)址的話铸豁,你看到的返回信息量是很大的,類似這樣:

[圖片上傳失敗...(image-be8e8b-1584698534980)]

[圖片上傳失敗...(image-7db8b2-1584698534980)]

由于有時候我無法直接訪問這個新浪的網(wǎng)址耗跛,因此我需要自行解決這個問題裕照。

如果是我的話,我會嘗試使用東方財富的數(shù)據(jù)调塌,因為其數(shù)據(jù)在美國也是很方便就能夠打開的:

http://data.eastmoney.com/xg/xg/default.html

可以發(fā)現(xiàn)晋南,在用gbk編碼后,文本中defjson里面的data就是我們需要的數(shù)據(jù)负间。博主的正則功力很弱,所以就想了其他辦法姜凄,如果有大神,歡迎提供方案檀葛!最后遍歷打印用了制表符\t,這樣打印出來可以好看些屿聋。

我這里只考慮輸出股票名稱和申購日期:

<pre data-reader-unique-id="363" style="font-family: -apple-system-ui-monospaced, Menlo; font-size: 0.87em; line-height: 1.45em; max-width: 100%; white-space: pre-wrap;">def get_ipo_info(): # 從東方財富網(wǎng)新股日歷中爬取
r = requests.get('http://data.eastmoney.com/xg/xg/default.html') # 對于字節(jié)流,用gbk方式編碼
r.encoding = "gbk"
# 將編碼后的str格式網(wǎng)頁信息存入wenben
wenben = r.text # 由于博主的正則功底很弱润讥,所以就考慮土辦法了
# 最近data:的位置
start = wenben.find('data:', wenben.find('defjson')) + 5
# data最后的符號
end = wenben.find('param:', wenben.find('defjson'))
data = wenben[start:end].strip()[:-2] # 此時獲得的data是str格式的json,可以轉(zhuǎn)為python了
data_python = json.loads(data) # 此時的data_python是一個列表楚殿,列表中的每一個元素是一個字典
# 包含上市新股的各種信息撮慨。我們只需要遍歷股票的名字和日期就可以了
# 其中securityshortname是股票名稱脆粥,purchasedate是申購日期

# 循環(huán)打印砌溺,用制表符\t讓打印出來的列表好看點。 
for i in data_python:  
    name = i['securityshortname']  
    purchase_date = i['purchasedate'][:10] print(name + '\t' + purchase_date)  </pre>

輸出結(jié)果的一部分為:

[圖片上傳失敗...(image-7ef642-1584698534980)]

stock.py子模塊函數(shù)總結(jié)

stock.py子模塊所包含的所有函數(shù)及大致功能如下:

get_stock_type(stock_code):判斷證券ID(stock_code)對應(yīng)的證券市場变隔。

get_code_type(code):判斷證券代碼(code)屬于哪種金融資產(chǎn)规伐。只有輸入代碼是A股證券代碼才能保證正確——讀取源代碼才能發(fā)現(xiàn)。

get_all_stock_codes():獲取所有的證券ID匣缘。但是獲取的ID中卻沒有包括東財轉(zhuǎn)債這樣的轉(zhuǎn)債猖闪,因此也不是全部證券。事實上肌厨,這個列表里面也不全是股票培慌,還包括一系列板塊指數(shù)。所以直接從這個列表中拿證券代碼去get_code_type(code)判斷的話會出錯的柑爸。

round_price_by_code(price, code):根據(jù)證券代碼(code)的金融資產(chǎn)類別對證券價格(price)按照報價小數(shù)點位進行四舍五入吵护。股票是2位小數(shù),基金是3位小數(shù)表鳍。由于這個函數(shù)用到了get_code_type(code)何址,所以code必須是A股證券代碼。

get_ipo_info(only_today=False):獲取網(wǎng)上ipo打新的信息进胯。——這個函數(shù)訪問的新浪新股日歷有時候打不開(針對在國外的情況)用爪,導(dǎo)致函數(shù)卡死。


看完這個模塊的代碼之后胁镐,不知道大家感覺如何偎血?那么,下期咱們來講easyutils包的timeutils.py模塊盯漂。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末颇玷,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子就缆,更是在濱河造成了極大的恐慌帖渠,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件竭宰,死亡現(xiàn)場離奇詭異空郊,居然都是意外死亡份招,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門狞甚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來锁摔,“玉大人,你說我怎么就攤上這事哼审⌒逞” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵涩盾,是天一觀的道長。 經(jīng)常有香客問我春霍,道長,這世上最難降的妖魔是什么终畅? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任离福,我火速辦了婚禮,結(jié)果婚禮上蝶涩,老公的妹妹穿的比我還像新娘。我一直安慰自己绿聘,他們只是感情好次舌,可當(dāng)我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布彼念。 她就那樣靜靜地躺著,像睡著了一般逐沙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上棚赔,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天靠益,我揣著相機與錄音,去河邊找鬼捆毫。 笑死冲甘,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的濒憋。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼凛驮,長吁一口氣:“原來是場噩夢啊……” “哼黔夭!你這毒婦竟也來了羽嫡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤婚惫,失蹤者是張志新(化名)和其女友劉穎魂爪,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體滓侍,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年捺球,在試婚紗的時候發(fā)現(xiàn)自己被綠了懒构。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡胆剧,死狀恐怖秩霍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情铃绒,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布矮燎,位于F島的核電站赔癌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏灾票。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一既们、第九天 我趴在偏房一處隱蔽的房頂上張望正什。 院中可真熱鬧,春花似錦脾拆、人聲如沸莹妒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至迷扇,卻和暖如春爽哎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背课锌。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留雏胃,地道東北人。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓方仿,卻偏偏與公主長得像统翩,于是被迫代替她去往敵國和親仙蚜。 傳聞我的和親對象是個殘疾皇子鳍征,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容