2016年12月31日 星期六

如何剖析不標準的網頁

最近在寫 PHP 爬蟲程式時遇到一個比較特殊的網頁, 需要費一點功夫來處理, 雖然也不算太麻煩, 但因為值得以後處理類似網頁時參考, 於是就抽出一點時間記錄一下. 這網頁就是鉅亨網的類股行情表 :

# 鉅亨網類股行情表

此網頁將集中市場每日盤後各產業分類的成交量, 比重, 以及指數漲跌幅等資料做成一個報表, 且可查詢近一周的資訊. 紀錄各分類所佔比重變化可以知道資金往哪個產業移動, 若比重逐漸增加, 表示該產業逐漸成長增溫, 資金流向此產業將推升股價, 特別是該產業的龍頭股; 反之則是該產業逐漸降溫, 有可能走入景氣循環的下坡, 市場籌碼漸漸抽出. 另外從台股產業龍頭的電子股與金融股也可以看出市場交易的冷熱情形, 電子類股占比若低於 50% 表示市場過冷; 而金融類股占比若超過 10% 表示市場過熱.

檢視此網頁的原始碼可知, 此網頁的類股資料放在 <!-- 資料區塊:start --> 與 <!-- 資料區塊:end --> 之間的 <div> 區塊裡, 裡面含有三個表格, 而我們需要的類股資料放在第三個表格中的 tbody 元素裡面, 因此我會以 thead 末尾的幾個欄位標題當作開頭標誌, 以 </tbody> 當作結束標誌, 這樣就能輕易抓出 tbody 內部的一堆 tr 與 td 所構成的儲存格內容. 擷取網頁的程式碼如下 :

//===擷取目標網頁 : 鉅亨網類股行情表===
$target="http://www.cnyes.com/twstock/a_price4.aspx";
echo "<br>擷取鉅亨網類股行情報表 : <a href='$target' target='_blank'>原始網頁</a><br>";
$ref=$target;
$web_page=http_get($target,$ref); //下載網頁檔
$file=$web_page['FILE'];
$file=preg_replace("/([\s]{2,})/","",$file); //先去除多餘空白以利尋標

這裡我們用 preg_replace() 函數將原始網頁中的多餘空白去除, 包含跳行字元, 因此取得的原始碼字元串會變得很緊緻, 有利於後續剖析. 接下來要抓出 tbody 內的儲存格資料, 程式碼如下 :

//擷取表格內容 : 最後一個 table
$start='市值(億)</th><th>增減</th></tr></thead><tbody>';
$end="</tbody>";
$data=return_between($file, $start, $end, EXCL);

這裡 $start 就是該表格的 thead 最後面的幾個欄位直到 <tbody>, 具有唯一性 (才不會找錯資料), 呼叫 return_between() 函數後會傳回開頭與結尾中間的儲存格內容, 包含 tr 與 td 元素. 不過這些元素含有樣式屬性與元素 (font), 必須去除才好處理, 程式碼如下 :

//純化表格:去除屬性與不需要的元素
$data=str_replace(' bgcolor="#FFFFFF"', "", $data);; //去除 <tr> 屬性
$data=str_replace(' nowrap align="center"', "", $data);; //去除 <td> 屬性
$data=str_replace(' nowrap align="right"', "", $data);; //去除 <td> 屬性
$data=remove($data, "<font", ">"); //去除 font
$data=str_replace('</font>', "", $data);; //去除 font

經過這樣處理後的 tr 與 td 序列就全部乾淨了, 只含有 tr, td 以及資料. 但是仔細看這些 tr 與 td 元素發現, 這些表格元素不是標準的 HTML 格式, 例如部分 tr 為 <tr >, 裡面含有一個空格; 其次它沒有 tr 與 td 的結束標籤, 格式如下 :

<tr >
  <td>電子
  <td>324.97
  <td>64.69
  ...
<tr>
  <td>金融
  <td>37.33
  <td>7.43
  ...

缺乏結束標籤會造成剖析上的困難, 因為 parse_array() 與 remove() 函數都需要開頭與結束標籤來尋標. 解決之道是對其進行矯正, 修正為標準 HTML 格式. 程式碼如下 :

//修正 HTML 格式
$data=str_replace('<tr >', "<tr>", $data);; //矯正 <tr >
$data=str_replace('<tr><td>', "</tr><tr><td>", $data); //補上 </tr>
$data=substr($data, 5); //刪除最前面多加的 </tr>
$data .= "</tr>";
echo "<br>類股比重漲跌<br><table border=1>".$data."</table><br>";

這裡首先用 str_replace() 將 <tr > 全部改成 <tr>, 去除 tr 中的空格, 其次用 str_replace() 將 <tr><td> 全部改成 </tr><tr><td>, 亦即補上缺漏的 </tr>, 但是這樣一來最前面就會多出一個 </tr> 了, 因此須用 substr() 把前面 5 個字元刪掉. 這樣就把 $data 變成標準的 HTML 格式了.

咦, td 不是還缺結束標籤嗎? 是的, 但這可以不需要處理, 因為拆分欄位不需要再次使用 parse_array(), 只要用 <td> 當拆分符去拆解每一列, 就可以取得所需要的比重與漲幅欄位了, 程式碼如下 :

$arr=parse_array($data, "<tr", "/tr>");  //拆分每一列 (共 30 列)
for ($i=0; $i<count($arr); $i++) { //拜訪每一列 (每一支股票)
  $brr=explode("<td>", $arr[$i]); //拆分每一欄
  $sector=$brr[1];  //類別
  $ratio=$brr[3];   //比重
  $delta=$brr[7];   //漲幅
  echo "$sector : 比重=$ratio% 漲跌=$delta%<br>";
  }

這裡先拆分 tr 列, 然後在迴圈中用 explode 拆分行, 類別欄位在第 1 欄 (索引 1), 比重在第 3 欄 (索引 3), 而指數漲幅在第 7 欄 (索引 7). 咦, 第一欄不是索引 0 嗎? 沒錯, 但因為用 <td> 當分界符, 所以索引 0 是一個空值. 舉個簡單的範例來說明會比較明白 :

$str="<td>1<td>2<td>3";  //沒有結尾 </td> 的儲存格
$arr=explode("<td>",$str);  //[0]為空白, [1]為 1,...
print_r($arr);

在這個範例中, 以  <td> 拆分字串時, 第一個  <td>  前面的空值 "" 是第一個被拆除來的子字串, 它會變成索引 0, 而第一欄的資料會在索引 1, 依此類推.

網頁格式不一定是標準的 HTML, 剖析處理時要視情況個別處理, 以上只是一種特例.

2016年12月30日 星期五

買養命酒與消費高手好關鍵

昨天去巷子口幫二哥買枇杷膏時看見貨架上有擺養命酒, 就問老闆一瓶多少, 答說那是 1000 毫升的, 要 1000 元. 本想買一瓶喝看看是否冬天手腳冰冷的情況會改善. 但轉頭一想, 還是回去比個價再說.

由於年底還有一天休假必休, 所以早上就在家上網查詢, 發現東森購物最便宜的是兩瓶 1599 元, 一瓶 800 元有找, 比巷子口藥局便宜兩百塊! 因此我就買了兩份共 3189 元.

# 【養命酒】藥用養命酒1000mlX2入組



另外, 爸的退化性 (髖) 關節炎四月份時小舅媽介紹去管醫師那裏, 吃坡尿酸剛開始效果非常顯著, 但之後似乎就疲乏了. 最近電視上廣告打得兇的消費高手好關鍵, 據說對退化性關節炎有改善效果, 所以我就進行了一番調查, 才曉得原來膠原蛋白有好幾種型, 第一型就是一般我們所認知的跟防止皮膚退化有關的, 而第二型才是跟關節有關的, 參考 :

關節不卡卡:非變性第二型膠原蛋白(UC-II)的實證
見証『 UC-II® - 非變性二型膠原蛋白(Undenatured Collagen Type II) 』
非變性二型膠原蛋白膠囊,保留膠原蛋白完整活性 讓你步伐好輕盈

消費高手好關鍵主要含非變性二型膠原蛋白, 由雞冠萃取的坡尿酸, 以及橄欖果萃取物. 除了消費高手外, 美商健而婷 NOW 也有類似商品 :

NOW健而婷-UCII 二型膠原蛋白 (60顆/瓶) $1350 (5 折)

不過它跟消費高手不同處在於它不含坡尿酸, 而是添加海藻鈣. 考慮了一早上, 最後還是買了消費高手好關鍵 3 入兩組共 6 瓶, 任買兩組折 1000 相當於 69 折共 9560 元.

【3.4倍升級】破億!愛用者好動見證★升級版★消費高手好關鍵(3盒組)


根據說明這要空腹吃, 搭配維他命 C 更好吸收. 等到貨後再帶回去鄉下給爸吃看看.

市立圖書館的管理問題

今天超不開心, 中午去河堤還書時, 志工阿姨說其中一本 C 語言的書應還光碟, 我說沒有借光碟啊! 因為我若有借光碟一定會用膠帶黏在書後以防遺失 (我是如此謹慎+龜毛的人), 但電腦註記我有借光碟, 只是光碟編號 2821 與館內實際那片 2812 不同, 這根本就是之前的志工記錯了, 第一, 我沒借記成有借, 第二, 光碟編號還記錯. 但是那個阿姨還是說這本書不能還, 要我回去找看看光碟, 這不就是要我跑兩趟嗎?

我對物品管理很有自信, 自知回去根本找不到那片光碟, 我就問那戴眼鏡的阿姨, 如果找不到光碟怎麼辦? 她說那就要買一本書來賠. 哇咧! 如果是志工打錯也要我賠? 我又不是沒經驗, 以前在左新就遇過明明剛拿去還的一本書, 三天後接到 EMAIL 通知說那本書逾期沒還. 我趕緊跑去澄清說已經還了, 但是館員與志工怎麼找也找不到, 電話查詢原藏書分館也說架上沒有, 逾期未還. 這下我真是跳到黃河也洗不清, 跟館長說你們要在櫃台架攝影機, 以免有爭議時吵不完. 如果再找不到我就要買書來賠了, 結果幾天後左新通知我, 書找到了, 因為志工把它放在櫃台底下忘記刷還了. 哇哩咧.

不只這樣, 幾年前還接過前鎮分館來電, 說我們在前鎮借了好多本書未還, 我連左新都嫌遠了, 還大老遠跑到前鎮去借? 電腦資料錯亂了張冠李戴亂栽贓, 我說這樣無憑無據對用書人非常不公平, 因為我們無法舉證, 請他們再查看看電腦哪裡出問題. 後來就沒再打來了, 我覺得至少也要打個電話道個歉吧.

為了避免這個困擾, 我決定以後非必要不借光碟, 跨館一定會有光碟就沒辦法, 只好用多重膠帶給它黏牢. 要不然圖書館志工來來去去對業務不熟悉以及少一根筋的大有人在, 隨便這樣亂一下就有我們受的了.

晚上我又再跑一趟河堤, 索性將一批最近不可能有時間看的 C 語言書全還了. 這回換一位歐吉桑, 他中午也知道這件事, 但在忙別櫃未介入, 他說光碟在館內 (也就是說我根本沒借光碟), 編號打錯, 就收書還了. 真是的, 可以一次搞定的事偏要我們跑兩趟. 要不是家裡沒地方擺書, 我寧可去書局買一本, 才不想這樣奔波去借書呢!

關於 cron jobs 執行頻率的調整問題

這兩周來已把 PHP 專案中的 cron jobs 程式大致寫完, 目前執行頻率大都是 5 分鐘, 但是手動執行時偶而會收到伺服器會回應 "Resource limit reached", 詢問線上客服, 他們回覆說是因為我的 Premium 方案可使用的 CPU 資源已超過之故, 這可能是我設定每五分鐘執行一次, 每一個 cron job 大約都在 0, 5, 10, ... 分同時執行的關係. 雖然等個幾秒鐘重試即恢復正常, 但顯然執行頻率似乎需要調整.

其實 Cron jobs 大致可分成兩類, 一種是所擷取的目標網頁是個別公司的資料, 因此需要靠指標來分批擷取, 也是這次要將擷取頻率從每 5 分鐘一次改成每 15 分鐘一次的對象; 第二種是擷取的網頁已包括全部訊息, 不必依賴指標移動來分批擷取者 (例如美元指數資料), 這種 cron job 只要半小時或一小時擷取一次即可, 我是將其分散在 0~59 分啟動執行, 例如下圖是指定在每小時的 5 分去執行一次 cron job :


而需分批執行的 cron jobs 則要依據資料多久必須全部更新一次來考量執行頻率. 若以目前擷取 976 個公司資料來計, 若要在一天內更新全部資料的話, 每小時要更新約 967/24=40 個, 每次抓 10 個的話, 每小時要抓 40/10=4 次, 亦即大概 60/4=15 分鐘抓一次即可. 在 Hostinger cPanel 的 advanced cron jobs 中有每 15 分鐘執行一次的選項 :


底下時, 日, 月, 周都用 Every ~ 即可, 如下所示 :


當然有些不需要每天更新一輪的 cron 可以設為每 30 分鐘執行一次 (通常一次 10 個), 這樣就是每兩天全部刷新一遍了, 同理可推, 每小時執行一次的話要 4 天才會抓齊全部資料; 每兩小時一次的話則要 8 天.

今天因為還剩休假一天必須休掉, 下午重新整理 cron jobs 設定, 大部分都改成 15 分鐘 run 一次, 少部分需要在 8 小時內分批抓齊資料者 (如 yahoo 的 credit, major, dealing) 仍維持每 5 分鐘跑一次. 在此將做法做個紀錄以便後續參考.

2016年12月27日 星期二

2016 年第 50 周記事

週六 12/24 參加了母校電機系 50 周年慶, 到場的同學還是我們這十個出頭的老面孔, 許多同學還是失聯中. 不過還是非常開心, 見到了三十年未謀面的老師, 大家能聚在一起吃個飯真不容易.

本周閒暇時間還是在忙 PHP 專案, 進度還算不錯, 爬蟲程式越寫越順手, 但是要在元旦前完成恐怕非常拚. 總之, 計畫只是理想, 人生中大部分的場景都是且戰且走.

週日因菁菁要求出去走走 (都是我大嘴巴沒做好計畫就隨口說說), 原本想去茂林, 後來覺得很久沒去三地門, 就臨時改去三地門, 順便測試一下剛買的 Garmin 4592R Plus 導航機. 實測結果非常滿意, 反應速度很快, 圖資也很精確.


茄萣濕地自行車之旅

昨天 12/26 補行憲紀念日放假, 是一例一休前最後一個勞工小確幸, 上回合歡山的老班底再次出擊, 這次是騎自行車到茄萣濕地, 中午到附近興達港吃海產再折回, 下面是老張寄給我的行程計劃 :

路程大約: 08:30文藻->蓮池潭->世運主場->援中港->台17->茄萣濕地(中午在興達港附近用餐再騎回高雄 ), 沿途會在7-11休息補充,原則每10km或50-60分鐘休息一次.

要準備的物品 :
  1. 腳踏車安全帽
  2. 頭巾
  3. 運動褲褲尾要束緊免得絞入鍊條
  4. 太陽眼鏡
  5. 飲用水 / 可攜帶香蕉等含鉀水果補充能量
  6. 可帶小背包(勿帶大背包)
  7. 手套
  8. 只攜帶健保卡及悠遊卡, 減少攜帶零錢
我這次不騎二哥的小黑 (登山越野車), 改騎小白 (小折), 因為小白從沒出過遠門, 我想操它一下, 未料此乃錯誤決策, 我反而被它操得吱吱叫. 因為小折輪子小, 變速器又少人家一檔, 人家踩一圈我要踩兩圈, 真是累死我了. 我一路都落後車隊起碼一百公尺, 都靠紅綠燈才跟上隊伍.

此行除了同學四人外, 還有老張的同事冠志兄, 他是自行車好手, 挑戰過武嶺, 老張請他幫我們領隊. 去程由於逆東北季風, 騎起來非常吃力, 冠志兄說由他在前面破風, 我們緊跟在後就會比較省力, 但這招對我沒用, 因為我 ... 全程落隊. 

早上約 9 點從文守路文壬家出發, 經蓮池潭轉台 17 線, 約 11:50 到達目的地茄萣濕地, 全程約 36 公里 :



這個濕地非常遼闊, 吸引許多水鳥聚集 : 




濕地周圍有賞鳥屋與賞鳥樓, 在賞鳥樓上吃過點心, 稍作休息後就前往興達港附近的大茄萣海產吃午餐, 這裡好吃的魚丸叫姑嫂丸, 據說是一對姑嫂合力經營的, 我看到招牌原先以為是在賣中藥哩. (這幾張都是老張拍的,  他們的相機比較好).

回程是順風較好騎, 但因為一吃完飯就啟程, 所以出發不久膝蓋內側肌肉就抽筋, 還好冠志兄教我以腳尖踩踏板, 兩腳稍內縮才不致一路抽筋. 回到高雄剛好下午四點半, 不僅屁股痛, 連腿都軟了. 

此行教訓 :
  1. 遠程不要騎小折.
  2. 一些裝備還是要買 : 束褲, 頭巾. 
至於車子, 的確, 好車當然有其價值, 但現階段還不用花大錢另買新車, 二哥那台越野車堪用即可. 

2016年12月24日 星期六

母校電機系 50 周年慶

今天回我的第一母校高應大參加電機系 50 周年慶, 見到了畢業 30 年來第一次相見的師長, 實在非常高興, 在中正堂遇見以前教我們電子學的洪麟老師 (也是三年級時的導師), 以及超級優秀當過母校校長的方俊雄學長, 跟阿超一起趨前報上級數向他們握手寒暄, 他們或許不記得我是誰了, 但這不重要, 我記得就好.

# 高應大電機工程系50周年慶 千餘系友回娘家

本來大家約好不用太早去, 因為典禮可能漫長而無聊. 但我覺得, 以前到中正堂參加集會確實無聊, 但現在還怕無聊嗎? 如果可能, 還真想回到過去全班被叫到中正堂集合的日子哩! 所以我還沒九點就到現場了, 但是舉目所見都是不認識的學長, 過了一會兒才看到阿超, 我們兩人聊了一會兒看見一位老先生走過來, 問說 : 我應該有教過你們吧? 我們馬上站起來說 : 老師好! 但是, 突然忘記老師名字了, 怎麼想就是想不起來, 只記得以前在自動控制與電磁學課堂上常聽他提到道家的龜息大法.

後來遇到我的同事蔡學長, 趕緊問他, 這才想起來是蔡繁仁老師. 中午在操場午宴時他來到我們這一桌, 我趕緊湊過去問他 : 老師, 您的龜息大法練到第幾層了? 他笑著說 , 哈哈哈, 說到這個就對你們不好意思了, 他大概是覺得以前在課堂上愛閒扯淡吧! 但我想說的是 : 老師, 您教的馬克士威爾方程式我們早已經忘記了, 但提到龜息大法, 沒有一個同學會忘記您啦! 人生中, 記得的才是無價.

Source : 中國時報

蔡老師來到我們這桌時, 剛好被中時記者拍下這張照片, 然而, 我卻被建興與明璜的兩個大頭擋住了! 殘念! (BTW : 以奴, 你的頭頂也太亮了吧!)

午宴席開 150 餘桌, 近尾聲時主持人請老師們到前台接受我們的致敬, 然後要求學生們別只是握手, 擁抱一下老師吧! 剛開始有些靦腆, 但我還是走向前擁抱了黃國恩老師, 黃文良老師, 以及洪麟老師, 但卻漏掉了李慶祥老師, 真可惜, 因為五十年慶就只有一次, 而我是離校三十年後才再次見到老師哩! 這種機會不會很多, 超珍貴.

以前保守的我不習慣擁抱, 但有了小孩後, 從小我就會跟小狐狸們 kiss good night, 但姐姐與二哥中年級後就不跟我抱抱了, 只有菁菁到現在還保持這個習慣. 阿超聽我說之後超羨慕, 我說就是一個習慣啦! 就跟寫日記一樣, 如果堅持每天都要寫, 那麼就不會間斷.

2016年12月20日 星期二

2016 年第 49 周記事 : 家裡來了一隻小貓

週五晚上菁菁去上英文課的時候, 學校的理化項老師送來一隻黑色小貓, 放在一個粉紅塑膠籠子裡, 我提上來的時候只有稍微打開瞧瞧, 只見她怯生生地待在角落裡, 咕嚕嚕的一雙大眼盯著陌生的我看. 菁菁本來說就放在警衛室, 她回來時再提上來, 因為她想要成為第一個看到她的人, 但是我說這樣管理員不就是第一個看到她的人嗎?

我提上來之後就放在客廳茶几上, 我看她到處張望後就躺在籠子裡睡覺了, 我也沒逗她, 就寫程式去了. 過一會兒就聽到她在喵而喵的, 兩爪不斷地在抓籠子, 我就放她出來, 她這裡看看那裏聞聞, 竟然鑽到客廳木椅底下躲起來了, 喵也喵不出來. 我想她可能還在觀察環境就不理她, 繼續寫我的程式. 接著我就例行去圖書館還書, 順道去畫室載姐姐, 回來時她還是躲在椅子底下, 不過菁菁說餵過後已經不會那麼怕生了. 晚上睡覺前還是把她關回項老師的籠子裡, 免得到處亂跑.

經過幾天適應後, 這隻小貓已經熟門熟路, 每天聽到早上我起來就從木椅下鑽出來喵啊喵的, 我就先把她抱進貓砂盆尿尿, 其實項老師自小貓出生後就有在訓練, 所以她現在已經會自己跑去盆裡解決衛生問題. 早上我在看新聞做甩手功時, 她還會蹲在電視前面盯著螢幕看, 真是可愛 :


菁菁說要叫她汪汪, 因為她本來想養狗不可得只好求其次養貓, 但是名字還是要叫原本要給狗狗的名字. 我則叫她 Monkey, 因為怎麼看, 我都覺得她那張貓臉長得真像猴子. 家裡從來沒養過貓犬類的寵物, 突然間客廳有一隻毛茸茸的東西在穿來穿去還真不習慣哩!

本周過完離新年剩下兩週了, 2016 就要莎喲娜拉囉. 這周我還是把全部閒暇時間拿來寫 PHP 專案, 所以沒有多少時間寫網誌, 我希望專案進度能在年底前結束, 我想盡速回到 Arduino 與 Raspberry Pi 的學習上.

兩周前種的玉米已長高不少, 故本周又買了 20 株來種, 但這次的苗較小, 所以先放個一周再種. 另外也買了十株茄子苗先行種下去, 這回要立柱牽繩好好照顧, 希望能採收兩年. 菁菁特愛吃絲瓜, 我應該多種些絲瓜才對, 所以預計下周再買三株來種.

2016年12月15日 星期四

EasyUI Datagrid 表格內容為超連結時的編輯問題

之前在測試 EasyUI 的 Datagrid 時發現, 若表格內容含有超連結, 這時使用 EasyUI Dialog 對話框編輯表格內容時會有一個問題, 就是呼叫 load 方法將所點選的列 (row) 內容載入表單中時, 含有超連結的欄位會載入超連結內容, 但實際上我們要編輯的只是超連結的 innerText 而已. 當時認為這很難處理, 所以就得到一個印象 : 表格內容盡量不要含有超連結, 除非它不需要編輯.

昨晚在替 PHP 專案新增一個 cron_list 資料表來管理 cron jobs 時, 又遇到這個問題. 因為我想在 cron_list 表格中可以直接按程式名稱的超連結就開啟新視窗去執行該 cron job, 但在編輯 cron 程式的說明欄位時, 由於程式名稱是超連結, 結果載入編輯欄位的就是那個超連結內容, 感覺很奇怪, 我要的是純粹的程式名稱而已.



經過來回測試各種方法後, 終於搞定這個長久以來未解決的麻煩. 其實方法很簡單, 就只是 Javascript 的字串處理而已. 處理方式如下 :

    $("#edit_cron").bind("click",function(){
      var row=$("#cron_list").datagrid("getSelected");
      if (row) {
        var str=row.program_name; //去除超連結用, 只取出程式名稱
        if (str.indexOf("<") != -1) { //避免第二次編輯時變空白, 只有第一次時才須擷取
          row.program_name=str.substring(str.indexOf(">")+1,str.lastIndexOf("<"));
          }
        $("#cron_list_dialog").dialog("open").dialog("setTitle","編輯 Cron 說明");
        $("#cron_list_form").form("load",row);
        $("#op").val("update_cron");
        $("#id").val(row.id); //edit 需要傳送 id
        }
      else {$.messager.alert("訊息","請選擇要編輯的 Cron 項目!","info");}
      });

與一般無超連結表格編輯方式不同之處就是加入了上面藍色部分的程式碼, 當第一次選取列進行編輯時, 所取得的 row.progrma_name 屬性值是超連結內容, 例如 :

<a href='http://www.myweb.com/cron/test.php' target='_blank'>test.php</a>

我要的只是其中 innerText 的 test.php 而已, 使用 Javascript 的 substring() 即可輕易取出超連結內的程式名稱, 先用 indexOf() 找出第一個大於符號索引, 加 1 就是 php 程式名稱的開始位置; 然後用 lastIndexOf() 從後面尋找第一個小於符號索引, substring() 會擷取此索引之前的字元. 哈哈哈, 總算搞定了, 這樣以後要處理含有超連結的表格就不會綁手綁腳了.

之前一直不願面對處理的難題竟然只要三行程式碼就解決了, 因為這次我既要在表格中使用超連結, 也要能編輯表格內容, 之前遇到阻礙就不想花時間去試, 這回被逼到了不得不面對. 可見, 障礙的高度通常不在問題的本身,  而是在我們心裡自己築起來的那道高牆.

參考 :

# Get selected row from DataGrid


2016年12月13日 星期二

2016 年第 48 周記事

之前菁菁一直想要養寵物, 從領養狗狗, 貓咪, 到市場的黃色小雞, 她都想要養, 當然都被我一一否決. 只要出去時看到有人遛狗, 她就會發狗瘋, 不斷地盧, 就是要養一隻狗. 我當然要技巧性地把話題扯開, 不然她的盧功真是讓人受不了.

前陣子學校的理化老師的母貓生了幾隻小貓, 問同學有沒有人要養, 她回來又再三央求要養貓, 水某鬆動了, 說給她養看看好了, 免得以後說我們都不讓她養. 我雖然覺得養寵物麻煩, 但也勉為其難答應了. 本來週六下午要送來, 但左等右等都沒來, 我看她捉摸不定, 一下子說這禮拜想在高雄, 一下子又說還是跟我回鄉下好了, 我說如果妳擔心周日老師送貓來, 那就留在高雄好了. 於是乎本周就我一個人回鄉下.

周六接到李麥克老師電話, 說二哥上課時在看數學, 被老師抓到唸了一頓, 跟家長通報一下. 我一直很煩惱二哥在英文上無法提起興趣, 上高中後雖然回李麥克上英文, 但是很少看到他拿英文小說來看. 回來後我也沒問他, 周一放學去載他, 他主動說想停掉李麥克的課, 因為實在無法應付. 我也覺得他說得對, 不能因為姊姊在那裏學得好, 就每一個人都得去那裏, 他覺得那種教學方式他不適合, 他適合傳統文法教學. 我看只好自己來教他了.

本周日傍晚程式告一段落, 想要活動一下筋骨, 又在菜園鋤了兩畦菜圃, 預定下周再種 20~40 株玉米. 其實把整理菜園當作一種運動也很不錯, 光一小塊地就讓我汗水淋漓 :


兩周前種下去的 22 株玉米只乾掉一株, 雖然幼苗有被壓折到, 但顯然生命力強大, 都活過來了. 但是離過年不到 50 天, 這次似乎太晚種了. 以後進十月就要開始種植. 另外兩周前種的地瓜葉也欣欣向榮, 再過兩周應可採摘矣 :


這一周來專心寫 PHP 爬蟲程式, 總算完成了預定進度的九成, 在闊別近一年後重回 PHP 專案雖然有點生疏, 但在之前認真寫下的筆記與備註輔助下, 短時間就可以迅速恢復戰力, 足見花點心力做備註跟寫筆記是有回報的. 不要太相信自己的記憶力, 也不要認為自己現在有多熟, 一段時間不摸保證忘光光, 之前多熟都沒有用.

鄉下頂樓的太陽能熱水器附裝的電熱控制器日前失靈了, 似乎是右側這顆電磁開關 (magnetic connector, 又稱電磁接觸器) 壞掉了, 因為按中間的手動按鈕也不會動作 :



爸說看能不能買到一樣的回來自己換, 回來高雄一查, 根本沒有這 Mitsuki FH-16H 型號! 畢竟是 25 年前的產品了! 我去小漢找, 發現電磁開關都非常貴, 至少要 500 元以上! 我看要先研究一下線路圖, 了解此電磁開關個接點作用後, 再去找個類似的來取代. 參考 :

# 電磁接觸器與按鈕開關該如何接線?
電磁接觸器(Magnetic Contactor)
動力群科電子報(教材教法) 交流電磁開關及其控制電路製作

2016年12月11日 星期日

PHP 日期字串格式轉換方式整理

這一周來演練 PHP 爬蟲程式, 發現不同的財經網站呈現日期的方式各不相同, 有民國紀元的, 例如 "105年12月9日"; 有西元紀元的, 例如 "2016/12/9", 而且個位數的月日有的有補 0, 有些沒有, 真是傷腦筋. 我寫程式喜歡格式統一, 這樣才能前後一致, 程式碼可以一刀未剪一再重複使用. 在日期方面採用 "2016-12-09" 十個字元格式; 日期時間則採 "2016-12-09 12:08:07" 格式, 其中日期與時間以空格隔開, 這樣要拆分處理時也方便.

我將這陣子在各財經網站看到的種種日期格式轉成 "YYYY-MM-DD" 格式的處理方法整理如後備查, 這樣以後要用到時就不用到每一個專案目錄下, 一個個檔案逐一開啟來尋找, 我最討厭那種 : "咦? 我記得在這個程式中有寫過這樣的功能啊? 怎麼找不到?", 這樣漫無目標地找, 還不如重新寫較快. 但再怎麼快, 都不會比用谷歌大神搜尋自己的筆記快.

1. 證交所三大法人買賣金額統計表 : 格式 "105年12月09日"

此網頁之日期含中文且使用民國紀元, 處理起來較費事, 不過個位數的月日都已補 0, 不算是最糟的, 處理方式如下 :

//擷取交易日期:格式 "105年12月09日"
$start="<span style='float:left;margin-left:171px;'>"; //頭標
$end="三大法人買賣金額統計表"; //尾標
$trade_date=return_between($file, $start, $end, EXCL);
echo $trade_date."<br>"; //"105年12月09日"
//改為 YYY-MM-DD 格式
$trade_date=str_replace("年","-",$trade_date); //年改為 "-"
$trade_date=str_replace("月","-",$trade_date); //月改為 "-"
$trade_date=preg_replace("/[^0-9-]/","",$trade_date); //除去其餘中文部分(日)
//改為西元年 YYYY-MM-DD 格式
$arr=explode("-",$trade_date);
$arr[0] += 1911; //民國年加 1911 為西元年
$trade_date=implode("-",$arr); //組合回去
echo $trade_date."<br>";

2. 鉅亨網新台幣兌美元匯率 : 格式 "20161209"

此網頁之日期為純數字, 年月日一氣呵成絕不含糊, 而且個位數的日月都已補 0, 所以處理方式就是抓出年月日後中間插 dash 即可 :

//擷取交易日期:格式 "20161209"
$start="昨收</th></tr><tr><td>"; //頭標
$end="</td>"; //尾標
$trade_date=return_between($file, $start, $end, EXCL);
echo $trade_date."<br>";
$Y=substr($trade_date,0,4);  //取出年
$M=substr($trade_date,4,2); //取出月
$D=substr($trade_date,6,2);  //取出日
$trade_date=$Y."-".$M."-".$D;  //組合成 YYYY-MM-DD 格式
echo $trade_date."<br>";

3. 期交所三大法人區分各期期貨契約 : 格式 "2016/12/9"

此網頁預設 GET 存取的話顯示最近一次交易日全部資料, 其日期標在表格右上角. 此頁日期格式月日個位數須補 0, 處理方式如下 :

//擷取交易日期:格式 "2016/12/9"
$start='<span class="right">日期'; //頭標
$end="</span>"; //尾標
$trade_date=return_between($file, $start, $end, EXCL);
echo "交易日期=".$trade_date."<br>"; //"2016/12/9"
//改為 YYYY-MM-DD
$arr=explode("/",$trade_date); //拆出年月日以調整月日格式
if ($arr[1] < 10) {$arr[1]="0".$arr[1];} //個位數前面補 0
if ($arr[2] < 10) {$arr[2]="0".$arr[2];} //個位數前面補 0
$trade_date=implode("-",$arr); //組成 YYYY-MM-DD
echo "交易日期=".$trade_date."<br>";

4. 期交所臺股期貨 (TX) 行情表 : 格式 "2016/12/09"

此網頁是期交所期貨每日交易行情查詢頁面, 預設顯示大臺行情. 其日期位置在表格左上方, 同樣是期交所網頁, 此頁日期格式硬是跟上面期交所三大法人持倉網頁的不一樣. 不過這個好處理, 因為個位數的日月都已經補 0, 只要將斜線改成 dash 即可 :

//擷取交易日期 : 格式 "2016/12/09"
$start="<h3 align='left'>日期:"; //頭標
$end="</h3>"; //尾標
$trade_date=return_between($file, $start, $end, EXCL);
echo "交易日期=".$trade_date."<br>"; //"2016/12/09"
//改為西元年 YYYY-MM-DD
$arr=explode("/",$trade_date);
$trade_date=implode("-",$arr); //組合回去
echo "交易日期=".$trade_date."<br>";

5. Yahoo 世界股市行情表 : 格式 "12/09/2016"

Yahoo 世界股市的日期格式是月/日/年, 要轉成 YYYY-MM-DD 有點麻煩, 不僅尋標要費點手腳, 日期還要拆開重組, 所幸個位數的月日已經補 0, 省了一道手續, 處理方式如下 :

//擷取交易日期:格式 "12/09/2016"
$file=preg_replace("/[\s]{2,}/", "", $file); //去除 2 個以上空格
$start="美加</font></td>"; //頭標
$end="&nbsp;"; //尾標
$trade_date=return_between($file, $start, $end, EXCL);
//去除前面不需要的元素
$trade_date=str_replace('<td height=20 align=right>', "", $trade_date);
$trade_date=str_replace('<font color="#330066">資料日期:', "", $trade_date);
$trade_date=trim($trade_date); //刪除左右可能之空格
echo "交易日期=".$trade_date."<br>"; //"12/09/2016"
//改為西元年 YYYY-MM-DD
$arr=explode("/",$trade_date); //[0]=月,[1]=日,[2]=年
$trade_date=$arr[2]."-".$arr[0]."-".$arr[1]; //組合成 YYYY-MM-DD
echo "交易日期=".$trade_date."<br>";

2016-12-12 補充 :

奇怪, 證交所三大法人買賣金額表明明昨天還是 BIG5 編碼的網頁, 今天就改版變成 UTF-8 編碼, 導致昨天改好的程式執行失敗. 即使改為 UTF-8, 還是無法擷取成功, 仔細看網頁原始碼, 原來網頁內容也有改變, 新版網頁處理方式要改成如下 :

//擷取交易日期:格式 "105年12月09日"
$start="<span style='float:left;margin-left:223px;'>";
$end="三大法人買賣金額統計表</span>";
$trade_date=return_between($file, $start, $end, EXCL);
echo $trade_date."<br>"; //"105年12月09日"
//改為 YYY-MM-DD
$trade_date=str_replace("年","-",$trade_date); //年改為 "-"
$trade_date=str_replace("月","-",$trade_date); //月改為 "-"
$trade_date=preg_replace("/[^0-9-]/","",$trade_date); //除去其餘中文部分(日)
//改為西元年 YYYY-MM-DD
$arr=explode("-",$trade_date);
$arr[0] += 1911; //民國年加 1911 為西元年
$trade_date=implode("-",$arr); //組合回去
echo $trade_date."<br>";


2016年12月7日 星期三

買 Garmin 4592R Plus 導航機

十月的時候花了很多時間在評估導航機, 當時結論是買 Garmin 4592R 這款, 但是後來一忙又不了了之. 上個月跟老張, 峰仔, 與文壬去攀登合歡山, 老張車上裝的就是 Garmin 導航機 (很舊了但還是照樣可下載圖資), 老同學們的意見也是買 Garmin, 但回來還是沒動作. 今天同事提醒我 10000 原的福利金 12/20 就要到期了, 所以就趕緊上網訂購了 :

訂單編號 :
219229
訂單狀態 :
新訂單
訂購時間 :
2016/12/07
訂單名稱 :
【GARMINnuvi 4592R Plus】GARMIN nuvi 4592R Plus Wi-Fi多媒體衛星導航(贈4好禮)
訂購商品明細 :


商品名稱項目單價數量
【GARMINnuvi 4592R Plus】GARMIN nuvi 4592R Plus Wi-Fi多媒體衛星導航(贈4好禮)10,990 1
商品金額 :
10,990
運費 :
0
訂單金額 :
10990
使用點數 :
10000
訂單應付金額: 
990

這比當初開始評估時的價格 11900 便宜了近 1000 元, 另外要將我原本的倒車攝影鏡頭連進來的話, 需要購買 Garmin 原廠的轉接線, 廠商原本說一條 420 元, 下午我打電話給客服問說要如何加購, 廠商回覆因之前客戶訂的貨有多出來, 所以免費送給我隨或寄送, 這樣又省了 420 元, 因此我這回只要花 990 + 800 (安裝費) 約 1800 元即可.

參考 :

# 汽車衛星導航評估

2016-12-10 補充 :

我 7 號訂, 8 號就通知要送貨, 比網頁上預訂的 9 號提早了一天. 由於在忙程式, 所以到貨後我只拆外箱, 確認贈品都在就擱在一邊. 昨天問鳳山阿勇的店, 說如果電源線與倒車攝影要走線必須確認線夠長, 隨即問供貨商, 說一條原廠電源延長線要 960, 好貴! 今早拆箱後比對發現原廠附的線確實只夠到點菸器而已. 不然先開去建億問看看, 結果有兩台車在裝, 只好預約下周二. 

2016-12-13 補充 :

今天請兩小時補休將車子開去民族路的建億, 將倒車顯影接到導航機上, 可惜電源線不夠長, 沒辦法將電源隱藏起來, 只好暫時垂一條線下來. 花了 300 元. 有找到一條 3.5 米的電源線, 如果可用的話再開去埋線. 這就是沒先問好得跑兩次, 花兩次錢 :

# 破盤王/台南 GARMIN 導航 行車記錄器 2A 車充線 電源線【3.5米】NUVI 4659R 4592R 57 $350

關於吸盤固定問題, 有人使用螢幕保護貼加 3M VHB 雙面膠, 參考 :

http://www.mobile01.com/topicdetail.php?f=621&t=3246461&p=2
# 3M VHB雙面膠帶-片狀(車外用) $139
# 壁掛式行車記錄器透明膠受不住高熱?
# 破盤王/台南 3M 助黏劑↘79元【透明10ml裝】超強助黏效果~晴雨窗/測速器/鯊魚天線/行車記錄器/後視鏡/氣氛燈 $79
# 行車記錄器後視鏡支架 $150
# 行車紀錄器支架 $120 
# 破盤王/台南 GARMIN 導航+行車記錄器 2合1【後視鏡支架】nuvi 3595 2585【錄得清】AA01 $399
# 3M 黏、吸盤吸的、後視境扣環,哪種行車記錄器安裝方法比較好呢?

"把黏貼式支架上的舊膠給撕下→塗上助黏劑後等個1分鐘→重新貼上3M專用超強雙面膠帶(灰色那種,厚度1.2mm)→前擋欲黏貼處用衛生紙擦一擦,塗上助黏劑後等個1分鐘→黏貼式支架與前擋結合→6個小時內不要扣上主機.有了助黏劑的加持就算要刻意把它拔下來也相當吃力"

薪資所得每月扣繳與定期定額投資 0056 比較

這幾天收到公司會計部門 Email, 要求就明年度所得稅額扣繳方式進行變更, 如果照舊由公司自每月薪資中預扣所得稅額, 那就不用採取任何動作. 我看了一下薪資單, 每個月都被預扣了 4200 元左右的稅額, 一年下來大概被預扣了超過 5 萬塊的稅, 也就是說我們每個月都已經在繳稅, 每年五月報稅只是做個總清理, 看是要補稅還是退稅.

這些錢如果自己存起來的話, 可以孳生額外的利息, 何必無息存在國稅局呢? 各銀行都有定期儲蓄存款, 可以臨櫃辦理一個在每年五月到期的一年定儲帳戶 (零存整付), 授權銀行每月自薪資帳戶中扣固定金額存在此帳戶, 次年五月要繳稅時剛好到期領出繳稅. 以台銀定儲一年利率 1.09 為例, 利用華南銀行網站提供的零存整付利息試算功能計算, 若每月扣 5000 元, 則到期本利和為 60355 元, 亦即可獲得 355 元利息 :

# 華南銀行零存整付儲蓄存款利息試算



雖然 355 元利息少得可憐, 但至少還可以吃個刷刷鍋吧! 對於欠缺理財技能的人小資族來說, 這是只要做個小改變就能擁有的小確幸. 當然, 對於理財高手來說, 每個月 5000 元若妥善投資, 應該可以帶來數倍於 355 元的利潤.

以投資台灣高股息 (0056) 為例, 它是 ETF (一籃子的高股息股票), 不必擔心踩到地雷股, 其股價通常在 20~25 元間變動, 股息殖利率平均來說大約 5%, 若這 5000 元每月購買 0056 零股, 一年投入 60000 元約可以買到 2400~3000 股, 以今年每股配 1 元來算, 應可獲利 2400 元左右, 這可是定儲利息 355 元近 7 倍的獲利啊!

由於是定期定額扣 5000 元買 0056, 當股價跌時買進股數會比較多, 漲時則買進股數較少, 因此實際持有成本大約是一年的平均值. 這樣投資到每年五月時就賣掉 0056 來繳稅, 如果股價高於平均持有成本還會有額外資本利得, 賣出後每個月 5000 塊還是繼續扣錢買進 0056. 定期定額投資零股可參考元富證券零股理財專區 :

http://www.masterlink.com.tw/money/DreamPlan

現在促銷方案零股定期定額每筆手續費 1 元!


參考 :

定存和定儲的存款利率如何計算?


2016年12月5日 星期一

埋貓記

今天下班騎機車前往悟饕買便當途中, 在河南路差點輾過一隻躺在路上的動物, 閃開後才弄清楚是一隻白色墨綠色花紋的大貓, 馬上掉轉回去查看, 發現這貓咪已經不幸身亡了. 念在牠屍身躺在路上可能會被車子輾爛, 我從機車置物箱找出一個塑膠袋, 抓住牠四肢將其移到路旁公園, 想說挖個洞埋起來, 但是沒有工具也沒辦法.

我抓住牠腳的時候, 發現仍有餘溫, 想必是在我到達之前剛被車子撞死的, 真是可憐. 剛好附近有垃圾車, 一位出來倒垃圾的老伯說那就丟垃圾車吧! 我說我趕時間, 我只能將牠放在公園草地上避免被車子輾爛而已, 不知道那位阿伯是否有將其丟到垃圾車. 在鄉下的話我就會在路旁農地挖個洞埋掉, 但在都市就不容易了, 只是沒有埋成那隻貓讓我感到有點為德不卒.

擷取期交所小型台指期 (MTX) 每日行情的方法

這幾天演練 PHP 爬蟲程式的寫法, 對象是期交所小型台指期 (MTX) 的每日行情表, 其網址為 :

http://www.taifex.com.tw/chinese/3/3_1_1.asp

不過連線這個網址預設是查詢大台 (MX) 的行情表, 小型台指期 (MTX) 必須在契約欄位選擇 "小型台指 (MTX)" 後, 按 "送出查詢" 後才會顯示小台資料 :



顯然這是透過表單以 POST 方法查詢, 其參數可在 Firefox 或 Chrome 按 F12 查得 :


雖然一共傳出 15 個參數, 但實際上只用到框起來的五個參數而已. 所以如果要抓今天的行情表資料, 可以利用 cURL 函式庫來擷取, 程式如下 :

/* 載入函式庫 */
include_once("../lib/http.php");    //匯入http模組     (必須)
/* 擷取目標網頁 */
$target="http://www.taifex.com.tw/chinese/3/3_1_1.asp";
echo "<a href='$target' target='_blank'>原始網頁</a><br>";
$ref=$target;
//取得交易日期 (今日)
$today=date("Y-m-d"); //2015-12-05
list($Y,$M,$D)=explode("-", $today); //分出年月日
echo "交易日期=".$today."(".$Y.$M.$D.")<br>";
//設定查詢表單 POST 方法參數
$para["qtype"]="2";
$para["commodity_id"]="MTX"; //小型台指
$para["syear"]=$Y;  //查詢年
$para["smonth"]=$M; //查詢月
$para["sday"]=$D;   //查詢日
$web_page=http_post_form($target,$ref,$para); //下載網頁檔
$file=$web_page['FILE'];  //取得網頁檔案內容
echo $file."<br>";

這是第一次嘗試用 POST 傳遞參數來擷取網頁, 以前我都一直使用 GET 方法, 沒想到 POST 其實也很容易, 只是要先查出必須傳遞那些必要參數而已.


2016年12月4日 星期日

2016 年第 47 周記事

本周非常忙碌, 公司一套舊系統要汰換, 相關配套措施有一些沒考慮清楚, 結果手忙腳亂. 從這次得到教訓, 撰寫 SOP 的工作必須抽空加速進行, 現在腦力不比從前, 過一個月就會忘得差不多了, 沒有 SOP 助憶是不行的.

二哥第二次段考往前推進到第七名, 僅英文 59 分不及格. 他對英文沒熱情, 文法根基不佳, 我看要寫個文法句型練習單來補強. 菁菁理化終於及格了, 但數學卻奇慘, 竟然考個 17 分, 她實在不是讀書的料, 還是儘早學一技之長為妙.

本周菁菁終於有空跟我回鄉下了, 而且還說要跟我去買菜. 她問說會不會去全聯? 會的話順便去隔壁的全家福看鞋子. 哇咧, 這才是她要跟我去買菜的目的吧! 以前是去市場旁邊那家米琪看衣服, 現在米琪遷走了, 換成看鞋子. 不過我自己也買了一個 Puma 的小背包, 原價 680, 特價 298, 我是想放在公司, 有時中午外出洽私用來背 iPad 或一些文件用.

早上去市場順路經過種子行, 買了 20 株玉米苗與一株絲瓜苗, 但到市場買了豆腐後, 粗心的菁菁把豆腐壓在上面, 導致絲瓜苗與約三, 四株玉米苗被折到. 傍晚還是將它們全數種在菜園, 能不能翻生就看造化了.

下午沒睡午覺, 一邊看守煮湯與燜蘿蔔的爐子, 一邊改 PHP 程式. 兩個小時無人打擾情況下專心地改好 fetch_taifex_options_pcr.php 與 fetch_taifex_futures_top10.php, 測試 OK.


2016年12月2日 星期五

還書一批

因為最近在忙 PHP 網路爬蟲程式, 手邊跟市圖借的幾本書都沒時間看, 乾脆一古腦兒全都還了, 以免分散了我的注意力, 在此做個紀錄, 爾後有空再借回來看 :
  1. jQuery Mobile 智慧型手機程式開發 (草衙)
  2. 超詳細! 基礎木工圖解書 (河堤)
  3. 自己動手打造超人氣木作 (河堤)
  4. 木工迷超手感入門木作 (河堤)
  5. 精確預測 :如何從巨量雜訊中,看出重要的信號? (美濃)
  6. 10 天做好 App-Corona SDK 超直覺遊戲開發攻略 (總館)
  7. 遊戲自己做-Corona SDK 跨平台 App 開發設計實戰 (河堤)
  8. 神奇洋蔥冰塊の自癒料理奇蹟
另外, 跟母校高師大借的 "人工智慧的未來:揭露人類思維的奧祕", 因為有人急用, 託圖書館寄了封 Email 來問我是否可先讓他看, 我想可能是有學弟妹寫論文要參考, 反正我也是沒時間, 只能斷斷續續看, 乾脆提前還書好了.

自從上週租了 Hostinger 虛擬主機後, 我的焦點又移回 PHP 專案了, 估計短時間內沒辦法繼續學習物聯網, 所以 Arduino 的實驗暫時要 Hold 一下, 時間所限, 網誌也會寫得少. 我現在的優先順序是 :
  1. PHP 專案 (新版工作日誌, 網路爬蟲)
  2. Python 學習 (GAE, 網路爬蟲, 機器學習)
  3. 樹莓派 (機器人, 智慧家庭)
  4. Arduino + ESP8266  (機器人, 智慧家庭)
  5. R 語言 (機器學習)
  6. 綠能
興趣廣泛實在不是件好事, 因為不容易聚焦.