<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          用Python搞了個(gè)基金查詢機(jī)器人,還可以拓展!

          共 12588字,需瀏覽 26分鐘

           ·

          2022-02-10 20:00

          ??????關(guān)注我,和老表一起學(xué)Python、云服務(wù)器


          跟老表一起學(xué)云服務(wù)器開發(fā)相關(guān)文章(如果是第一次閱讀該系列文章,強(qiáng)烈建議先學(xué)習(xí)下面文章):

          先導(dǎo)篇:擁有有一臺(tái)服務(wù)器后,我竟然這么酷?

          替代項(xiàng)目:10行代碼寫一個(gè)簡歷頁面!

          手把手教大家如何給域名申請(qǐng)免費(fèi)SSL證書

          Linux里的寶塔,真正的寶塔!詳細(xì)教程

          終于有了一個(gè)人人可以訪問的網(wǎng)站了

          如何用Python發(fā)送告警通知到釘釘?
          Python自動(dòng)化實(shí)戰(zhàn),自動(dòng)登錄并發(fā)送微博
          終于“打造”出了一個(gè)可以隨時(shí)隨地編程的工具
          如何打造一個(gè)能自動(dòng)回復(fù)的釘釘機(jī)器人

          一、說點(diǎn)東西

          老早就想搞個(gè)基金監(jiān)控機(jī)器人了,方便自己查看自己關(guān)注基金的各種指數(shù)漲跌情況,及時(shí)進(jìn)行止損或者止盈,從今天開始,我們先建樓基,手把手帶大家實(shí)現(xiàn)一個(gè)基金查詢機(jī)器人,目前主要可以查詢基金指定日期段數(shù)據(jù)和查看基金凈值走勢圖,后面慢慢新增功能。

          二、開始動(dòng)手動(dòng)腦

          2.1 環(huán)境準(zhǔn)備

          • Linux、Mac、Windows 都可以
          • python 3.7及以上
          • 相關(guān)第三方包:pandas(數(shù)據(jù)處理)、requests(爬取數(shù)據(jù))、re(文本內(nèi)容解析)、akshare(獲取基金股票數(shù)據(jù))、matplotlib(數(shù)據(jù)可視化)、dataframe-image(dataframe表格轉(zhuǎn)成圖片)

          2.2 獲取指定日期段基金數(shù)據(jù)

          基金數(shù)據(jù)可以從一些金融相關(guān)的網(wǎng)站獲取到,比如天天基金網(wǎng)、新浪基金網(wǎng)等,可以自己寫爬蟲程序獲取網(wǎng)站數(shù)據(jù),也可以使用現(xiàn)成的工具包獲取數(shù)據(jù),比如:一行代碼獲取股票、基金數(shù)據(jù),并繪制K線圖里用到的akshare。

          這里我們同時(shí)介紹下兩種方法:

          2.2.1 回顧下akshare獲取基金數(shù)據(jù)

          目前akshare不支持獲取指定日期范圍內(nèi)的基金凈值數(shù)據(jù),但是可以一次獲取到基金歷史凈值數(shù)據(jù),調(diào)用函數(shù)fund_em_open_fund_info獲取基金歷史數(shù)據(jù),然后自己根據(jù)日期選取時(shí)間斷進(jìn)行分析。

          import akshare as ak
          fund_data = ak.fund_em_open_fund_info(fund='005827', indicator='單位凈值走勢')

          print(fund_data)

          自己調(diào)用現(xiàn)成數(shù)據(jù)接口

          本質(zhì)上akshare也是從一些金融相關(guān)的網(wǎng)站獲取到數(shù)據(jù),我們也可以自己寫代碼進(jìn)行獲取,通過瀏覽器我們很快能搜索到基金數(shù)據(jù)接口,來自東方財(cái)富的天天基金網(wǎng)。

          f'http://fund.eastmoney.com/f10/F10DataApi.aspx?type=lsjz&code={code}&page={page}&sdate={start_date}&edate={end_date}&per={per}'

          code - 基金代碼
          page - 基金數(shù)據(jù)頁碼
          start_date - 數(shù)據(jù)開始日期
          end_date - 數(shù)據(jù)結(jié)束日期
          per - 每頁展現(xiàn)數(shù)據(jù)量,最多40

          根據(jù)指定參數(shù),瀏覽器會(huì)返回指定參數(shù),一段js賦值代碼,包括了 基金數(shù)據(jù)(content)、總記錄條數(shù)(records)、總頁數(shù)(pages)、當(dāng)前頁數(shù)(curpage)。

          格式非常規(guī)整,我們可以直接通過正則提取數(shù)據(jù),

          '''
          獲取單頁面 基金數(shù)據(jù)
          '''

          def get_html(code, start_date, end_date, page=1, per=40):
              url = f'http://fund.eastmoney.com/f10/F10DataApi.aspx?type=lsjz&code={code}&page={page}&sdate={start_date}&edate={end_date}&per={per}'
              # print(url)
              rsp = requests.get(url)
              html = rsp.text
              
              return html

          通過返回的數(shù)據(jù)可以發(fā)現(xiàn),基金數(shù)據(jù)部分是一個(gè)由table標(biāo)簽包裹的html代碼,那我們可以直接使用pandas的read_html來解析數(shù)據(jù)。

          # 從html中解析出數(shù)據(jù)表部分 并解析成df
          def parses_table(html):
              # 獲取基金數(shù)據(jù)表
              pattern = 'content:"<table(.*)</table>",'
              table = re.search(pattern, html).group(1)
              table = '<table' + table + '</table>'
              fund_data = pd.read_html(table)[0]
              return fund_data

          前面有提到,基金數(shù)據(jù)接口返回?cái)?shù)據(jù)每頁最多展示40條,所以要想獲取所有數(shù)據(jù),我們可能需要遍歷每一頁,那么我們還需要通過正則將總頁數(shù)pages獲取到,然后遍歷調(diào)用get_htmlparses_table函數(shù)解析出所有數(shù)據(jù)。

          # 獲取指定日期內(nèi) 累計(jì)凈值 等數(shù)據(jù)
          def get_fund_data(code, start_date, end_date):
              first_page = get_html(code, start_date, end_date)
              # 獲取總頁數(shù)
              pattern = 'pages:(.*),'
              pages = re.search(pattern, first_page).group(1)
              # 轉(zhuǎn)成int數(shù)據(jù)
              try:
                  pages = int(pages)
              except Exception as e:
                  r = f'【錯(cuò)誤信息】{e}'
                  # print(r)
                  return r 
              
              # 存放每頁獲取到的基金數(shù)據(jù) dataframe格式 便于后面合并
              fund_df_list = []
              
              # 循環(huán)便利所有頁面
              for i in range(pages): 
                  if i == 0:
                      fund_data = parses_table(first_page)
                  else:
                      page_html = get_html(code, start_date, end_date, page=i+1)
                      fund_data = parses_table(page_html)
                  fund_df_list.append(fund_data)
              
              # 將每頁的數(shù)據(jù)合并到一起
              fund_df = pd.concat(fund_df_list)
              
              return fund_df

          上面兩種方法都可以獲取到基金凈值數(shù)據(jù),最后我選擇了akshare方式獲取,設(shè)置一個(gè)定時(shí)任務(wù),每天三點(diǎn)更新自己關(guān)注的基金所有數(shù)據(jù),存儲(chǔ)到本地,后面要查詢的時(shí)候直接讀取本地文件查詢即可。

          • 定時(shí)任務(wù):每天早上3點(diǎn)獲取所有關(guān)注的基金歷史數(shù)據(jù),存儲(chǔ)到本地
          # 定時(shí)任務(wù):每天早上3點(diǎn)獲取所有關(guān)注的基金歷史數(shù)據(jù),存儲(chǔ)到本地
          def get_all():
              try:
                  # 從文件讀取 自己關(guān)注的基金代碼列表
                  with open('./FD/funds.txt'as f:
                      funds = [i.strip() for i in f.readlines()]
                  # 遍歷 一個(gè)個(gè)更新數(shù)據(jù)
                  for fund in funds:
                      fund_df = ak.fund_em_open_fund_info(fund, indicator='單位凈值走勢')
                      fund_df = fund_df.sort_values(by=['凈值日期'], ascending=False)
                      fund_df.to_csv(f"./FD/DATA/F{fund}_data.csv", index=False)
                      # print(f"./FD/DATA/F{fund}_data.csv")
                      time.sleep(random.randint(1,5))
                  return '基金數(shù)據(jù)更新完成'
              except Exception as e:
                  r = f"【錯(cuò)誤信息】{e}"
                  return r
          • 獲取指定基金 指定日期段 凈值數(shù)據(jù)
          # 獲取指定基金 指定日期段 凈值數(shù)據(jù)
          def get_fund_data(fund, start_d, end_d):
              fund_df = pd.read_csv(f'./FD/DATA/{fund}_data.csv')
              result_df = fund_df.query(f"'{start_d}'<=凈值日期<='{end_d}'")
              return result_df

          2.3 返回?cái)?shù)據(jù)呈現(xiàn)方式

          目前先簡單點(diǎn),設(shè)置規(guī)則如下:

          • 1)如果數(shù)據(jù)量小于等于30條,就返回原始數(shù)據(jù)圖

          原始數(shù)據(jù)圖就是直接將獲取到的數(shù)據(jù)轉(zhuǎn)成圖片的方式發(fā)送給用戶,這里我們使用dataframe-image這個(gè)第三方包,使用非常簡單,pip安裝后,直接調(diào)用export函數(shù)即可快速將datafrmae數(shù)據(jù)轉(zhuǎn)成圖片。

          # 將dtaframe表格轉(zhuǎn)變成圖片
          def df_to_img(fund_df, fund, start_d, end_d):
              if fund_df.shape[0] <=1:
                  dfi.export(fund_df, f'./FD/IMG/{fund}_{start_d}_{end_d}_data.png')
                  return 
              
              # 格式化表格 凸顯最大最小值
              fund_df = fund_df.style.highlight_max(subset=['單位凈值'], color='red')\
                       .highlight_min(subset=['單位凈值'], color='green')\
                       .format({'日增長率''{:}%'})
              
              dfi.export(fund_df, f'./FD/IMG/{fund}_{start_d}_{end_d}_data.png')

          為了圖片數(shù)據(jù)更好看,我們還使用了df.style設(shè)置數(shù)據(jù)表格樣式(單位凈值最大值、最小值高亮和日增長率添加百分號(hào))。

          • 2)如果數(shù)據(jù)量大于30條,就返回原始數(shù)據(jù)趨勢圖

          原始數(shù)據(jù)趨勢圖就是將數(shù)據(jù)可視化下,然后返回給用戶,這里我們選擇繪制數(shù)據(jù)的走(趨)勢圖,使用matplotlib進(jìn)行繪制。

          # 繪制基金單位凈值走勢圖
          def draw_fund_line(fund_df, fund, start_d, end_d):
              plt.rcParams['figure.figsize'] = (8.04.0# 設(shè)置figure_size尺寸
              plt.rcParams['savefig.dpi'] = 300 #保存圖片分辨率

              # 不顯示右、上邊框
              ax=plt.gca() 
              ax.spines['right'].set_color('none')
              ax.spines['top'].set_color('none')

              # 設(shè)置坐標(biāo)網(wǎng)格
              plt.grid(axis="y", color='gray')  

              # 計(jì)算最大值 最小值坐標(biāo) 并標(biāo)注到圖中
              fund_max = fund_df.loc[fund_df['單位凈值'].idxmax()]
              fund_min = fund_df.loc[fund_df['單位凈值'].idxmin()]

              ax.annotate(f'({fund_max[0]},{fund_max[1]})', xy=(fund_max[0], fund_max[1]), color='red')
              ax.annotate(f'({fund_min[0]},{fund_min[1]})', xy=(fund_min[0], fund_min[1]), color='green')

              # 畫圖
              plt.plot(fund_df['凈值日期'],fund_df['單位凈值'], color="c")
              plt.title('基金單位凈值走勢圖')
              plt.xticks(rotation=30)
              plt.xlabel('凈值日期')
              plt.ylabel('單位凈值')
              plt.savefig(f'./FD/IMG/{fund}_{start_d}_{end_d}_data.png')

          這里使用的是折線圖,有一些對(duì)圖片樣式的設(shè)置,比如:大小、邊框、最大/小值標(biāo)注,但依然不是很美觀,后面繼續(xù)優(yōu)化。

          • 完整調(diào)用
          # 返回?cái)?shù)據(jù)
          def response_data(fund, start_d, end_d):
              # 本地查看 查詢結(jié)果是否已存在
              imgs = os.listdir('./FD/IMG/')
              if f'{fund}_{start_d}_{end_d}_data.png' in imgs:
                  return f'./FD/IMG/{fund}_{start_d}_{end_d}_data.png'
              
              # 獲取數(shù)據(jù)
              fund_df = get_fund_data(fund, start_d, end_d)
              
              # 如果數(shù)據(jù)量小于等于30條,就返回原始數(shù)據(jù)圖
              if fund_df.shape[0]<= 30:
                  df_to_img(fund_df, fund, start_d, end_d)
              else:
                  # 否則返回?cái)?shù)據(jù)趨勢圖
                  fund_df = fund_df.sort_values(by=['凈值日期'])
                  draw_fund_line(fund_df, fund, start_d, end_d)

              return f'./FD/IMG/{fund}_{start_d}_{end_d}_data.png'

          2.4 對(duì)接釘釘機(jī)器人設(shè)置守護(hù)程序

          目前項(xiàng)目中使用到了之前介紹過的兩種機(jī)器人:釘釘群機(jī)器人、企業(yè)機(jī)器人,相關(guān)配置方法和代碼可以查看之前的文章:如何用Python發(fā)送告警通知到釘釘?如何打造一個(gè)能自動(dòng)回復(fù)的釘釘機(jī)器人,非常詳細(xì)。

          釘釘群機(jī)器人主要用來匯報(bào)每天自動(dòng)匯報(bào)基金數(shù)據(jù)更新情況,后面還可以加基金漲跌檢測情況等。

          企業(yè)機(jī)器人主要用來做基金數(shù)據(jù)查詢自動(dòng)回復(fù)功能,也可以拓展主動(dòng)發(fā)消息給用戶,后面研究研究。

          2.5 遇到問題及解決方法

          2.5.1 Linux上datafrmae-image轉(zhuǎn)圖片出錯(cuò)

          最開始是提示沒有chrom,然后按網(wǎng)上教程安裝了google chrom。

          參考:https://segmentfault.com/a/1190000022425145

          安裝后,運(yùn)行代碼提示SyntaxError: not a PNG file。

          看錯(cuò)誤提示以為是Pillow和matplotlib的問題,修改到了和本地一樣的版本也不行。

          最后看了源碼,發(fā)現(xiàn)可以轉(zhuǎn)換方法除了使用chrom,還可以用matplotlib,修改后,確實(shí)可以正常生成圖片了,但是沒有格式?。?!

          最后改回默認(rèn)table_conversion,仔細(xì)看,發(fā)現(xiàn)有提示以下內(nèi)容,大概意思linux下不能直接使用root用戶權(quán)限允許谷歌chrome,最簡單的方法就是創(chuàng)建一個(gè)普通用戶。

          [0209/162730.350381:ERROR:zygote_host_impl_linux.cc(90)] Running as root without --no-sandbox is not supported. See https://crbug.com/638180

          在root權(quán)限下,新建一個(gè)用戶od,并將/root目錄權(quán)限授予給它,然后輸入su指令切換到新用戶下。

          useradd od
          chown -R od /root
          su od

          再次運(yùn)行確實(shí)能解決圖片生成和數(shù)據(jù)格式問題,但是有新問題:表頭中文無法顯示。。。

          百般搜索,看源碼調(diào)試、看項(xiàng)目倉庫問題都沒解決,最后,最后我突然想到,我本地可以,兩個(gè)包的版本又是一樣,應(yīng)該不是代碼問題,會(huì)不會(huì)是因?yàn)閘inux里沒有安裝中文字體,所以無法顯示中文?

          root用戶權(quán)限下,先創(chuàng)建一個(gè)目錄,存放中文字體,創(chuàng)建好后,可以直接利用寶塔將本地的SimHei字體上傳到對(duì)應(yīng)目錄即可。

          mkdir -p /usr/share/fonts/my_fonts

          可以通過下面指令查看中文字體是否安裝成功,

          fc-list :lang=zh

          再次運(yùn)行代碼,生成的圖片就正常啦~開心!

          2.5.2 matplotlib圖片中文顯示問題

          之前有寫過詳細(xì)解決方法,可以直接查看之前文章:永久解決matplotlib中文亂碼

          2.5.3 釘釘機(jī)器人無法直接傳輸圖片

          釘釘機(jī)器人目前只支持傳輸:普通文本、markdown文本、連接、actionCard消息和feedCard消息類型。

          如果我想要將生成的基金數(shù)據(jù)圖發(fā)送給用戶,最好的方法是和之前一樣,先將圖片轉(zhuǎn)成鏈接,然后通過markdown形式傳輸。

          如果系統(tǒng)僅個(gè)人使用,數(shù)據(jù)量不大,我們不必選擇網(wǎng)絡(luò)上現(xiàn)有的圖床工具(這樣我們還要寫接口對(duì)接代碼),可以直接開放個(gè)http端口去共享我們的圖片,本身企業(yè)機(jī)器人就使用到了flask,所以我們可以更簡單的實(shí)現(xiàn)這個(gè)功能。

          app = Flask(__name__, static_folder='xxx/FD/IMG', static_url_path='/static')

          在初始化flask app時(shí),指定靜態(tài)文件所在目錄和靜態(tài)文件路由后綴即可,這樣,我們就可以通過:http://服務(wù)器IP地址:端口號(hào)/static/圖片文件名,訪問到對(duì)應(yīng)圖片了。

          然后將圖片鏈接嵌入到markdown中,即可正常返回給用戶了。

          2.6 最終效果圖

          • 指定查詢

          查看某基金某個(gè)時(shí)間段內(nèi)的基金凈值數(shù)據(jù)。(30條以內(nèi)數(shù)據(jù),表格展示;大于30條,趨勢圖展示)

          查詢格式: F基金代碼 起始日期 結(jié)束日期,如:F005827 2021-12-03 2022-02-10

          效果圖
          • 普通查詢

          查看某基金近10天內(nèi)凈值和日增長率數(shù)據(jù)+趨勢圖

          查詢格式: F基金代碼,如:F005827

          最近10天內(nèi),只有兩個(gè)交易日

          三、后言后語

          這項(xiàng)目說大不大,說小也不小,百行代碼,本機(jī)測試還是很順暢的,主要是遷移到Linux上后出現(xiàn)一些問題,從最開始的python版本問題(安裝了一個(gè)3.7.9),到datafrmae-image問題,延展出來的Linux安裝谷歌、設(shè)置新用戶、分配權(quán)限,以及源碼測試學(xué)習(xí)。

          遇到問題、解決問題的過程確實(shí)花費(fèi)了我很長時(shí)間,一度還讓我很苦惱,但是,這個(gè)過程也讓我覺得很有益,是一個(gè)不斷積累、不斷練習(xí)、不斷鞏固的過程,解決問題后更會(huì)為自己歡呼。

          目前基金監(jiān)測機(jī)器人還比較簡陋,甚至都沒有監(jiān)測功能(目前只支持?jǐn)?shù)據(jù)查詢和更新),但是這個(gè)樓基很穩(wěn)、很深,后面添加其他功能會(huì)簡單、便捷許多,歡迎大家評(píng)論區(qū)留言,說說你想為這個(gè)機(jī)器人添加的功能。

          代碼在下個(gè)版本文章發(fā)布時(shí)一起分享出來,這里招聘5個(gè)‘產(chǎn)品經(jīng)理’,一起來體驗(yàn)這個(gè)基金查詢機(jī)器人,給反饋、提需求,有興趣的可以微信私聊我(需要有2年以上的基金或者股票投資經(jīng)驗(yàn)或者理財(cái)資產(chǎn)超過10w)。

          堅(jiān)持 and 努力 :終有所獲。

          點(diǎn)贊 在看 留言 轉(zhuǎn)發(fā) ,四連支持,原創(chuàng)不易。好的,那么下期見,我是愛貓愛技術(shù),更愛思思的老表???( ˙?˙ )???

          如何找到我:

          瀏覽 86
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  无码午夜| 成人精品喷水视频wwww | 国产A级毛片 | 成人看的视频网站在线观看 | 国产大雷美女被干的网站 |