2017年11月13日 星期一

證交所上市櫃股票列表

由於證交所網站在 2017-05-23 左右改版, 新版網頁拋棄全部資訊集中顯示在一張網頁的方式, 改採輸入股號股名查詢, 導致之前寫的爬蟲軟體無法再抓取到資料, 必須進行改版. 之前一頁式網頁股票名單就在網頁表單中, 只要單純剖析網頁即可獲得全部收盤資料, 但現在得分成兩步驟, 首先須取得最新上市櫃股票名單, 然後再一一查詢各股收盤資料. 但問題是如何得知最新的上市櫃股票名單呢?

原來證交所網站就有提供此最新名單, 參考 :

本國上市證券國際證券辨識號碼一覽表
本國上櫃證券國際證券辨識號碼一覽表

這兩張表格都只需要抓 "股票" 與 "ETF" 部分, 剖析表格中的 "有價證券代號及名稱", "上市日", "市場別", "產業別" 這四個欄位即可, 將其存入 stocks_list 資料表的 stock_id, stock_name, listed_date, market, 以及 category 等 5 個欄位中, 其中 listed_date 與 market 是此次改版要新增的欄位.


上市股票

上市 ETF

上櫃股票

上櫃 ETF


以擷取上市股票資料為例, 開頭位置在標示 "股票" 這一列; 而結束位置在 標示 "上市認購(售)權證" 字樣前面, 如下所示 :




檢視網頁原始碼可以找到具體的開始與結束位置 (保留 tr), 如下所示 :





用這兩個頭尾信標為界就可以抓到上市股票的資料了.

$start="<B> 股票 <B> </td></tr>";  //起始字串
$end="<tr><td bgcolor=#FAFAD2 colspan=7 ><B> 上市認購(售)權證 <B>";   //結束字串

抓下來之後就用迴圈一列列去拆解表格內容, 在迴圈內又將每一列的儲存格拆解成陣列, 這樣就可分離出各欄資料了. 注意, 在拆解股名與股號時要注意, 分隔兩者的是全形空白, 我是直接由網頁複製到記事本, 再複製到程式中 :

list($stock_id,$stock_name)=explode(" ", $brr[0]); //分出股號與股名

抓 ETF 方式一樣, 只要更改頭尾信標即可 :





$start="<B> ETF <B> </td></tr>";  //起始字串
$end="<tr><td bgcolor=#FAFAD2 colspan=7 ><B> 臺灣存託憑證(TDR) <B>";   //結束字串

注意, 證交所的原始網頁使用 MS950 編碼, 可從網頁的標頭中看出 :

Server: Apache-Coyote/1.1
Content-Type: text/html;charset=MS950 
Transfer-Encoding: chunked
Date: Tue, 14 Nov 2017 12:54:07 GMT

MS950 是在 BIG5 碼的基礎上進行的編碼擴充, 增加了 7 個中文字以及一些符號, 也是 Windows 繁中版預設字元集, 關於編碼參考 :



在 PHP 中 MS950 編碼可以當成 BIG5 碼來處理, 但是以前用 iconv() 將 BIG5 轉成 UTF-8 都沒問題 : 

$file=iconv("BIG5","UTF-8",$web_page['FILE']);  //MS950 轉 UTF-8 有問題

但這回用此指令轉換後, 剖析時讀取到宏碁的 "碁" 時卻停止轉換, WHY?  後來在下面這篇看到作者用 mb_convert_encoding() 函數做轉換 : 


$file=mb_convert_encoding($web_page['FILE'],"utf8","big5");

這樣就沒問題了. 如果要偵測一個字串是甚麼編碼則要用 mb_detect_encoding() 函數, 參考 :


由上市日期可以計算至今該公司已上市多久, 在選股時通常上市未滿 5 年者我都不予考慮. 從證交所網頁取得的資料經剖析後每一列會放在陣列 $brr 中, 上市日期位於第三欄, 因此索引為 2 即 $brr[2], 格式為 YYYY/MM/DD, 先用 str_replace() 將 "/" 換成 "-",  拆出年月日後呼叫 mktime() 轉成時戳, 再與目前時戳相減即得上市秒數, 最後轉換成年 :

  $listed_date=str_replace("/","-",$brr[2]);
  list($Y,$M,$D)=explode("-",$listed_date); //拆分 1987-02-21
  $listed_time=mktime(0,0,0,$M,$D,$Y); //計算 1970-01-01 以來至上市日秒數
  $listed_years=round((time()-$listed_time)/3600/24/365,1); //小數一位

另外, 漲跌家數資訊已改在下列網頁提供 :


沒有留言 :