[C#] 取得證交所上市及上櫃的股票及ETF清單
在證券交易所及櫃買中心市場上每一天都會有上市或下市的有價證券清單,做程式交易除了每日更新股價之外,還需要定期更新一下新上市或已下市的有價證券清單。
在證交所有一個網址可以查看最新上市或上櫃的清單。
查詢上市清單網址
https://isin.twse.com.tw/isin/C_public.jsp?strMode=2
查詢上櫃清單網址
https://isin.twse.com.tw/isin/C_public.jsp?strMode=4
透過這 2 個網址就可以知道最新的上市或上櫃有價證券清單是什麼。
可是這清單實在是太長了,包含了眾多的有價證券類別,如果只想看最常見的股票及 ETF 清單,
那就需要用程式把清單中只屬於股票及 ETF 的清單篩選出來。
接下來我會示範一下如何利用 C# 取得證交所有價證券清單的上市及上櫃股票 ETF 清單。
先看一下操作畫面
建置環境
前端架構: Vue.js, jQuery, Bootstrap
後端架構: C# ASP.Net MVC .Net Framework
我網頁上只講解重要的程式碼部份,完整範例可至下方下載。
HTML 語法
主要規劃一個查詢區域和結果列果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<main id="Page"> <div class="row"> <div class="col-md-3"> 市場別 <select class="form-control" v-model="form.Q_MARKET_TYPE"> <option value="TWSE">證交所</option> <option value="OTC">櫃買中心</option> </select> </div> <div class="col-md-3"> 資產別 <select class="form-control" v-model="form.Q_ASSETS_TYPE"> <option value="STOCK">股票</option> <option value="ETF">ETF</option> </select> </div> <div class="col-md-3"> <br /> <button type="button" class="btn btn-default" v-on:click="GetList()">查詢</button> </div> </div> <div class="panel panel-default"> <div class="panel-heading"> {{gridList.CName}}清單 <div style="float:right;"> 總筆數: {{gridList.RowCnt}} </div> </div> <div class="panel-body"> <table class="table"> <tr> <th>代碼</th> <th>名稱</th> <th>市場別</th> <th>資產別</th> <th>上市日</th> <th>產業別</th> </tr> <tr v-for="(item, index) in gridList.datas"> <td>{{item.SYMBOL_CODE}}</td> <td>{{item.SYMBOL_NAME}}</td> <td>{{item.MARKET_TYPE}}</td> <td>{{item.ASSETS_TYPE}}</td> <td>{{item.PUBLIC_DATE}}</td> <td>{{item.INDUSTRY}}</td> </tr> </table> </div> </div> </main> |
Javascript 語法
查詢動作向後端呼叫 GetList()
取得資料
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
<script> var Page = new Vue({ el: '#Page' , data: function () { var data = { form: { Q_MARKET_TYPE: "" , Q_ASSETS_TYPE:"" } , gridList: { datas: [] , RowCnt: 0 , CName:'' } }; return data; } , created: function () { var self = this; // 預設資料 self.form.Q_MARKET_TYPE = "TWSE"; self.form.Q_ASSETS_TYPE = "STOCK"; } , methods: { // 查詢 GetList: function () { var self = this; // 組合表單資料 var postData = {}; postData['Q_MARKET_TYPE'] = self.form.Q_MARKET_TYPE; postData['Q_ASSETS_TYPE'] = self.form.Q_ASSETS_TYPE; $.blockUI({ message: '處理中...' }); $.ajax({ url:'@Url.Action("GetList", "Home")', method:'POST', dataType:'json', data: { inModel: postData}, success: function (datas) { if (datas.ErrMsg) { alert(datas.ErrMsg); return; } self.gridList.datas = []; // 顯示列表 for (var i in datas.gridList) { var data = {}; for (var key in datas.gridList[i]) { data[key] = datas.gridList[i][key]; } self.gridList.datas.push(data); } // 資料筆數 self.gridList.RowCnt = datas.RowCnt; // 名稱 self.gridList.CName = datas.CName; // 解除畫面鎖定 $.unblockUI(); }, error: function (err) { alert(err.responseText); }, }); } } }) </script> |
後端 C# 邏輯
後端語法有用到一個新元件 HtmlAgilityPack 此套件主要是解析 HTML 標籤,取得 HtmlAgilityPack 方法在 NuGet 上搜尋名稱「HtmlAgilityPack」,我安裝時的版本為 1.11.33 。
安裝之後,在引用時就可以加入
using HtmlAgilityPack;
再來看一下主要的程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
/// <summary> /// 取得證交所上市及上櫃的股票及ETF清單 /// </summary> /// <param name="inModel"></param> /// <returns></returns> public ActionResult GetList(GetListIn inModel) { GetListOut outModel = new GetListOut(); // 檢查輸入來源 if (string.IsNullOrEmpty(inModel.Q_MARKET_TYPE)) { outModel.ErrMsg = "請輸入市場別"; return Json(outModel); } if (string.IsNullOrEmpty(inModel.Q_ASSETS_TYPE)) { outModel.ErrMsg = "請輸入資產別"; return Json(outModel); } string url = ""; string codeName = ""; string stockCode = ""; string stockName = ""; string marketType = ""; string industry = ""; string publicDate = ""; string assetType = ""; if (inModel.Q_MARKET_TYPE == "TWSE") { outModel.CName = "證交所"; // 來源網址 url = "https://isin.twse.com.tw/isin/C_public.jsp?strMode=2"; } else if (inModel.Q_MARKET_TYPE == "OTC") { outModel.CName = "櫃買中心"; // 來源網址 url = "https://isin.twse.com.tw/isin/C_public.jsp?strMode=4"; } WebClient webClient = new WebClient(); MemoryStream ms = new MemoryStream(webClient.DownloadData(url)); HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument(); doc.Load(ms, Encoding.Default); // 取得 HTML 標籤 HtmlNodeCollection trNode = doc.DocumentNode.SelectNodes("//table[2]/tr"); if (trNode != null) { // 建立輸出table DataTable dtTwseStockList = new DataTable(); dtTwseStockList.Columns.Add("SYMBOL_CODE"); dtTwseStockList.Columns.Add("SYMBOL_NAME"); dtTwseStockList.Columns.Add("MARKET_TYPE"); dtTwseStockList.Columns.Add("ASSETS_TYPE"); dtTwseStockList.Columns.Add("PUBLIC_DATE"); dtTwseStockList.Columns.Add("INDUSTRY"); foreach (HtmlNode tr in trNode) { stockCode = ""; HtmlNodeCollection tdNode = tr.SelectNodes("td"); if (tdNode.Count == 7) { string stockType = tdNode[5].InnerText; if (inModel.Q_MARKET_TYPE == "TWSE" && inModel.Q_ASSETS_TYPE == "STOCK") { // 取得證交所股票 if (stockType == "ESVUFR") { codeName = tdNode[0].InnerText; stockCode = codeName.Split(' ')[0]; stockName = codeName.Split(' ')[1]; publicDate = tdNode[2].InnerText; marketType = tdNode[3].InnerText; industry = tdNode[4].InnerText; assetType = "股票"; } } else if (inModel.Q_MARKET_TYPE == "TWSE" && inModel.Q_ASSETS_TYPE == "ETF") { // 取得證交所ETF if (stockType == "CEOGEU" || stockType == "CEOGDU" || stockType == "CEOGMU" || stockType == "CEOJEU" || stockType == "CEOIBU" || stockType == "CEOJLU" || stockType == "CEOGBU" || stockType == "CEOIEU" || stockType == "CEOGCU" || stockType == "CEOIRU") { codeName = tdNode[0].InnerText; stockCode = codeName.Split(' ')[0]; stockName = codeName.Split(' ')[1]; publicDate = tdNode[2].InnerText; marketType = tdNode[3].InnerText; industry = tdNode[4].InnerText; assetType = "ETF"; } } else if (inModel.Q_MARKET_TYPE == "OTC" && inModel.Q_ASSETS_TYPE == "STOCK") { // 取得櫃買中心股票 if (stockType == "ESVUFR") { codeName = tdNode[0].InnerText; stockCode = codeName.Split(' ')[0]; stockName = codeName.Split(' ')[1]; publicDate = tdNode[2].InnerText; marketType = tdNode[3].InnerText; industry = tdNode[4].InnerText; assetType = "股票"; } } else if (inModel.Q_MARKET_TYPE == "OTC" && inModel.Q_ASSETS_TYPE == "ETF") { // 取得櫃買中心ETF if (stockType == "CEOGBU" || stockType == "CEOGEU" || stockType == "CEOIBU" || stockType == "CEOIEU" || stockType == "CEOIRU" || stockType == "CEOJBU") { codeName = tdNode[0].InnerText; stockCode = codeName.Split(' ')[0]; stockName = codeName.Split(' ')[1]; publicDate = tdNode[2].InnerText; marketType = tdNode[3].InnerText; industry = tdNode[4].InnerText; assetType = "ETF"; } } if (stockCode != "") { // 加入 datatable DataRow drNew = dtTwseStockList.NewRow(); drNew["SYMBOL_CODE"] = stockCode; drNew["SYMBOL_NAME"] = stockName; drNew["MARKET_TYPE"] = marketType; drNew["ASSETS_TYPE"] = assetType; drNew["PUBLIC_DATE"] = publicDate; drNew["INDUSTRY"] = industry; dtTwseStockList.Rows.Add(drNew); } } } // 輸出資料 outModel.gridList = new List<StockRow>(); foreach (DataRow dr in dtTwseStockList.Rows) { StockRow row = new StockRow(); row.SYMBOL_CODE = dr["SYMBOL_CODE"].ToString(); row.SYMBOL_NAME = dr["SYMBOL_NAME"].ToString(); row.MARKET_TYPE = dr["MARKET_TYPE"].ToString(); row.ASSETS_TYPE = dr["ASSETS_TYPE"].ToString(); row.PUBLIC_DATE = dr["PUBLIC_DATE"].ToString(); row.INDUSTRY = dr["INDUSTRY"].ToString(); outModel.gridList.Add(row); } outModel.RowCnt = dtTwseStockList.Rows.Count; } // 回傳 Json 給前端 return Json(outModel); } |
此段程式碼主要是呼叫 https://isin.twse.com.tw/isin/C_public.jsp?strMode=2 或是 https://isin.twse.com.tw/isin/C_public.jsp?strMode=4 取得 HTML 原始檔,再從原始檔之中擷取表格資料。
擷取股票及 ETF 有使用到一些條件: ESVUFR, CEOGBU, CEOIEU, CEOGCU…
這些是我依照股票或是 ETF 他們分類而歸納的條件,只取得需要的資料加入在 DataTable 裡面。
最後只要將 DataTable 裡面的資料呈現在前端網頁上即可。
這個範例就簡單演示取證交所網頁的資料,大家可以拿去應用在自己的專案裡面,要記得擷取 HTML 原始碼都會因為來源網址改版而失效喔,應用在實務上時需要一些檢查條件和讀取錯誤判斷喔。
重點整理
- 有網址就可以查詢上市上櫃股票清單
- 使用 WebClient 取得網頁程式碼
- 使用 HtmlAgilityPack 爬蟲網頁語法
- 分析 HTML 取出資料
範例下載
實際網頁專案開發範例
此連結是我實際應用在網頁上呈現的範例: Winvest 雲投資
相關學習文章
開發應用網站
如果你在學習上有不懂的地方,需要諮詢服務,可以參考站長服務,我想辨法解決你的問題
如果文章內容有過時、不適用或錯誤的地方,幫我在下方留言通知我一下,謝謝