[C#] 取得公開資訊觀測站股票基本資料(上市、上櫃、興櫃

文章推薦指數: 80 %
投票人數:10人

公開資訊觀測站. 網址為: https://mops.twse.com.tw/mops/web/t51sb01 會看到所有上市、上櫃、興櫃、公開發行公司的「基本資料查詢彙總表」 Skiptocontent 在公開資訊觀測站可以查到股票的基本資料、財報、股東會、重大訊息等資料。

官網連結:公開資訊觀測站 我這次要講解的是如何利用C#ASP.NetMVC取得公開資訊觀測站的股票基本資料。

在公開資訊觀測站可以點選「彙總報表>基本資料>基本資料查詢彙總表」開啟頁面。

網址為:https://mops.twse.com.tw/mops/web/t51sb01 會看到所有上市、上櫃、興櫃、公開發行公司的「基本資料查詢彙總表」 在不選產業別下直接按「查詢」鈕就會取得所有上市公司的基本資料 查詢結果還提供了「另存CSV」的下載連結。

下載檔案是CSV格式,可以利用程式讀取檔案內容 此CSV格式的表頭欄位包含這些 "公司代號","公司名稱","公司簡稱","產業類別","外國企業註冊地國","住址","營利事業統一編號","董事長","總經理","發言人","發言人職稱","代理發言人","總機電話","成立日期","上市日期","普通股每股面額","實收資本額(元)","已發行普通股數或TDR原發行股數","私募普通股(股)","特別股(股)","編製財務報告類型","普通股盈餘分派或虧損撥補頻率","普通股年度(含第4季或後半年度)現金股息及紅利決議層級","股票過戶機構","過戶電話","過戶地址","簽證會計師事務所","簽證會計師1","簽證會計師2","英文簡稱","英文通訊地址","傳真機號碼","電子郵件信箱","公司網址","投資人關係聯絡人","投資人關係聯絡人職稱","投資人關係聯絡電話","投資人關係聯絡電子郵件","公司網站內利害關係人專區網址" 1 "公司代號","公司名稱","公司簡稱","產業類別","外國企業註冊地國","住址","營利事業統一編號","董事長","總經理","發言人","發言人職稱","代理發言人","總機電話","成立日期","上市日期","普通股每股面額","實收資本額(元)","已發行普通股數或TDR原發行股數","私募普通股(股)","特別股(股)","編製財務報告類型","普通股盈餘分派或虧損撥補頻率","普通股年度(含第4季或後半年度)現金股息及紅利決議層級","股票過戶機構","過戶電話","過戶地址","簽證會計師事務所","簽證會計師1","簽證會計師2","英文簡稱","英文通訊地址","傳真機號碼","電子郵件信箱","公司網址","投資人關係聯絡人","投資人關係聯絡人職稱","投資人關係聯絡電話","投資人關係聯絡電子郵件","公司網站內利害關係人專區網址" 目前查出來的公司就有965筆資料,利用程式讀取大量資料是比較合適的方式。

接下來為講解如何利用C#取得所有股票的基本資料CSV檔案,並讀取CSV檔案內容。

目錄1手動取得「查詢」呼叫網址2手動取得CSV呼叫網址3C#取得查詢網址3.1HTML前端View語法3.2Javascript前端View語法3.3C#後端Controller語法3.4GetData()方法3.5C#後端Model語法3.6例外處理3.7測試注意事項4重點整理4.1範例下載 手動取得「查詢」呼叫網址 在「查詢」鈕按右鍵>檢查 在Elements頁籤裡面會看到指標停在「查詢」的HTML上面,然後往上找最近的

標籤,看到
標籤指向的網址為/mops/web/ajax_t51sb01 在DevTools有開啟的情況下,再按一次剛剛的查詢鈕 這時候上面頁籤切換到「Network」,然後往下找到「ajax_t51sb01」的名稱。

點擊名稱後,右邊頁籤選擇「Header」,可以看到RequestURL以及下面的FormData 點擊FormData>viewsource可以看到組合參數 網址RequestURL:https://mops.twse.com.tw/mops/web/ajax_t51sb01 參數FormData:encodeURIComponent=1&step=1&firstin=1&TYPEK=sii&code= 網址及參數將兩者用“?”合併起來為 https://mops.twse.com.tw/mops/web/ajax_t51sb01?encodeURIComponent=1&step=1&firstin=1&TYPEK=sii&code= 就是查詢鈕背後傳送的網址。

單獨執行此網址會回傳HTML型式的資料 雖然此頁面已經可以利用HTMLParser解析來取得資料,但為了更好解讀資料,還是再取得CSV檔案來解讀會比較簡單。

手動取得CSV呼叫網址 在「另存CSV」鈕按右鍵>檢查 在頁籤「Elements」裡面會停在「另存CSV」鈕的HTML上,然後往上找到最近的
標籤。

會看到指向目標為/mops/web/ajax_t51sb01 在
底下有3個參數 這3個參數是呼叫ajax_t51sb01時傳送欄位 在DevTools開啟的情況下,按一次「另存CSV」鈕,然後頁籤切換到「Network」 點擊名稱「t105sb02」,然後記錄右邊的「RequestURL」及「FormData」參數 點擊FormData>viewsource可以看到組合參數 網址RequestURL:https://mops.twse.com.tw/server-java/t105sb02 參數FormData:firstin=true&step=10&filename=t51sb01_20210523_111213410.csv 網址及參數將兩者用“?”合併起來為 https://mops.twse.com.tw/server-java/t105sb02?firstin=true&step=10&filename=t51sb01_20210523_111213410.csv 就是「另存CSV」鈕背後傳送的網址。

單獨執行此網址會下載CSV檔案資料,此CSV檔案是程式要解析的資料。

要分2步驟取得CSV網址是因為CSV網址參數會一直改變,由第1步查詢時取得參數,才能再取得CSV網址參數。

以上的說明是手動取得網址的方式,重要的是知道背後呼叫的網址,有了網址之後,接下來就會轉換成C#語法呼叫網址取得CSV檔案後解讀資料。

範例建置環境 後端架構:C#ASP.NetMVC.NetFramework 前端架構:Vue.js,jQuery,Bootstrap 使用VisualStudio建立ASP.NetMVC專案,我用新專案取得公開資訊觀測站的股票基本資料為範例說明,最下方會提供此範例下載。

C#取得查詢網址 這是我簡單設計的介面,只提供市場別選擇,查詢結果包含全部產業別。

查詢結果欄位我只顯示前面幾個欄位示範就好,下載CSV時會包含所有欄位,實務應用時可以依所需欄位讀取。

可以選擇上市、上櫃、興櫃、公開發行查詢 可以選擇上市、上櫃、興櫃、公開發行查詢,查詢後就會將CSV檔案內容呈現在網頁上。

HTML前端View語法 XHTML 查詢條件 市場別 上市 上櫃 興櫃 公開發行 查詢 股票基本資料 公司代號 公司名稱 公司簡稱 產業類別 外國企業註冊地國 住址 營利事業統一編號 董事長 {{item.CompanyCode}} {{item.CompanyName}} {{item.CompanyAbbreviation}} {{item.IndustryCategory}} {{item.ForeignCompanyRegistrationCountry}} {{item.Address}} {{item.UniformNumberProfitBusiness}} {{item.Chairman}} 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253             查詢條件                                                        市場別                                                                            上市                        上櫃                        興櫃                        公開發行                                                                            查詢                                     股票基本資料                                                                公司代號                    公司名稱                    公司簡稱                    產業類別                    外國企業註冊地國                    住址                    營利事業統一編號                    董事長                                                    {{item.CompanyCode}}                    {{item.CompanyName}}                    {{item.CompanyAbbreviation}}                    {{item.IndustryCategory}}                    {{item.ForeignCompanyRegistrationCountry}}                    {{item.Address}}                    {{item.UniformNumberProfitBusiness}}                    {{item.Chairman}}                                         Javascript前端View語法 JavaScript 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152 C#後端Controller語法 後端語法有用到一個新元件HtmlAgilityPack此套件主要是解析HTML標籤,取得HtmlAgilityPack方法在NuGet上搜尋名稱「HtmlAgilityPack」,我安裝時的版本為1.11.33。

安裝之後,在引用時就可以加入 usingHtmlAgilityPack; GetData()方法 C# /// ///查到股票基本資料 /// /// /// publicActionResultGetData(GetDataIninModel) { GetDataOutoutModel=newGetDataOut(); if(string.IsNullOrEmpty(inModel.Q_MARKET_TYPE)) { outModel.ErrMsg="請選擇市場別"; returnJson(outModel); } outModel.gridList=newList(); WebClientwebClient=newWebClient(); HtmlAgilityPack.HtmlDocumentdoc=newHtmlAgilityPack.HtmlDocument(); //市場別網址 stringQueryUrl="https://mops.twse.com.tw/mops/web/ajax_t51sb01?encodeURIComponent=1&step=1&firstin=1&TYPEK={0}&code="; QueryUrl=string.Format(QueryUrl,inModel.Q_MARKET_TYPE); //取得查詢回傳 MemoryStreamms=newMemoryStream(webClient.DownloadData(QueryUrl)); doc.Load(ms,Encoding.Default); //取得另存CSVform HtmlNodeCollectionformNode=doc.DocumentNode.SelectNodes("//form[@name='fm']"); if(formNode!=null) { //取得欄位 HtmlNodefilenameNode=doc.DocumentNode.SelectSingleNode("//form[@name='fm']/input[@name='filename']"); stringfilenameValue=filenameNode.GetAttributeValue("value",""); //CSV網址 stringcsvUrl="https://mops.twse.com.tw/server-java/t105sb02?firstin=true&step=10&filename={0}"; csvUrl=string.Format(csvUrl,filenameValue); //呼叫CSV網址 stringcsvData=webClient.DownloadString(csvUrl); if(csvData.Trim().Length>0) { DataTabledt=newDataTable(); string[]lineStrs=csvData.Split('\n'); for(inti=0;i ///解析CSV /// /// /// privatevoidParseCSVData(ArrayListresult,stringdata) { intposition=-1; while(position ///解析CSV /// /// /// /// privatestringParseCSVField(refstringdata,refintStartSeperatorPos) { if(StartSeperatorPos==data.Length-1) { StartSeperatorPos++; return""; } intfromPos=StartSeperatorPos+1; if(data[fromPos]=='"') { intnextSingleQuote=GetSingleQuote(data,fromPos+1); StartSeperatorPos=nextSingleQuote+1; stringtempString=data.Substring(fromPos+1,nextSingleQuote-fromPos-1); tempString=tempString.Replace("'","''"); returntempString.Replace("\"\"","\""); } intnextComma=data.IndexOf(',',fromPos); if(nextComma==-1) { StartSeperatorPos=data.Length; returndata.Substring(fromPos); } else { StartSeperatorPos=nextComma; returndata.Substring(fromPos,nextComma-fromPos); } } /// ///解析CSV /// /// /// /// privateintGetSingleQuote(stringdata,intSFrom) { inti=SFrom-1; while(++i///查到股票基本資料/////////publicActionResultGetData(GetDataIninModel){ GetDataOutoutModel=newGetDataOut();  if(string.IsNullOrEmpty(inModel.Q_MARKET_TYPE)) { outModel.ErrMsg="請選擇市場別"; returnJson(outModel); }  outModel.gridList=newList(); WebClientwebClient=newWebClient();  HtmlAgilityPack.HtmlDocumentdoc=newHtmlAgilityPack.HtmlDocument();  //市場別網址 stringQueryUrl="https://mops.twse.com.tw/mops/web/ajax_t51sb01?encodeURIComponent=1&step=1&firstin=1&TYPEK={0}&code="; QueryUrl=string.Format(QueryUrl,inModel.Q_MARKET_TYPE);  //取得查詢回傳 MemoryStreamms=newMemoryStream(webClient.DownloadData(QueryUrl)); doc.Load(ms,Encoding.Default);  //取得另存CSVform HtmlNodeCollectionformNode=doc.DocumentNode.SelectNodes("//form[@name='fm']"); if(formNode!=null) { //取得欄位 HtmlNodefilenameNode=doc.DocumentNode.SelectSingleNode("//form[@name='fm']/input[@name='filename']"); stringfilenameValue=filenameNode.GetAttributeValue("value","");  //CSV網址 stringcsvUrl="https://mops.twse.com.tw/server-java/t105sb02?firstin=true&step=10&filename={0}"; csvUrl=string.Format(csvUrl,filenameValue);  //呼叫CSV網址 stringcsvData=webClient.DownloadString(csvUrl);  if(csvData.Trim().Length>0) { DataTabledt=newDataTable(); string[]lineStrs=csvData.Split('\n'); for(inti=0;i///解析CSV/////////privatevoidParseCSVData(ArrayListresult,stringdata){ intposition=-1; while(position///解析CSV////////////privatestringParseCSVField(refstringdata,refintStartSeperatorPos){ if(StartSeperatorPos==data.Length-1) { StartSeperatorPos++; return""; }  intfromPos=StartSeperatorPos+1; if(data[fromPos]=='"') { intnextSingleQuote=GetSingleQuote(data,fromPos+1); StartSeperatorPos=nextSingleQuote+1; stringtempString=data.Substring(fromPos+1,nextSingleQuote-fromPos-1); tempString=tempString.Replace("'","''"); returntempString.Replace("\"\"","\""); }  intnextComma=data.IndexOf(',',fromPos); if(nextComma==-1) { StartSeperatorPos=data.Length; returndata.Substring(fromPos); } else { StartSeperatorPos=nextComma; returndata.Substring(fromPos,nextComma-fromPos); }} //////解析CSV////////////privateintGetSingleQuote(stringdata,intSFrom){ inti=SFrom-1; while(++igridList{get;set;} } publicclassStockRow { //只示範前面幾個欄位 publicstringCompanyCode{get;set;} publicstringCompanyName{get;set;} publicstringCompanyAbbreviation{get;set;} publicstringIndustryCategory{get;set;} publicstringForeignCompanyRegistrationCountry{get;set;} publicstringAddress{get;set;} publicstringUniformNumberProfitBusiness{get;set;} publicstringChairman{get;set;} publicstringGeneralManage{get;set;} } 123456789101112131415161718192021222324 publicclassGetDataIn{ public  stringQ_MARKET_TYPE{get;set;}} publicclassGetDataOut{ publicstringErrMsg{get;set;} publicListgridList{get;set;}} publicclassStockRow{ //只示範前面幾個欄位 publicstringCompanyCode{get;set;} publicstringCompanyName{get;set;} publicstringCompanyAbbreviation{get;set;} publicstringIndustryCategory{get;set;} publicstringForeignCompanyRegistrationCountry{get;set;} publicstringAddress{get;set;} publicstringUniformNumberProfitBusiness{get;set;} publicstringChairman{get;set;} publicstringGeneralManage{get;set;}} 例外處理 以上語法在Controller執行webClient.DownloadData()語法時會出現錯誤 System.Net.WebException:‘伺服器認可通訊協定違規.Section=ResponseStatusLine’ 此錯誤問題是WebClient是對HttpWebrequest進行了封裝, Theservercommittedaprotocolviolation.Section=ResponseHeaderDetail=CRmustbefollowedbyLF微軟沒有容忍不符合RFC822中的httpHeader必須以CRLF結束的規定服務器響應。

解決方法是Web.config增加一段設定 XHTML 12345                    測試注意事項 此範例的資料來源是公開資訊觀測站,而公開資訊觀測站會避免使用者密集頻繁的讀取資料,若發現此情況,則會立即封鎖IP,導致使用者無法再使用網站,這是為了安全著想,避免網站因DoS(denial-of-serviceattack)被攻擊。

若太密集頻繁查詢而被封鎖IP,我測試時就發生了一次,實際封鎖時間不知道,但隔一天後就可以正常使用了,所以提醒大家不要太密集頻繁的向公開資訊觀測站查詢資料,包含證交所也是,太密集頻繁查詢也是會被封鎖IP的。

重點整理 利用程式模擬網頁下載CSV資料 使用WebClient取得網頁原始碼 使用HtmlAgilityPack解析原始碼 解析CSV取出有用資料 範例下載 以上是C#ASP.NetMVC取得公開資訊觀測站的股票基本資料範例,若需要下載範例可以點擊:範例下載 相關學習文章 [C#]取得證交所台股價格的3種實用方法(附範例下載) [C#]取得證交所上市及上櫃的股票及ETF清單(附範例) 開發應用網站 WinTog雲投資 如果你在學習上有不懂的地方,可以參考線上家教服務 如果你喜歡這篇文章,請幫我在上方綠色的拍手圖示按5下使用Google或FB免費登入,你的鼓勵支持我繼續創作,寫出好的文章 [加入社團一起討論]或是[追蹤Instagram] 文章導覽 [ASP.NETMVC]如何套用Bootstrap網頁設計樣版SBAdmin2開發網站管理介面(附範例)[C#]產生MSSQLTableDML(SELECT,INSERT,UPDATE,DELETE)SQL語法 2thoughtson“[C#]取得公開資訊觀測站股票基本資料(上市、上櫃、興櫃、公開發行)(附範例下載)” 請問這樣算爬蟲嗎? 回覆 這樣算是啊 回覆 發佈留言取消回覆發佈留言必須填寫的電子郵件地址不會公開。

必填欄位標示為*留言顯示名稱* 電子郵件地址* 個人網站網址 在瀏覽器中儲存顯示名稱、電子郵件地址及個人網站網址,以供下次發佈留言時使用。

熱門文章 WindowsServer如何安裝SQLServer2019免費開發版-12,888views如何申請免費Let’sEncryptSSL自動更新憑證,自架IIS站台適用-8,428views[C#]取得證交所台股價格的3種實用方法(附範例)-6,788viewsSQLServer每日定期備份與定期刪除舊有備份檔-6,097views如何將亂碼簡體檔名、資料夾在繁體電腦正常顯示-使用ConvertZZ免費軟體-4,985viewsWindowsServer安裝MySQLCommunity免費社群版-4,502views[開箱]ASUSE510MA夢幻白15.6吋平價文書筆電開箱評價-4,334viewsWindowsServer2019如何安裝IIS運行ASP.NET專案-4,102views[ASP.NETMVC]如何套用Bootstrap網頁設計樣版SBAdmin2開發網站管理介面(附範例)-3,841viewsWindowsServerIIS如何安裝PHP網頁伺服器-3,791views 贊助 近期文章 以太坊ETH2.0升級說明及Binance幣安鎖倉質押教學 [ASP.NETMVC]產生Bootstrap+Vue.js多層式選單範本教學(附範例) MaiCoin提幣ETH以太幣至Binance幣安錢包充值教學 加密貨幣交易所Binance幣安API開通教學 Kucoin提幣ETH至Pionex派網充值教學 學習目標在這裡我會向你分享網頁設計相關的技術文章。

包含WordPress,ASP.NetC#為主的技術文章。

以及伺服器管理及資料庫應用的知識文章。

我是Mars,我們一起學習成長吧。

是否有問題需要我協助,或是網頁設計及WordPress提案合作,可與我聯絡。

訂閱電子報 訂閱電子報 當有最新文章時,會發送郵件通知你。

訂閱 Loading... 感謝你的訂閱 請至你的信箱開啟驗證確認信。

訂閱完成後,未來有新文章時,將會寄發郵件通知你。

贊助


請為這篇文章評分?