今天陳陳就來教大家如何抓取上市各支股票歷年最高價吧!!
首先就是開啟Anaconda3的Jupyter Notebook
如果還沒安裝Anaconda的可以先去看上一篇
【python】如何安裝Anaconda3(Jupyter Notebook)
【尋找所需資料的網站】
首先,我們要先找到能爬取我們要的資料網站
這裡我們是去抓取台灣證券交易所的資料
從台灣證券交易所抓資料的優點是網址為GET,所以抓取簡單方便,但缺點是太頻繁抓取會被封鎖,所以從這抓資料會花比較長的時間
進入證交所後,我們點選上方的交易資訊→盤後資訊的個股月成交資料
接下來我先隨便打個股票代碼做搜尋,並對「CSV下載」點選右鍵選擇「複製連結網址」
https://www.twse.com.tw/exchangeReport/FMSRFK?response=csv&date=20200615&stockNo=2330
這就是我們複製的網址,我們可以從date=後方的20200615知道,這是日期,stockNo=後方的2330知道是代碼
沒錯,這就是證交所爬取資料為什麼那麼簡單,因為規則就直接打在網址上,一看就知道,我們只要改變這兩個地方我們就能查詢不同年份跟不同的股票
那接下來就是要知道每個上市股票代碼才能替換stockNo=後方的代碼
這部分陳陳前也有發過一篇教學,可以先點去看唷
那我們找尋資料的部分就告一段落了,接下來就是撰寫程式的部分拉!!
【撰寫程式】
在撰寫程式前我們都要先設定一個目標,這樣才能快速清楚的知道自己是要抓取什麼資料
而我們今天的目標就是抓到上市各檔股票從1996年到2020年5月的每月最高價
那目標設定好後,我們就開始撰寫吧!!
【step1 import package】
首先我們習慣把要爬取的網站先用##標記在最上方,以方便使用
然後我們要import一些package
1 import requests
2 import pandas as pd
3 import io
4 import datetime
5 import time
6 import calendar
這次一樣會用到基本的requests以及pandas
外加io,io能讓我們讀寫一個虛擬的檔案,因為我們從證交所複製的csv檔網址並非實際下載下來的檔案,所以我們需要透過 io.StringIO 讓我們讀檔
datetime是之後讓我們抓取日期所用
time是為了之後不被證交所封鎖,我們需要time.sleep來限制每次抓取的間隔時間
calendar是為了讓我們的月份更改成英文
其實這一步我們一開始並不會import這麼多,都是往後在抓取時遇到問題我們才會一一加上去,所以剛開始學python的朋友不用太緊張,不用想說怎麼一開始我就要知道import什麼
【step2 先處理股票代碼】
我們透過【python】如何利用python輕鬆抓取上市櫃股票清單已經將股票代碼抓取下來,但還需要做一些整理,因為我們會遇到一個問題,那就是我們是要抓取1996年之後的每檔股票每月最高價,所以要區分該股票是在1996年之前還之後上市,這會影響我們抓資料的些許不同
那我們首先就是要將抓取的股票清單中的「公開發行/上市(櫃)/發行日」的年跟月分出來
7~12以及19行都是我們先前教的,這裡我們增加了幾行讓我們能分出「年」跟「月」
13、14是先在原先的DataFrame中增加我們要的欄位,也就是年跟月,一開始先設為0
15~17是利用for迴圈將每一個原本在「公開發行/上市(櫃)/發行日」中的年跟月分開加入我們創建的欄位,而年月日本身中間有「/」當作區隔,所以我們就利用split("/")來隔開,split的意思就是只要原文中()內的東西都變成分隔資料的間格,例如:2020/6/15,利用split("/")就會變成['2020', '6', '15'],而年的部分就變成字串[0],月的部分變成字串[1]
最後18就是將我們不要的「公開發行/上市(櫃)/發行日」drop掉,那我們就股票代碼清單整理就告一段落了
7 url2 = "https://isin.twse.com.tw/isin/class_main.jsp?owncode=&stockname=&isincode=&market=1&issuetype=1&industry_code=&Page=1&chklike=Y"
8 page2 = requests.get(url2)
9 df = pd.read_html(page2.text)[0]
10 df = df.drop([0,1,4,5,8,9],axis = 1)
11 df.columns = df.iloc[0]
12 df = df.iloc[1:]
13 df["年"] = 0
14 df["月"] = 0
15 for t in range(1,len(df)+1):
16 df["年"][t] = df["公開發行/上市(櫃)/發行日"][t].split("/")[0]
17 df["月"][t] = df["公開發行/上市(櫃)/發行日"][t].split("/")[1]
18 df = df.drop("公開發行/上市(櫃)/發行日",axis = 1)
19 df = df.set_index("有價證券代號")
將我們處理好的股票代碼清單用一個def來定義函式
【step3 開始抓取股票每月最高價】
這裡陳陳直接拿已經定義def的最終型態來講解
首先我們先定義def,後方名字可自行取,陳陳命名為stock,()內的名稱也能自行設定,因為這只是讓你方便知道要輸入什麼參數
21~24接下來就是處理先前說的從證交所抓取的csv檔,我們將有規律的date=後方以及stockNO=後方的位置改成我們的參數,而date=後方因為我們只需要更改年就好,所以月跟日我們隨便設,因為不影響我們要抓的資料
25行是用if來判斷我們抓的股票他是在1996年之前還之後上市,這樣才不會有股票在還未上市的年分抓不到資料
26~29行是因為證交所在2015年之後有修改他的版面,多了一行,所以我們要多刪掉一行
30、31行是將中文月份更改成英文,這樣在跑圖表時顯示英文比較好看,但如果你想要看中文也是可以不用這行
32、33行是將「有價證券名稱」這欄位名稱更改成最高價,因為我們最後合併放上圖表時,這行的標頭要顯示最高價
34、35行是將民國的年份更改成西元
36、37行是將修改好的年、月合併,例如:修改後的西元年為2020,月份為5月是Mar,合併後變成2020Mar,這就會變成我們圖表右下角跑動的年月份
38~42行是將我們的資料轉置,因為我們最後要合併需要是橫的形式,然後修改一下成我們要的樣式,然後最後加上產業別,這最後在做圖表時可以加入查看股王都是哪個產業的股票,但陳陳最後是沒有放上產業,因為太多產業圖表看上去太雜亂,但如果是要自己看的可以放上去看
44行是如果我們輸入該股票但年份卻在它上市之前,我們就回傳您輸入的年份該股票尚未上市
45行最後就是輸入我們的回傳值,也就是我們最終的答案
20 def stock(stock_id,date_time):
21 df2 = df_u(stock_id)
22 url = "https://www.twse.com.tw/exchangeReport/FMSRFK?response=csv&date=" + date_time + "0101&stockNo=" + stock_id
23 page = requests.get(url)
24 use_text = page.text.splitlines()
25 if date_time >= str(df2["年"][0]):
26 if date_time > str(2015):
27 use_text1 = pd.read_csv(io.StringIO(''.join([text[:-1] + "\n" for text in use_text[1:len(use_text)-4]])))
28 else:
29 use_text1 = pd.read_csv(io.StringIO(''.join([text[:-1] + "\n" for text in use_text[1:len(use_text)-3]])))
30 for i in range(0,len(use_text1)):
31 use_text1["月份"][i] = calendar.month_abbr[use_text1["月份"][i]]
32 df_text = use_text1[["年度","月份","最高價"]]
33 df_text1 = df_text.rename(columns={'最高價':df2["有價證券名稱"][0]})
34 for i in range(0,len(df_text1)):
35 df_text1["年度"][i] = int(df_text1["年度"][i]) + 1911
36 for i in range(0,len(df_text1)):
37 df_text1["年度"][i] = str(df_text1["年度"][i]) + df_text1["月份"][i]
38 df_text2 = df_text1.drop(["月份"],axis = 1)
39 df_text2_T = df_text2.T
40 df_text2_T.columns = df_text2_T.iloc[0]
41 df_text2_T = df_text2_T.iloc[1:]
42 df_text2_T.insert(0,"產業別",df2["產業別"][0])
43 else:
44 df_text2_T = "您輸入的年份該股票尚未上市"
45 return df_text2_T
然後隨便輸入一檔股票跟年份來查看是否為我們要的格式
輸入後檢查一下,2330是台積電,產業是否為半導體業,年份部分對不對,最高價拿去跟證交所查詢的比對一下對不對,並且多查看幾家,如果都沒錯,那恭喜我們又完成一個段落了
【step4 單一股票年份合併】
接下來的工作就是將每檔股票從1996年到2020年5月的最高價資料合併起來
46行一樣先定義一個def
47行是先抓取現在的時間,因為後續會用到
48行一樣先定義前方寫的股票清單為stock_a
49~65行是判斷該股票是在1996年之前還之後上市,如果是之後,我們就從該股票上市那年開始抓取,如果是之前,我們就從1996年開始抓
接下來就是一連串的合併動作,中間記得要間隔8秒,因為在這間隔5秒一樣會被檔
66行是回傳我們要定義值出現的值
那單一股票合併的工作就也完成了
46 def stock_tw1(stock_id):
47 z = datetime.datetime.now()
48 stock_a = df_u(stock_id)
49 if stock_a["年"][0] > 1996:
50 s_0 = stock(stock_id,str(stock_a["年"][0]))
51 s_0_T = s_0.T
52 for b in range(stock_a["年"][0]+1,z.year+1):
53 time.sleep(8)
54 s_1 = stock(stock_id,str(b))
55 s_1 = s_1.drop(["產業別"],axis = 1)
56 s_0_T = s_0_T.append(s_1.T)
57 return s_0_T.T
58 else:
59 s_0 = stock(stock_id,"1996")
60 s_0_T = s_0.T
61 for b in range(1997,z.year+1):
62 time.sleep(8)
63 s_1 = stock(stock_id,str(b))
64 s_1 = s_1.drop(["產業別"],axis = 1)
65 s_0_T = s_0_T.append(s_1.T)
66 return s_0_T.T
然後出來結果大概會像下方這樣,因為康控-KY是2016年才上市,所以資料是從2016年開始到2020年
【step5 將各家股票下載下來】
接下來就是將股票一一下載成CSV檔,這裡可能會有人說為什麼不一起合併成一個檔案再下載下來,因為我們先前有說證交所會有阻擋機制,太頻繁抓取就會被檔,陳陳試了很多次,發現間隔5秒是最適合的抓取時間,但還是有機率會被檔,所以陳陳選擇一檔一檔抓取完後再進行合併,而且我們光上市公司就有900多家,如果每檔都沒失敗都抓成功,外加每檔每年間隔8秒,要抓20幾年,換算下來就要抓個2到3天的時間了,所以使用一檔一檔抓取方式就可以中斷,有空的時候再繼續
66行是先定義我們要存放CSV檔的位置
67行是用for迴圈將股票代碼一一跑過,所以如果中間有中斷,我們能先將df.index[0]額外取出來先看自己目前抓到的檔案位於股票清單第幾個位置,如果找到了就將該數字+1放回index去繼續跑,67行就會變成for a in df.index[要開始抓的股票代碼位置+1:]:
然後剩下的就是抓取每檔股票,try是嘗試執行,失敗話就會跳到except,所以我們在抓取成功的股票後方加上print("Successful!!" + " " + a),當我們成功時就會顯示successful!!,失敗的話print("Fails at" + ' ' + a + "1"),也就是出現failes,我們才知道我們該檔股票是成功還失敗,而陳陳還會在多加一個try然後間隔10秒是怕在抓取的過程中被封鎖IP,所以休息個10秒看看能不能被打開繼續抓,如果都失敗,我們之後抓完後再例外處理,像陳陳就有遇到一些股票上市日期跟資料提供開始日不同,這就可以事後單獨小修程式抓取出來
66 file_path = r"D:\python\stock_high"
67 for a in df.index[0:]:
68 time.sleep(5)
69 try:
70 stock_tw2 = stock_tw1(str(a))
71 stock_tw2.to_csv(file_path + "\\" + str(a) + '.csv',encoding= 'utf_8_sig')
72 print("Successful!!" + " " + a)
73 except:
74 print("Fails at" + ' ' + a + "1")
75 time.sleep(10)
76 try:
77 stock_tw2 = stock_tw1(str(a))
78 stock_tw2.to_csv(file_path + "\\" + str(a) + '.csv',encoding= 'utf_8_sig')
79 print("Successful!!" + " " + a)
80 except:
81 print("Fails at" + ' ' + a + "2")
【總結】
上市的部分做到這裡算是大功告成了,那最終合併的資料陳陳會跟上櫃的一起合併,所以留在下篇上櫃教學完後會一起教合併
其實爬蟲說難不難,說簡單也沒很簡單,但只要有心要學習,就一定能做出自己想要的東西,對未來一定有很大的幫助,因為在未來不論你從事什麼行業都會跟網路脫離不了關係,而python又是比較好入門的程式語言,所以大家一起加油的把python學好吧!!
留言列表