證交所最佳五檔的程式解析
註:
首先我要說明一下現在的日期是 2017年1月,因為 證交所最佳五檔的程式 的規則有時會變動,不見得適用未來的日子。
證交所提供了股票即時最佳五檔的網頁,供一般民眾查詢(但我相信,會觀注的多半是碼農,從最佳五檔的網址,它主要的Host是 mis.twse.com.tw ,這是我們要注意的第一個點,透過 dig command,我們可以發現twse.com.tw網域用了4台DNS主機來應付輸詢,這也是我要提的第一個建議:
若不是用瀏覽器執行JavaScript程式
- 2018/11/29,發現已經無法一次查詢多個代號,只能迴圈一個一個查。
- 今天是2017/1/16,一早發現網址又改,原來第3第4點的getSTKInfo.jsp改成 getStockInfo.jsp?,而且也把json亂數表頭的方式移除,已緊急改程式了,請重新下載程式(程式碼在此)
2018/8/23,一早發現必須透過getStockInfo.jsp才是取得Session的地方,所以修改下面第一點
首先我要說明一下現在的日期是 2017年1月,因為 證交所最佳五檔的程式 的規則有時會變動,不見得適用未來的日子。
證交所提供了股票即時最佳五檔的網頁,供一般民眾查詢(但我相信,會觀注的多半是碼農,從最佳五檔的網址,它主要的Host是 mis.twse.com.tw ,這是我們要注意的第一個點,透過 dig command,我們可以發現twse.com.tw網域用了4台DNS主機來應付輸詢,這也是我要提的第一個建議:
若不是用瀏覽器執行JavaScript程式
- URL直接採用網址IP連結,不要使用mis.twse.com.tw網域,輸詢太多次會卡住,且若每次輸詢結果不一致時,會導致Session遺失
- 先說一下,用網頁查詢的網址格式是
http://mis.twse.com.tw/stock/fibest.jsp?lang=zh_tw&stock={代號}
,在瀏覽器下通常lang=zh_tw參數會被省略,但是程式設計時,請不要忘了要加上去。這是最重要的一個關鍵,因為我們必須在這頁取得Session(並且要保留),否則後面的資料存取將無以為繼,所以若是以程式要取得Session的第一件事就是連到http://mis.twse.com.tw/stock/fibest.jsp?lang=zh_tw我們取得這個網頁只是為了取得Cookie,但現在除了這個功能外,還必須取得網頁並分析其中的3個Function:rA1()、rA2()與rA3(),因為後續取得的JSON屬性表頭不再是固定的字串,而是隨機定義在這三個函數內,我們必須透過分析JavaScript以瞭解JSON內容每個項目所代表的意義,取得範例的部分內容說明如下:
在以往,我們取得這個網頁只能為了取得Cookie,<script>function rA3(item){ item.o=item.gQ0; //開盤價 item.z=item.lW1; //最近或最後成交價 item.y=item.jH2; //昨日收盤價
item.s=item.wR3; //當盤成交量
… }</script> <script>function rA2(item){ item.h=item.hT4; //最高價 item.u=item.fL6; //漲停價 item.a=item.jL7; //五檔賣價 item.v=item.hJ8; //累積成交量 … }</script> <script>function rA1(item){ item.pz=item.pW9; //試算參考成交價 rA=new RegExp('NN11','g');//要清除的字串 item.b=item.pB11; //五檔買價 item.f=item.nL12; //五檔賣量 item.l=item.nN13; //最低價 item.g=item.nQ16; //五檔買量 item.w=item.fT17; //跌停價 … }</script>
- 然後我們再檢閱最佳五檔的網頁源碼, 可以發現一個重要的JavaScript:ctrl.fibest.js,所有的故事從這裡開始:假設我們查詢的是6294智基,它會先到(參考 function initStock())
http://mis.twse.com.tw/stock/api/getStock.jsp?ch=6294.tw&json=1&_=時間亂數
取回6294的程式代號{ "msgArray":[ { "ex":"otc", "d":"20170109", "it":"12", "n":"智基", "i":"20", "ip":"0", "w":"110.00", "u":"134.00", "t":"07:50:00", "p":"0", "ch":"6294.tw", "key":"otc_6294.tw", "y":"122.00" } ], "rtmessage":"OK", "queryTime":{ "stockDetail":2315, "totalMicroTime":2315 }, "rtcode":"0000" }
其實這裡不難看出上市代號前綴 tse_,而上櫃代號則前綴 otc_,作為碼農,您應該找個地方把所有代號的上市櫃狀態記錄在某個地方,並不時更新,若不知道請參考
http://isin.twse.com.tw/isin/C_public.jsp?strMode=2
與
http://isin.twse.com.tw/isin/C_public.jsp?strMode=4
另外,tse_t00.tw與otc_o00.tw分別為上市櫃指數代號 - 2017/8/26 開始在取得最佳5檔前,必須先經過
http://mis.twse.com.tw/stock/api/getStockInfo.jsp?ex_ch=tse_t00.tw|otc_o00.tw|tse_FRMSA.tw&json=1&delay=0&_={時間亂數}
請注意,| 必須escape %7c(c必須為小寫)
然後再參考下面一點取得5檔資料 - 最後是取得五檔資訊(參考 function loadStockInfo()),它會載入
/stock/api/getStockInfo.jsp?ex_ch=otc_6294.tw&json=1&delay=0&_=時間亂數
實際上這個連結反履變過數次,若是資料出不來,就回報檢查看看是否這裡有所變動,其資料回傳範例如下(BJ4,自已對一下就知道欄位義意,該注意的是它的Json資料型態是Array,可見是為了可以回傳多筆查詢),註:回傳的json其值必須包含 "rtcode":"0000" 項問才可視為查詢成功,若不是,則請重新查詢{ "msgArray":[ { "pB11":"120NN11.50_120.00_119.50_119.00_118.50_NN11", "ts":"0", "pW9":"123ZG9mt.00ZG9mt", "tk0":"6294.tw_otc_20170109_B_9999789751", "jH2":"122HJ2.00HJ2", "fT17":"110LL17vt.00LL17vt", "tk1":"6294.tw_otc_20170109_B_9999784676", "nQ16":"7_6WJ16ll_2_6_1_WJ16ll", "jL7":"121CN7.50_122.00_122.50_123.00_123.50_CN7", "tlong":"1483933295000", //目前時間 "wR3":"FJ30FJ3", "fL6":"134NN6cn.00NN6cn", "ex":"otc", "d":"20170109", "it":"12", "c":"6294", "mt":"953540", "n":"智基", "hJ8":"3NF8z5NF8z", "gQ0":"123GL0jw.00GL0jw", "nN13":"120VV13n.50VV13n", "ip":"0", //1:趨跌,2:趨漲,4:暫緩收盤,5:暫緩開盤 "i":"20", "nL12":"2_5TQ12_6_10_4_TQ12", "t":"11:41:35", //揭示時間 "tv":"1", "p":"0", "nf":"智基科技開發股份有限公司", "ch":"6294.tw", "hT4":"123HP4f.00HP4f", "lW1":"121JW1h.00JW1h", "ps":"4" //試算參考成交量 } ], "userDelay2":5000, "userDelay":5000, "rtmessage":"OK", "referer":"/", "queryTime":{ "sysTime":"11:46:30", "sessionLatestTime":-1, "sysDate":"20170109", "sessionKey":"otc_6294.tw_20170109|", "sessionFromTime":-1, "stockInfoItem":538, "showChart":false, "sessionStr":"UserSession", "stockInfo":107575 }, "rtcode":"0000" }以第一行的 "pB11":"120NN11.50_120.00_119.50_119.00_118.50_NN11"為例
從第1點的function rA1(item)可以知道,它指的是五檔買價,但它的格式看起來卻有點奇怪?
其實它是用底線(_)做為分隔,而在function內也可以看到NN11已經被清除了,所以第一項的120NN11.50,把其中的NN11清除,它的值就是120.5{ "msgArray":[ { "ts":"0", "tk0":"2330.tw_tse_20170116_B_9999925914", "tk1":"2330.tw_tse_20170116_B_9999925580", "tlong":"1484528728000", //目前時間 "f":"217_1784_1144_1788_1190_", //五檔賣量 "ex":"tse", "g":"2165_1133_3126_1611_1962_", //五檔買量 "d":"20170116", "it":"12", "b":"179.00_178.50_178.00_177.50_177.00_", //五檔賣價 "c":"2330", "mt":"549353", "a":"179.50_180.00_180.50_181.00_181.50_", //五檔賣價 "n":"台積電", "o":"180.00", //開盤價 "l":"179.00", //最低成交價 "h":"180.50", //最高成交價 "ip":"0", //1:趨跌,2:趨漲,4:暫緩收盤,5:暫緩開盤 "i":"24", "w":"163.50", //跌停價 "v":"6359", //累積成交量 "u":"199.50", //漲停價 "t":"09:05:28",//揭示時間 "s":"34", //當盤成交量 "pz":"180.00", "tv":"34", "p":"0", "nf":"台灣積體電路製造股份有限公司", "ch":"2330.tw", "z":"179.50", //最近成交價 "y":"181.50", //昨日成交價 "ps":"2459" //試算參考成交量 } ], "userDelay":... ... }, "rtcode":"0000" }
第3點是取得單一股票的方式,另外它也有方法可以同時取得多檔股票記錄,它取得的方式是:
http://mis.twse.com.tw/stock/api/getStockInfo.jsp?ex_ch=代號1|代號2|...|代號N&cp=0&json=1&delay=0&_=時間亂數
,範例如下(要注意HTTP Get 有長度限制):
http://mis.twse.com.tw/stock/api/getSTKInfo.jsp?ex_ch=tse_2377.tw%7cotc_6294.tw%7c&cp=0&json=1&delay=0&_=1483942971571
2017-8-12註:|(pipeline) 若要編碼請一定編為%7c(小寫),因為在前一天(8/11),證交所不再接受%7C了
取回的資料格式跟第3點類似,只不過取回的資料是一次多筆罷了,所以作為碼農,打開IDE,開始寫程式去吧!
java -jar StockBest5.jar 股號1 股號2 ...股號N
即可取回股票市況
謝謝你, 這次的改版還是看了你的網誌才知道怎麼改~
回覆刪除請問最佳五檔是不是抓不到資料了?
回覆刪除應該是可以抓得到資料,已重新上傳程式碼與編譯檔(在Linux環境顯示情況比較好),您也可以自已抓程式回去看,基本上上面描述的抓取方式沒有改變
刪除作者已經移除這則留言。
刪除作者已經移除這則留言。
刪除作者已經移除這則留言。
回覆刪除作者已經移除這則留言。
回覆刪除看來是session的問題?(即您說的第一點) 有沒有簡單一點的範例? 只要有session可以讓我查上下五檔的API (即能抓到http://mis.twse.com.tw/stock/api/getStockInfo.jsp?ex_ch=代號&cp=0&json=1&delay=0&_=時間亂數 的回傳值)就好了,我原本就已寫好程式,只是現在那個網址不能用了,只好改網址..;感謝您的回覆!
回覆刪除可能我的問題太淺,還是我自己努力就好,不用勞煩忙碌的貴格主了
回覆刪除謝了~
我參考其他範例~程式碼愈來愈簡潔~跑得又快!呵~
回覆刪除感謝分享,8/23的修正。
回覆刪除08/22 晚間證交所修改之後程式就撈不到資料了。
回覆刪除我是使用 php curl 送兩次 request,第一次原本的目標是 index.jsp,取得回應的 header 中 Set-cookie 的 JSESSIONID 值,夾在第二次對 stock/api/getStockInfo.jsp 的 request 中 cookie 已查詢即時交易資訊。
看了版大最新修改的註解說明還是有點懵懵懂懂,目前還沒有試出解法~
請問應該是先向 stock/api/getStockInfo.jsp 還是 stock/fibest.jsp 送出 request 才能取得有效的 cookie 呢?
這次改版是取得Session的地方不同,先到http://mis.twse.com.tw/stock/api/getStockInfo.jsp?ex_ch=tse_t00.tw|otc_o00.tw|tse_FRMSA.tw&json=1&delay=0&_={數值亂數}
刪除別忘了把 | escape %7c(必須小寫,大寫不吃),也別忘了用 curl_setopt 加入 Referer 表頭
最後所有傳送方法都得是 GET(Post 證交所好像也不吃)
不好意思請問一下,8/25晚上證交所又改做法了
回覆刪除http://mis.twse.com.tw/stock/api/getStockInfo.jsp?ex_ch=tse_t00.tw|otc_o00.tw|tse_FRMSA.tw&json=1&delay=0&_= 這已經無法取得cookie,然後用http://mis.twse.com.tw/stock/fibest.jsp取回的cookie也無法使用,請問您有發現新的做法嗎? 感謝
1.先連到 /stock/fibest.jsp?lang=zh_tw 取得Cookie
刪除2.必須經過到 /stock/api/getStockInfo.jsp?ex_ch=tse_t00.tw|otc_o00.tw|tse_FRMSA.tw&json=1&delay=0&_=時間亂數
3.取最佳5檔
若您用Linux 可用以下指令驗證
$curl -b c.txt -c c.txt "http://mis.twse.com.tw/stock/fibest.jsp?lang=zh_tw"
$curl -b c.txt -c c.txt "http://mis.twse.com.tw/stock/api/getStockInfo.jsp?ex_ch=tse_t00.tw|otc_o00.tw|tse_FRMSA.tw&json=1&delay=0&_=1503848179451"
$curl -b c.txt -c c.txt "http://mis.twse.com.tw/stock/api/getStockInfo.jsp?ex_ch=tse_2330.tw|tse_00642U.tw&json=1&delay=0&_=1503848179451"
喔喔喔~成功了,太感謝大大了~~~~
刪除大大 證交所好像又改做法了 有解法嗎?感恩
回覆刪除8/25後應該沒改,我的程式還是正常取值
刪除應該是我搞錯了。現在可以了。謝謝你一連串的教學
回覆刪除