Flask框架小巧强悍?手把手教你打造港股通北水追踪器!不可商用
1.该文章整理自台湾新闻,可能与实际情况有出入,请领会其文章精髓。(滑稽) 2.若手机看代码出现折行,建议点击阅读原文查看。(懒死我得了) 欢迎进群:548377875 ? 里面已经有很多小伙伴上传了大量的学习资料!以及学习交流的好地方! 由数据可见,北水于5月头便开始减持腾讯(0700),直到8月底才沽压稍敛。至于汇丰(0005),则自年头一轮追入后,近期才再见北水捧场,但声势远不及年头强大。一些北水持重货的股份,如新华制药(0719),虽然股价受政策影响大幅回落,但北水仍然密密吸纳,未有泄气情况。种种走势,用以下的程式便可一目了然。 读取数据的程式,主要用了我过去常用的Selenium及BeautifulSoup框架,模拟人手逐页下载数据,并将其组合成一个资料库供之后使用。相比人手逐页copy & paste,程式可以三数分钟便下载过百日的数据,而且做了基本整理,正是自动化的好处。不过,实际处理上来,仍有一些复杂的地方,如公司改名,便会出现一个股票编号、两个公司名称的情况,又如公司供股,也有可能会出现临时股票编号,而原来的股票编号所撷取的数据便出现断层。现时程式只处理首一个情况,未能处理第二个情况(笔者太懒),惟相信后者占整体数据只是少数,有兴趣的读者不妨完善之。程式northwater.py已放于文末,只是短短数十行程式。 笔者利用这程式,下载了今年至今的所有股份持股量数据,之后便要用flask来撰写一个可让网页读到及让用户互动的程式。这方面准备工夫较多,先下载flask框架,再设定一个python的虚拟环境(virtual environment),然后把程式需要用到的框架都写到一个requirements.txt的文字档案内,这是方便之后云端服务了解程式的需要,不用每次均下载一大堆无用的框架,浪费资源。这些设定都是三几行指令码的事情,读者可到flask的官方网页了解。 前期准备功夫完成后,便可以撰写程式,输入flask框架及相关数据库,其主要程序结构如下: 1app = Flask(__name__) 2 3@app.route("/",methods=['POST','GET']) 4 5def submit_new_profile(): 6 7 if request.method == 'POST': 8 9 . 10 11 . 12 13 return render_template(...) 14 15 elif request.method == 'GET': 16 17 。 18 19 。 20 21 return render_template(...) 首行是指定动作,第二行则是当在首页出现存取表格资料时启动submit_new_profile程序,若是用户输入资料,则request.method是'POST',按照用户输入的股票编号抽取数据库中的时序数据,然后再制作相关的图形,并更新index.html档案。 除了以上几行程式外,其余与Python一般的程式无异,详细程式看文末的main.py列表。 各样元素齐备后,便可以在flask模拟的一个简单本地伺服器上测试,确定程式无误,便可以选择一个云端服务商把程式上线(google cloud,亚马逊的AWS,或微软的Azure均可),笔者则选择免费的Pythonanywhere ,先登记一个帐户,然后开设一个新的web app,再把程式上载,包括requirements.txt,main.py,index.html(这档案一般会放在templates的子目录下) ,以及相关的数据库、图片及字型库。大功告成? 别想得太美了,整个过程,最令人沮丧的,并非要学习试用flask框架,而是除虫工作,以为在本地伺服器环境下运作正常的,到云端就不知为何无法再运作了,看错误讯息永远摸不着头脑,左查右查之下,才发现Pythonanywhere要求载入档案必须输入完整的档案路径,否则会发出档案不存在的警告。 另一个痛点,是广受欢迎的绘图框架matplotlib并不支援中文,若要显示中文,必须指定一个中文字库,方能正常显示。具体作法坊间有很多,但不是招招都管用,这又成为一个实验试错的过程,笔者现时用的是google开发的思源宋体。 虽然今次颇多挑战,但因为flask可以将完成品直接送到网民面前,让不懂Python的人也能直接使用,而且日后还会多多使用,倒也不算白费功夫。事实上,用flask来撰写web app,令Python应用可以直接与互联网语言html配搭,加上云端服务,便可以直接执行,连伺服器设定等繁琐工作都可以悭返。昔日写web application可能要学php,mysql等后台语言,但现在都可以由Python一手包办,这正是Python的强大之处。 1来自 bs4 进口 BeautifulSoup 2将 pandas 导入为 pd 3导入请求 4进口重新 5进口时间 6导入日期时间 7来自 selenium import webdriver 8来自 selenium.webdriver.support.ui 导入 WebDriverWait 9从 selenium.webdriver.support 导入 expected_conditions 作为 EC 10从 selenium.webdriver.common.by 进口通过 11 12尝试: 13 table = pd.read_csv(' northwater.csv ') 14除了 IOError: 15 table = pd.DataFrame( columns = [ '股票编号','公司名称' ]) 16 table.to_csv(' northwater.csv ',index = False) 17 18targetpage = “ http://www.hkexnews.hk/sdw/search/mutualmarket_c.aspx?t=hk ” 19 20def gendate(start_date,end_date): 21 dates = [start_date + datetime.timedelta(n)for n in range(int((end_date - start_date).days)+ 1)] 22 date_string = [] 23 对于我在 范围(LEN(日期)): 24 temp = f ' { dates [i]:% Y % m % d } ' 25 date_string.append(TEMP) 26 return date_string 27 28#检查日期是否已加载 29def checknewdate(date,alldate): 30 如果日期在 alldate: 31 返回 False 32 否则: 33 返回 True 34 35def loadpage(目标页面,日期): 36 年=日期[:4 ] 37 月=日期[ 4:6 ] 38 day = date [ 6:] 39 40 driver.get(targetpage) 41 在 driver.title中断言“ HKEX ” 42 43 driver.find_element_by_xpath(“ // select [@ name ='ddlShareholdingDay'] / option [text()=' ” + day + “ '] ”)。click() 44 driver.find_element_by_xpath(“ // select [@ name ='ddlShareholdingMonth'] / option [text()=' ” + month + “ '] ”)。click() 45 driver.find_element_by_xpath(“ // select [@ name ='ddlShareholdingYear'] / option [text()=' ” + year + “ '] ”)。click() 46 driver.find_element_by_name(“ btnSearch ”)。click() 47 48 断言 “没有找到结果。” 不是 在 driver.page_source 49 50 time.sleep(2) 51 page = driver.page_source 52 53 返回页面 54 55 def readpage(页面,日期): 56 汤= BeautifulSoup(页面,' html.parser ') 57 record = soup.find_all(' tr ',class_ = re.compile(r “ ^ row ”)) 58 59 code_tags = [] 60 name_tags = [] 61 nostock_tags = [] 62 63 for in in range(len(record)): 64 code_tags.append(int(record [i] .select(' td ')[ 0 ] .get_text()。strip())) 65 name_tags.append(record [i] .select(' td ')[ 1 ] .get_text()。strip()) 66 nostock_tags.append(int(record [i] .select(' td ')[ 2 ] .get_text()。strip()。replace(',',' '))) 67 68 table = pd.DataFrame({ 69 "股票编号" : code_tags,70 "公司名称" : name_tags,71 日期:nostock_tags, 72 }) 73 74 返回表 75 76def appenddata(table,temptable): 77 result = pd.merge(table,temptable,on = [ '股票编号','公司名称' ],how = ' outer ' ) 78 返回结果 79 80START_DATE = datetime.date(2018,9,1) 81END_DATE = datetime.date(2018,9,29) 82dates = gendate(start_date,end_date) 83 84driver = webdriver.Firefox() 85 86对于我在 范围(LEN(日期)): 87 如果 checknewdate(dates [i],alldate): 88 page = loadpage(targetpage,dates [i]) 89 temptable = readpage(页面,日期[i]) 90 table = appenddata(table,temptable) 91 alldate = list(table)[ 2:] 92 93driver.close() 94 95table.to_csv(' northwater.csv ',index = False) 1#!/ usr / bin / env python 2来自 flask 导入 Flask,render_template,flash,request,jsonify,Markup 3导入 matplotlib 4matplotlib.use(' Agg ') 5将 matplotlib.pyplot 导入为 plt 6将 matplotlib.font_manager 导入为 mfm 7将 matplotlib.ticker 导入为自动收报机 8将 pandas 导入为 pd 9import io,base64 10 11font_path = “/ home / bigfish / mysys / NotoSerifCJKtc-Regular.otf ” 12prop = mfm.FontProperties(fname = font_path,size = 30) 13table = pd.read_csv('/ home / bigfish/mysite/northwater.csv ') 14 15app = Flask(__name__) 16 17def sortdate(表): 18 alldate = list(table)[ 2:] 19 alldate.sort() 20 cols = list(table)[ 0:2 ] + alldate 21 table = table [cols] 22 返回表 23 24result = sortdate(table) 25 26def checkcodeduplicate(代码): 27 tseries = result[result[ '股票编号' ] == code] 28 print(len(tseries)) 29 如果 len(tseries)> 1: 30 t1 = tseries.iloc [ 0 ] 31 t2 = tseries.iloc [ 1 ] 32 tseries.iloc [ 0 ] = t1.combine_first(t2) 33 tseries = tseries [:1 ] 34 返回 tseries 35 36 37@ app.route(“ / ”,methods = [ ' POST ',' GET ' ]) 38def submit_new_profile(): 39 代码= 700 40 name = “ ” 41 submit_value = “ ” 42 tseries_table = “ ” 43 44 如果 request.method == ' POST ': 45 code = int(request.form [ ' selected_code ' ]) 46 submit_value = request.form [ ' submit ' ] 47 48 尝试: 49 tseries = checkcodeduplicate(代码) 50 name = tseries[ '公司名称' ].iat[ 0 ] 51 tseries = tseries.iloc [:,2:] 52 tseries = tseries.T 53 tseries.columns = [code] 54 tseries.index.name = '日期' 55 change = tseries.diff() 56 pattern = ' { :,。0f } '。 format 57 58 plt.rcParams [ “ figure.figsize ” ] =(10,10) 59 fig = plt.figure() 60 斧= fig.add_subplot(2,1,1) 61 ax.xaxis.set_major_locator(ticker.MultipleLocator(40)) 62 ax.plot(T系列) 63 plt.title(name,fontproperties = prop) 64 AX2 = fig.add_subplot(2,1,2) 65 ax2.plot(其他城市) 66 ax2.xaxis.set_major_locator(ticker.MultipleLocator(40)) 67 plt.axhline(linewidth = 1,color = ' r ') 68 69 img = io.BytesIO() 70 plt.savefig(img,format = ' png ') 71 img.seek(0) 72 plot_url = base64.b64encode(img.getvalue())。decode() 73 74 return render_template(' index.html ', 75 model_plot = Markup(' (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |