2018年6月30日 星期六

好站 : 使用 Arduino IDE 開發 ESP32+LoRa 應用

今天在 Randan Nerd Tutorial 網站找到下面這篇文章 :

ESP32 with LoRa using Arduino IDE – Getting Started

作者使用 ESP32 當主控, 透過 SPI 介面與 LoRa 模組連接, 利用 Arduino IDE 編譯程式後上傳韌體, 這樣兩個 ESP32 就可以透過 LoRa 通訊了. LoRa 從去年做過初步測試就沒時間玩, 那時是用 Arduino 當主控, 有空要 ESP8266 來試試.

2018-07-01 補充 :

感謝好友 Jason 分享了下面這篇, 先記下來有空再試 :

http://wei1234c.blogspot.com/2017/08/sx127x-lora-transceiver-driver-for.html?m=1
wei1234c.blogspot.com - SX127x (LoRa transceiver) driver for (Micro)Python on ESP8266/ESP32/Raspberry_Pi

好書 : 網頁應用程式設計-使用 Node 和 Express

早上載二哥去河堤分館後就直接去母校高科大還書, 花了兩個小時把 2, 3 樓藏書尋了一遍, 覺得還是有蠻多書想借, 可惜十本額度已借滿借好, 爆表了. 不過下面這本非借不可 :

網頁應用程式設計-使用 Node 和 Express (歐萊里出版, 賴屹民譯)


Source : 博客來


若要在樹莓派上建物聯網網站, 我認為用 Node.js 最適合, 無阻塞式 I/O 比較不會讓頻繁的物聯網存取塞車, 用在 Fintech 金融資訊運算儲存也很適合. 為了借這本書, 我把目前沒時間看的 "資料科學的商業運用" 這本還回去了, 沒辦法, 凡是總是要取捨, 而且人心很奇怪, 當你擁有的時候不覺得珍貴, 沒有的時候就想要得不得了.

Win 10 安裝 Movie Maker

因為水某要用 Movie Maker 製作影片, 發現 Win 10 並未內建 Movie Maker, 而且微軟官網聲明已不再支援 Movie Maker 下載 :

Windows Movie Maker 已不再提供下載

而且提醒 :  "提供免費下載 Movie Maker 的網站並非提供真正的程式,這些下載項目可能包含惡意軟體、病毒或隱藏費用。"

我找到下面這個 Win 10 版的大約 79MB, 經掃描沒有病毒之類的 :

Windows Movie Maker 多語言版本安裝包下載

安裝後確實可用.

2019-11-29 補充 :

有網友表示此 Movie Maker 並非微軟所開發, 或者輸出時要求付費等問題, 與我當時安裝使用經驗不同, 建議若與預期有異勿安裝, 我找時間翻找之前微軟版光碟看看. 

2018年6月29日 星期五

關於麗星郵輪之旅

前天收到公司福委會轉發的麗星郵輪宮古島那霸旅遊優惠方案, 五天四夜高級海景房約 15800 元, 且免港務費與小費, 似乎蠻優惠的. 中午問爸有無興趣, 他說 OK 昨天我即傳真報名. 水某也想去, 但菁菁卻意興闌珊, 姊姊則無回應. 也問了舅舅與舅媽, 她說她會暈船.

事實上此行程大部分都在郵輪上, 僅在宮古島與那霸個停留約 8~11 小時, 大都認為沒多少時間攬勝. 確實如此, 下船時間不是下午 1 點就是 3 點 (宮古島), 老實說只能選一條路導覽路線而已. 宮古島琉球位置如下 :




航線為高雄港 - 巴士海峽 - 台灣東岸 - 宮古島 - 那霸.

2018-07-02 收到鋼友旅行社資料, 其中注意事項如下 :

  1. 請攜帶個人慣用之藥品及自備盥洗用具
    拖鞋請自備。 (牙膏、牙刷、毛巾等…船上也有提供) 
  2. 金錢:不可攜帶超過美金一萬元等值外幣(旅行支票或銀行匯票不限)
    新台幣不可超過六萬元
  3. 請攜帶個人有效證件以及COPY本(護照正本有效期限六個月以上)辦理登船手續
    ★★★請注意役男的身份,依照境管局規定88年次以前未服兵役者,需要蓋兵役章
  4. 孕婦必須出示醫師證明適合在船上旅遊
    ★★★懷孕24星期以上之孕婦不能登船航遊★★★。
  5. 未滿6個月嬰兒(24星期以下)及年滿90歲以上的旅客,不接受訂位。 
  6. 如遇不可抗拒之因素(如颱風等),依照麗星郵輪規定;麗星郵輪保有取消或更改既定 航程、時間、船舶及價格之權利,恕不預先通知,請參照麗星郵輪旅遊要項需知 (旅客需簽回)。 
  7. 請於〝報到時間~關閘前半小時〞須辦理登船報到,如有延誤請自行負責。 
  8. 自行攜帶護照之旅客,請另行備妥A4護照影本1份 (一定要 A4, 不可裁切),以供日本海關查驗。請留意,在整個旅程中,我們的登輪部門會為您保管您的護照,在航程結束後才歸還給您。
  9. 因岸上觀光遊覽車數量有限,如欲報名岸上行程之旅客請出發前一星期預約。 
  10. 麗星郵輪規定,為維護航程安全問題,嚴禁攜帶Samsung Galaxy Note7手機、對講機上船。 11.麗星郵輪規定,任何酒精飲品(啤酒/葡萄酒/烈酒)將不允許攜帶上郵輪。

好奇的是為何指名 Note 7 手機?

關於報到與登輪還需注意下列事項 :

報到
  1. 請貴賓依您購買的艙房於報到大廳辦理報到手續。
  2. 建議貴賓於接駁巴士下車處先辦理托運行李手續。(行李會由郵輪人員送至您的房間)
  3. 報到時請出示您的護照,工作人員會將您的護照、船卡(房卡)、日本出境卡、日本海關單、岸上觀光資料(若有購買)、船上晚宴預約單(若有)一起交給您,再排隊前往通關登船。
登上郵輪
  1. 請事先準備護照(有照片頁)影印本,一定要A4尺寸(不可裁切)1:1比例(不可放大/縮小),每次上下船連同船卡均要隨身攜帶。
  2. 登上郵輪之前,乘客必須經過保安審查及出示登船卡。
  3. 帶小朋友同遊的乘客請代為保管他們的登船卡,避免遺失。
  4. 登上郵輪後,請遞上您的登船卡,以便船員為您〝開卡〞和〝啓用〞您的客房資料,以及記錄您已經登上郵輪。
  5. 若您沒有〝開卡〞的情況下直接通過,您將無法進入您的客房。
  6. 切記每當上下郵輪時必須〝過卡〞。
兒童信用限制 :

如果您希望凍結小朋友登船卡之購買記帳功能,請前往7樓接待處(服務台)辦理。不然,小朋友如果在您不知情的情況下徑自使用登船卡購買物品或服務,您必須得為小朋友所簽記的款項負起責任。

遺失登船卡 :

若您不慎遺失登船卡,請立即向7樓接待處(服務台)報失以補發新卡及取消遺失的登船卡。以避免為帳額不符而負責。
請在您的登船卡背面簽上大名。 套房級及一般客房之乘客將個別獲得黃色、紅色及藍色的登船卡。

登船卡的功能:
  1. 登輪通行卡。
  2. 開啟您客房的鑰匙 
  3. 船上的身份証明。
  4. 餐廳用餐通行証。 
  5. 消費記帳卡。
行程如下 :

7/8 11:30 開始報到, 須於關閘 (13:30) 前半小時, 即 13:00 前報到. 登輪地點在高雄市蓬萊路棧 9-2 號碼頭, 港區管制私人車輛無法進入, 須搭接駁車, 搭車地點在七賢路底與蓬萊路口之香蕉碼頭.






行程說明手冊重要資訊整理如下 :
  1. 進入公海後手機訊號為經過衛星電話線路 (以分計費), 非必要最好關閉行動通話功能.
  2. 船上特定地點有付費 WiFi, 按日 (280 元) 或套餐收費
  3. 地毯顏色判別位置在船頭 (紅色), 船中 (藍色), 船尾 (綠色)
  4. 房間都會有洗髮精, 沐浴乳, 牙膏, 牙刷, 浴巾及浴帽, 但拖鞋自備
  5. 共有四間免費餐廳 (9F 王朝, 11F 香味軒, 9F 奧珊娜燒烤, 12F 航海家). 每一用餐時段只能選擇其中一家餐廳. 宵夜僅王朝與航海家提供. 
  6. 岸上觀光注意 : 船上熟食不可帶下船, 岸上生魚片不可帶上船, 水果食品不可入境
  7. 離岸後手機無法使用, 7F 接待處有衛星電話可用, 打台灣每分鐘 62.5 元
  8. 回港前一天晚上先結清款項, 以免次日擁擠
關於旅遊平安險問題, 一般搭機刷卡支付團費 8 成以上送旅遊平安險與不便險等優惠並不包括搭乘郵輪旅遊, 參考 :

信用卡旅綜險 不承保郵輪
別省這個錢! 刷卡贈旅平險 搭郵輪不在保障範圍

不過有少數銀行信用卡例如台企銀, 兆豐金, 凱基銀, 中國信託信用卡旅遊平安險有包含郵輪, 其中中國信託只限麗星郵輪, 參考 :

信用卡旅平險總整理 航空高鐵台鐵全都保

我有一張中國信託國旅卡金卡, 問了中國信託客服, 說詳細要問承保的台壽保公司 (0800-882868), 我問的結果是只含旅遊平安險, 不包含不便險與醫療險, 需另外投保.

參考 :

http://kimbella.pixnet.net/blog/post/180836004-搭麗星郵輪心得-%26-注意事項
elsy310.pixnet.net/blog/post/112191382-%28麗星郵輪.寶瓶星號%29-船上設施
寶瓶星號-乘客常見問題 (FAQ)
強烈建議不要搭麗星郵輪!!  (這是反話)
# 上順旅行社寶瓶星客房種類
2016年麗星郵輪行前準備及工具包

2018-07-02 補充 :

下午已傳真合約書, 協議書, 刷卡單, 以及護照影本給旅行社, 費用 34800 元, 含宮古岸上觀光 (1800*2) 與那霸岸上觀光 (1600*2) 共 6800 元.

行程被改 下船延誤 數百位台灣旅客海上抗議
出國換匯怎樣不吃虧? 臨櫃最不划算 這方式匯率較優
# 明台產物保險網路投保




2018年6月26日 星期二

購買有 HDMI 輸入之液晶螢幕

因鄉下樹莓派使用的 XP 時代 ViewSonic 螢幕已經很兩光, 有時會突然斷訊, 所以上露天找到下面這款有 HDMI 輸入介面的 15.6 吋螢幕取代, 這家賣的螢幕是最便宜的, 適合做監視器, 但品牌是沒聽過的 BroadWatch, 品質不知如何, 權且使用看看, 總之是因為樹莓派輸出為 HDMI, 經過上回買的轉換頭轉成 VGA 總覺得不穩定, 如果螢幕有 HDMI 介面就不需要轉換了 :

寬屏15.6寸液晶屏高清VGA / HDMI/超薄壁掛顯示器 PS3/PS4/XBOX/樹莓派/戶外監控便攜式1080P $1680

含運 1680+60=1740 元

我打算把此螢幕放在客廳爸看書的小書桌, 把書房的 Pi 3 移到小書桌接到新螢幕上, 這樣如果要跟爸說明資料時我就可以透過 VNC 遠端連線進入 Pi 3 操作給他看.

2018-07-08 補充 :

此螢幕就只有電源, HDMI, VGA, 與 AUDIO 輸入孔, 另一側則是簡單的按壓式視訊調整按鈕與電源開關, 因為在背板不好關電源, 直接拔插頭比較方便. 

2018年6月25日 星期一

評估新手機

我的華為 Honor 3C 使用到現在已近 4 年, 最近覺得越來越卡, 所以動了換機的念頭. 之前二哥力推 HTC U11, 說 CPU 強壯有力,  CP 值很高, 我想他是從手遊角度看得吧, 高通 S835 評價真的不錯, GPU 也很棒,  不過我一直對 Note 8 念念不忘, 那隻 S Pen 可以在螢幕上手繪蠻吸引我的, 只是空機價格太貴. 最近 Zenfone 5 (高通 S636) 與 Zenfone 5Z (高通 S845) 推出後又覺得還不錯, 所以稍微對這三款進行了研究比較, 其實我對手機的要求只有以下幾點 :
  1. 雖然不玩遊戲, 但運算能力越強越好
  2. 通訊網速要盡可能快 (3CA 以上或 MIMO)
  3. 拍照效果要好 
我不是追流行頻頻換手機的人 (畢竟手機也不便宜), 但要能用起來順才行. 像現在常常要清記憶體才不會卡卡, 這就有點困擾. 但若要像同事那樣咬牙硬撐到 5G 出來, 我看還要等 3~4 年才會成熟順暢. 所以現在換手機只是為了過渡到 5G 時代的性能要求而已.

由於 Note 8 空機超過 2 萬元實在貴森森不予考慮, 只剩下 HTC U11 與 Zenfone 5/5Z 需要選擇而已. 其實 Zenfone 5Z 於六月開賣後我比較傾向於買 5Z, 但 U11 現在有降價, momo 賣 12935 送保護套 + 螢幕貼 + 行動電源, 但露天空機有更便宜的, 賣 11799, 差 1100 元, 參考 :

HTC U11 (4G/64G) (空機) 全新未拆封 原廠公司貨 $11799
HTC U11 4G/64G 5.5吋 贈TPU透明殼+5200行動電源+螢幕貼 智慧型手機 0利率 免運費 (momo) $12935 元
全新 HTC U11 U3U 128G 4G LTE 5.5吋 旗艦機 $12800
全新 HTC U11 6G/128G 雙卡防水防塵 Edge Sense握壓側框感應 3D水漾玻璃 U-3u【海棠數位】 $13190
HTC U11 (6G/128G) (空機) 全新未拆封 原廠公司貨 X10 M10 X9 A9 S9 ULTRA $13399

U11 雖然已經是去年的舊產品, 但考慮到 CP 值到還可以考慮, 以下是 U11 的優缺點 :

U11 缺點 :
  1. 沒有 3.5mm 耳機孔, 附轉換頭
  2. 曲面螢幕較易摔壞
  3. 單鏡頭
  4. 據說毛病多 (?)
U11 優點 :
  1. CPU/GPU 強 (高通 S835)
  2. 拍照效果好
  3. 有 4CA 功能
  4. ROM 使用最快的 UFS2.1  
  5. 有側框感應功能, 拍照錄影等方便
  6. 有智慧數位助理
  7. 立體聲雙喇叭音量大效果好
  8. 兩年保固



U11 有一個大優點是其 ROM 用的是最快速的 UFS2.1, 而 Zenfone 5 用的是較慢的 eMMC, 不過 到了 5Z 就改用 UFS2.1 了, 效能優先考量, 其實應該考鎖定 5Z 就好, 參考 :

選Zenfone5 比較好還是U11比較好?
Zenfone 5z 跟 5 大家比較愛哪個?
請問:zenfone5和zenfone5z的差別只在處理器嗎?

比較之後發現 Zenfone 5 規格當然跟 U11 沒得比, 但跟 Zenfone 5Z 比起來, U11 就有點弱了 (CPU 低一檔). 不過測框感應功能對照相確實非常方便, 可及時捕捉難得的畫面. Zenfone 5Z 規格確實是非常的優, 從 CPU, RAM, ROM, 到光學防手震等都有, 可說是高階旗艦機. 所以結論就是 : 買 Zenfone 5Z 6G/128G 款, 把規格一次買足.

我查了公司福利社有賣 Zenfone 5, 售價 11390 元, 但還沒有賣 Zenfone 5Z. 我今年福利點數還沒用, 那就拿來買 5Z 吧! 福利社廠商說原廠還沒報價, 要等一等. 反正我也不急, 只要年底前把福利點數用掉即可.

參考 :

ASUS ZenFone 5揭曉 軟體、相機有AI加持、全螢幕也長瀏海了
華碩ZenFone 5、5Z與5 Lite發表 3月中起陸續上市[MWC 2018]
「手機選購」破解迷思 手機挑選.購買法則 (2018版)
U11擠壓拍照是有沒有防手震?
[討論] U11的4CA
CA還不夠!什麼是4X4 MIMO?
# NCC 頻率資料庫查詢
高通S845跑分成績逆天,但單核成績依舊被蘋果A11輾壓 (還是蘋果厲害)
Android 旗艦機最強大的處理器?高通 S845 帶來全新三大進化
Alexa 智慧助理正式登上 HTC U11,但目前僅有美國玩得到

PS :

晚上在 mobile1 論壇看到有人介紹也是 6 月開賣的 Sharp S3 高配版 (同樣 6G/128G), 有無線充電功能, 但 CPU 比 Zenfone 5Z 的 S845 差兩檔次 (排老三), 用的是高通 S660, 現在 momo 與 PC Home 月底前都有促銷活動, 參考 :

全新未拆封 SHARP AQUOS S3 高配版 S660 6G/128G 無線充電版 iMos滿版玻璃貼 包膜優惠卷 $13990
【SHARP 夏普】AQUOS S3 6G+128G 高配版智慧機 momo $13990
首購送無線充電板+imos滿版玻璃保貼及包膜折價卷 限時刷星展送11% $13990

原本也很動心, CPU 差一點沒關係, 反正我不玩遊戲. 但載二哥回來的路上, 他說 CPU 對效能很重要, 應該買最高檔的 Zenfone 5Z, 無線充電目前效能比不上線充, 不一定需要. 而且他聽同學說 Sharp 手機之前有電池充電問題, 又讓我縮手了. 雖然有賣家以 13000 元優惠價格 (含促銷贈品) 要轉售未拆封 Sharp S3 高配版新機, 高 CP 值讓我非常心動, 但後來看到下面這篇, 就完全打消念頭了 :

自AQUOS S3上市購入至今使用感想! 小問題好多!!

總之, 現在先不要動, 等福利社 5Z 上架後再買. 根據 ASUS 官網 Zenfone 5 賣 11990 元估計, 福利社 Zenfone 5Z 報價應該在 16390 左右 (差價約 5000 元), 我大概還要補貼 8390 元.

2018年6月24日 星期日

2018 年第 25 周記事

下了一周的雨, 週三開始放晴, 但這幾天天氣仍不穩定. 下雨天真的很不方便, 因為我隨身總是要帶幾本書解悶, 寫程式時要查閱書卻不在身邊會非常苦惱.

鄉下的芒果開始收成了, 今年因花期逢雨, 海頓結果率太差, 上個月芒果套袋時感覺大約 100 顆左右, 還好只是屋子周圍種來遮陰只有 7, 8 棵, 全部自用送親友. 如果是種來出售, 這種一年一獲花期又怕雨的水果要怎麼過活啊! 農人真是靠天吃飯的命.

這兩天在 MOD 瘋狂看了 4 部片 : 兩部日片 (冰果, 跟爺爺說再見)  , 兩部韓片 (興夫-撼動朝鮮的文學家, 屍蹤七號房), 除了 "屍蹤七號房" 我覺得還好外 (黑色喜劇), 其他都帶給我不一樣的感受.

"冰果" 中的折木奉太郎那個節能至上主義令我印象深刻 : "非必要的事絕對不做, 必要的事盡速解決", 這哲學還真合我的口味, 只是心軟的我總是禁不起拜託與慈悲心撩落去, 盡幹一些浪費時間的事. 冰果原來是 I scream 的諧音, 意思是吶喊, 想要傳達的是懦弱隱忍而不敢大聲為自己辯護, 最後可能喪失理想而形同行屍走肉一般. 另外千反田同學的 "我很好奇" 哲學帶來的啟示是 : 當我們對世間不再好奇, 其實也就是行屍走肉了. "跟爺爺說再見" 我覺得風格有點類似台灣電影 "父後七日", 但描寫更多家庭手足親情的衝突, 不過都對中日兩國的喪禮做了大致的描述.

最近要集中火力學習 Python 應用, 我覺得自己太花心, 這裡也想那裏也想終究備多力分. 巴菲特認為找到好股就要重押, 把雞蛋放在不同的籃子裡固然分散了風險, 但也分散了利潤. 重點在找到不會破的金雞蛋後, 要把它們全部放在同一個籃子裡才能放大獲利. 學習也是如此.

樹莓派安裝 Python 編輯器 Thonny

今天在 Pi 3 上也安裝了 Thonny 編輯器, 安裝指令為 :

sudo apt-get install python3-thonny

參考 :

THONNY ON A RASPBERRY PI: USING THE NEW PYTHON IDE IN RASPBIAN
Installing Thonny on existing set up

安裝過程如下 :

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sun May 13 15:05:30 2018 from 192.168.2.121
pi@raspberrypi:~ $ sudo apt-get install python3-thonny 
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
  python-chardet-whl python-colorama-whl python-distlib-whl
  python-html5lib-whl python-pip-whl python-requests-whl python-setuptools-whl
  python-six-whl python-urllib3-whl python3-jedi python3-venv python3.4-venv
The following NEW packages will be installed:
  python-chardet-whl python-colorama-whl python-distlib-whl
  python-html5lib-whl python-pip-whl python-requests-whl python-setuptools-whl
  python-six-whl python-urllib3-whl python3-jedi python3-thonny python3-venv
  python3.4-venv
0 upgraded, 13 newly installed, 0 to remove and 214 not upgraded.
Need to get 2,859 kB of archives.
After this operation, 4,594 kB of additional disk space will be used.
Do you want to continue? [Y/n] y
Get:1 http://mirrordirector.raspbian.org/raspbian/ jessie/main python-chardet-whl all 2.3.0-1 [170 kB]
Get:2 http://mirrordirector.raspbian.org/raspbian/ jessie/main python-colorama-whl all 0.3.2-1 [20.2 kB]
Get:3 http://archive.raspberrypi.org/debian/ jessie/main python3-jedi all 0.10.2 [127 kB]
Get:4 http://mirrordirector.raspbian.org/raspbian/ jessie/main python-distlib-whl all 0.1.9-1 [141 kB]
Get:5 http://mirrordirector.raspbian.org/raspbian/ jessie/main python-html5lib-whl all 0.999-3 [112 kB]
Get:6 http://mirrordirector.raspbian.org/raspbian/ jessie/main python-six-whl all 1.8.0-1 [14.8 kB]
Get:7 http://archive.raspberrypi.org/debian/ jessie/main python3-thonny all 2.1.10-1+rpi3~jessie [158 kB]
Get:8 http://mirrordirector.raspbian.org/raspbian/ jessie/main python-urllib3-whl all 1.9.1-3 [76.8 kB]
Get:9 http://mirrordirector.raspbian.org/raspbian/ jessie/main python-requests-whl all 2.4.3-6 [241 kB]
Get:10 http://mirrordirector.raspbian.org/raspbian/ jessie/main python-setuptools-whl all 5.5.1-1 [233 kB]
Get:11 http://mirrordirector.raspbian.org/raspbian/ jessie/main python-pip-whl all 1.5.6-5 [126 kB]
Get:12 http://mirrordirector.raspbian.org/raspbian/ jessie/main python3.4-venv armhf 3.4.2-1 [1,438 kB]
Get:13 http://mirrordirector.raspbian.org/raspbian/ jessie/main python3-venv armhf 3.4.2-2 [1,100 B]
Fetched 2,859 kB in 13s (213 kB/s)
Selecting previously unselected package python-chardet-whl.
(Reading database ... 123711 files and directories currently installed.)
Preparing to unpack .../python-chardet-whl_2.3.0-1_all.deb ...
Unpacking python-chardet-whl (2.3.0-1) ...
Selecting previously unselected package python-colorama-whl.
Preparing to unpack .../python-colorama-whl_0.3.2-1_all.deb ...
Unpacking python-colorama-whl (0.3.2-1) ...
Selecting previously unselected package python-distlib-whl.
Preparing to unpack .../python-distlib-whl_0.1.9-1_all.deb ...
Unpacking python-distlib-whl (0.1.9-1) ...
Selecting previously unselected package python-html5lib-whl.
Preparing to unpack .../python-html5lib-whl_0.999-3_all.deb ...
Unpacking python-html5lib-whl (0.999-3) ...
Selecting previously unselected package python-six-whl.
Preparing to unpack .../python-six-whl_1.8.0-1_all.deb ...
Unpacking python-six-whl (1.8.0-1) ...
Selecting previously unselected package python-urllib3-whl.
Preparing to unpack .../python-urllib3-whl_1.9.1-3_all.deb ...
Unpacking python-urllib3-whl (1.9.1-3) ...
Selecting previously unselected package python-requests-whl.
Preparing to unpack .../python-requests-whl_2.4.3-6_all.deb ...
Unpacking python-requests-whl (2.4.3-6) ...
Selecting previously unselected package python-setuptools-whl.
Preparing to unpack .../python-setuptools-whl_5.5.1-1_all.deb ...
Unpacking python-setuptools-whl (5.5.1-1) ...
Selecting previously unselected package python-pip-whl.
Preparing to unpack .../python-pip-whl_1.5.6-5_all.deb ...
Unpacking python-pip-whl (1.5.6-5) ...
Selecting previously unselected package python3-jedi.
Preparing to unpack .../python3-jedi_0.10.2_all.deb ...
Unpacking python3-jedi (0.10.2) ...
Selecting previously unselected package python3.4-venv.
Preparing to unpack .../python3.4-venv_3.4.2-1_armhf.deb ...
Unpacking python3.4-venv (3.4.2-1) ...
Selecting previously unselected package python3-venv.
Preparing to unpack .../python3-venv_3.4.2-2_armhf.deb ...
Unpacking python3-venv (3.4.2-2) ...
Selecting previously unselected package python3-thonny.
Preparing to unpack .../python3-thonny_2.1.10-1+rpi3~jessie_all.deb ...
Unpacking python3-thonny (2.1.10-1+rpi3~jessie) ...
Processing triggers for man-db (2.7.0.2-5) ...
Processing triggers for gnome-menus (3.13.3-6) ...
Processing triggers for desktop-file-utils (0.22-1) ...
Processing triggers for mime-support (3.58) ...
Setting up python-chardet-whl (2.3.0-1) ...
Setting up python-colorama-whl (0.3.2-1) ...
Setting up python-distlib-whl (0.1.9-1) ...
Setting up python-html5lib-whl (0.999-3) ...
Setting up python-six-whl (1.8.0-1) ...
Setting up python-urllib3-whl (1.9.1-3) ...
Setting up python-requests-whl (2.4.3-6) ...
Setting up python-setuptools-whl (5.5.1-1) ...
Setting up python-pip-whl (1.5.6-5) ...
Setting up python3-jedi (0.10.2) ...
2018-06-19 19:50:03 py2deb.hooks[30274] INFO Generated 56 Python bytecode files(s) for python3-jedi package.
Setting up python3.4-venv (3.4.2-1) ...
Setting up python3-venv (3.4.2-2) ...
Setting up python3-thonny (2.1.10-1+rpi3~jessie) ...

安裝完畢後在 Programming 工具集中就可找到 Thonny Python IDE 了, 操作方式與 Windows 上完全相同, 參考 :

好用的 Python 編輯器 Thonny







不過與 Windows 上不同的是, 安裝好後 Python 解譯器會先指向本機 Python 而非 Thonny 自帶的 BundledPython, 開啟 "Tools/Options" 確實是本機的 /usr/bin/Python3.4 :




因此開啟 "Tools/Manage Packages" 視窗時會出現一大堆已安裝套件列表 :




與 Windows 上一樣, 由於不是自帶的 Python, 無法在此 GUI 介面安裝/升級/刪除套件. 但可開啟 "Tools/Open system shell" 來進行操作, 例如升級 pip3 :

pi@raspberrypi:~ $ sudo -H pip3 install --upgrade pip
Downloading/unpacking pip from https://files.pythonhosted.org/packages/0f/74/ecd13431bcc456ed390b44c8a6e917c1820365cbebcb6a8974d1cd045ab4/pip-10.0.1-py2.py3-none-any.whl#sha256=717cdffb2833be8409433a93746744b59505f42146e8d37de6c62b430e25d6d7
  Downloading pip-10.0.1-py2.py3-none-any.whl (1.3MB): 1.3MB downloaded
Installing collected packages: pip
  Found existing installation: pip 1.5.6
    Not uninstalling pip at /usr/lib/python3/dist-packages, owned by OS
Successfully installed pip
Cleaning up...
pi@raspberrypi:~ $ pip3 -V
pip 1.5.6 from /usr/lib/python3/dist-packages (python 3.4)

2018年6月23日 星期六

好書 : 高效率資料分析-使用 Python

昨天從市圖總館借到下面這本好書 :

#  高效率資料分析-使用 Python (歐萊里出版, Clinton W. Brownley 著, 賴屹民譯)


Source : 博客來


這本書是美商 Oreilly 出版的 "Fundations for Analytics with Python" 的中譯本, 此書可說是進入資料科學非常棒的 Data wrangling 入門書, 詳細介紹了如何使用 Python 處理 CSV 與 EXCEL 檔案中的資料為 Python 資料科學可用的形式 (即 DataFrame), 並將其儲存於 sqlite3 或 MySQL 資料庫, 此外還介紹了 matplotlib, pandas, ggplot, seaborn 等資料繪圖工具, 以及資料的描述統計與模型建立等等.

書中的範例程式可於 GitHub 中下載 :

# http://github.com/cbrownley/fundations-for-analytics-with-python

2018年6月22日 星期五

Python 學習筆記 : 網頁擷取 (一) 使用 urllib 與 HTMLParser

上個月測試完 Python 的 SQLite 與 MySQL 資料庫操作後, 本來想要繼續測試 NoSQL 資料庫 (例如 MongoDB), 但想想有 SQLite 與 MySQL 應該就夠用了. 我比較急著想要測試的是 Python 的網頁擷取功能, 也就是撰寫網路爬蟲程式.

所謂網頁擷取 (Web scraping) 是指利用程式下載網頁並從中擷取資訊的技術, 這種技術極具變化性與挑戰性, 因為每一個目標網頁呈現內容的方式都不同, 因此沒有一個通用的資料擷取函數可用, 必須依要探索之目標網頁量身訂做. 這種專門設計用來擷取網頁內容的程式稱為網路爬蟲 (Web crawler/scrapper), 例如 Google 便是利用網路爬蟲取得 Internet 上的網頁內容以便製作搜尋引擎所需之索引.

理想上, 如果每一個網站都有提供 API 讓使用者下載網頁內容之結構化格式 (如 JSON 或 XML 等) 資料給使用者, 那就不需要網頁擷取這種技術了; 但事實上只有部分網站例如臉書或推特有提供 API 或 RSS, 絕大部分的網站都是從資料庫中提取資料後將其嵌在 HTML 網頁中輸出, 因此若要從目標網頁中撈取這些資訊就只能利用網路爬蟲了.

任何可用瀏覽器閱讀的內容都能用網路爬蟲程式抓下來進行剖析以擷取其中有價值之資訊, 然後透過程序排程器 (例如 crontab) 即可將原本需要手動經由瀏覽器查詢才能獲取資料進行分析預測的作業全部自動化, 因此網路爬蟲可說是現代的網路煉金術.

Python 有豐富的函式庫支援網頁擷取功能, 例如內建的 urllib, HTMLParser, 以及功能強大的第三方套件如 requests, ButterflySoup, Scrapy, 與 Sellenium 等方便好用工具, 因此 Python 可說是撰寫網路爬蟲的最佳語言.

不過利用網路爬蟲擷取公開網頁雖然基本上合法, 但實務上須注意下列事項 :
  1. 向網站伺服器提出網頁要求 (Request) 時須修改 HTTP 標頭 (Headers) 中的 User-Agent 選項偽裝成一般瀏覽器, 因為有些伺服器會阻擋非瀏覽器取得網頁.
  2. 下載同一網站網頁的頻率不可太高, 某些網站會封鎖高頻存取之 IP, 甚至以頻繁擷取影響伺服器穩定為由提起法律訴訟.  
  3. 擷取網頁內容作為私人用途沒有問題; 但若將其重新公開發布於自己的網站則可能有法律問題. 特別是有版權之網頁, 須注意勿違反其服務條款.
以下的測試參考了下列 Python 網頁擷取相關書籍 :
  1.  Python 網路爬蟲實戰 (松崗, 胡松濤)
  2.  Python 自動化的樂趣 (碁峰, AL Sweigart)
  3.  Python 程式設計實務 (博碩, 何敏煌)
  4.  Python 初學特訓班 (碁峰, 文淵閣工作室)
  5.  網站擷取 : 使用 Python (碁峰, Ryan Mitchell)
  6.  Python 入門邁向高手之路-王者歸來 (深石, 洪錦魁)
  7.  Python 程式設計入門指南 (碁峰, 蔡明志譯)
  8.  Web Scraping with Python (Packt, Richard Lawson)
  9.  Python Web Scraping Cookbook (Packt, Michael Heydt)
  10.  Automate the Boring Stuff with Python (Starch, AL Sweigart)
  11.  Introduction to Programming Using Python (Pearson, Y. Daniel Liang)
  12.  Python Web Scraping : Fetching data from the web (Packt, Katharine Jarmul)
  13.  Data Wrangling with Python (O'Reilly, Jacqueline Kazil & Katharine Jarmul)
  14.  Web Scraping with Python : Collecting Data from the Modern Web (O'Reilly, Ryan Mitchell) 
本系列之前的文章參考 :

Python 學習筆記 : 安裝執行環境與 IDLE 基本操作
Python 學習筆記 : 檔案處理
Python 學習筆記 : 日誌 (logging) 模組測試
Python 學習筆記 : 資料庫存取測試 (一) SQLite
Python 學習筆記 : 資料庫存取測試 (二) MySQL


Pyhton 2.x 版在網頁擷取方面內建了 urllib 與 urllib2 兩種套件, 都是用來處理 URL 要求用的, 但其功能不同, 主要差異有兩點 :
  1. urllib2 可傳入 Request 物件來設定 URL 要求之標頭; 而 urllib 則只能傳入 URL 字串, 無法操縱標頭資訊, 例如利用竄改 USER AGENT 來進行偽裝.
  2. urllib 有一個 urlencode() 方法可用來產生 GET 查詢字串, 但 urllib2 卻沒有此方法. 
由於有這些差異, 使得用 Python 2.x 版擷取網頁時通常需要同時使用 urllib 與 urllib2. 參考 :

Python: difference between urllib and urllib2
What are the differences between the urllib, urllib2, and requests module?

但是在 Python 3 版已經將這兩個模組整合為 urllib 一個套件, 並將整個套件重組為 urllib.request, urllib.parse, urllib.error, urllib.robotparser 等四個模組 :

 模組 說明
 urllib.request 開啟並讀取 URL
 urllib.error 捕捉 urllib.requests 引起之例外
 urllib.parse 剖析 URL
 urllib.robotparser 剖析 robot.txt 檔

參考 :

https://docs.python.org/3/library/urllib.html
https://docs.python.org/2/library/urllib2.html

以下將以 Python 3 內建的 urllib 模組來擷取並剖析網頁.  


一. 使用 urllib.prase 解析 URL :

urllib 模組中的 urllib.parse 類別用來解析與組合 URL 字串, 此類別常用方法如下 :

 方法 說明
 urlparse(url) 解析傳入之 url, 傳回 ParseResult 物件
 urlunparse(parts) 將 urlparse() 拆解出來的 URL 部件組合回 URL 
 urljoin(base, url) 將 base 位址與 url 位址組合成絕對 URL 網址

urllib.parse.urlparse(url) 方法會解析傳入之 url 字串, 並將結果以 ParseResult 物件 (是一個 tuple) 傳回, 此物件包含下列屬性與方法 :
  1. sheme (協定)
  2. netloc (網址)
  3. path (路徑)
  4. params (參數)
  5. query (查詢)
  6. fragment (頁內錨點)
  7. geturl() 方法 
其中 scheme 是指 http, https 等協定; netloc 是網址或網域名稱; path 是資源檔案之路徑, query 是 ? 後面的查詢字串 fragment 則是 # 後面的頁內錨點 (params 不知是啥?), 參考 :

https://en.wikipedia.org/wiki/Fragment_identifier 

urlunparse() 方法功能與 urlparse() 相反, 將 urlparse() 解析出來的 ParseResult 物件 (即 URL 的各成分組成之 Tuple) 傳入, 此方法會將其組合回完整之 URL. urljoin() 方法則是將傳入之 base 網址與 url 資源檔名兩部分組合為完整之 URL 網址, 若 base 含有資源檔名, 則將被第二參數 url 檔名取代 .

測試範例如下 :

>>> from urllib.parse import urlparse
>>> from urllib.parse import urlunparse
>>> from urllib.parse import urljoin
>>> urla="http://www.abc.com/abc.htm?a=1&b=2&c=3"  #有 query 字串
>>> result=urlparse(urla)
>>> type(result)
<class 'urllib.parse.ParseResult'>
>>> result
ParseResult(scheme='http', netloc='www.abc.com', path='/a/b/c/abc.php', params='', query='a=1&b=2&c=3', fragment='')
>>> result.scheme
'http'
>>> result.netloc
'www.abc.com'
>>> result.path
'/a/b/c/abc.php'
>>> result.params
''
>>> result.query
'a=1&b=2&c=3'
>>> result.fragment
''
>>> result.geturl()
'http://www.abc.com/a/b/c/abc.php?a=1&b=2&c=3'
>>> urlunparse(result) 
'http://www.abc.com/a/b/c/abc.php?a=1&b=2&c=3'
>>> urlb="http://www.abc.com/abc.htm#home"     #有 fragment
>>> urlparse(urlb)          #將 ParseResult 部件組合回完整 URL
ParseResult(scheme='http', netloc='www.abc.com', path='/abc.htm', params='', query='', fragment='home') 
>>> urljoin('http://www.abc.com/abc.htm', 'xyz.htm')     #替換 base 中的資源檔
'http://www.abc.com/xyz.htm'
>>> urljoin('http://www.abc.com', 'xyz.htm')       #串接 base 與資源檔
'http://www.abc.com/xyz.htm'


二. 使用 url.request 下載網頁 : 

urllib.request 是 Python 內建的網頁擷取模組, 其 urlopen() 方法是基於檔案處理的 open() 方法, 以 Connection: Close 標頭向指定之 URL, 向伺服器提出 HTTP/1.1 要求以下載並開啟網頁, 功能相當於 Python 2.x 版的 urllib2.urlopen(), 其 API 如下 :

urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)

其中必要傳入參數 url 為欲開啟之 URL 字串或 Request 物件; data 為一個 urllib.request.Request 物件, 主要用來設定標頭資訊. 備選參數 timeout 用來指定 HTTP, HTTS, 以及 FTP 要求之逾時時間 (單位為秒), 未傳入時以全域預設值為準.

urllib.request.urlopen() 會傳回一個 http.client.HTTPResponse 物件, 可呼叫其 read() 方法來取得伺服器回應之訊息. 我之前在寫 PHP 爬蟲時所參考的 "Webbots, Spiders, And Screen Scrapers (No Starch Press, Michael Shrenk)" 一書中, 作者在其網站中提供了一個簡單的網頁可做為測試的標的 :

http://www.webbotsspidersscreenscrapers.com/hello_world.html

這網頁很簡單, 就是顯示兩行文字而已. 其 HTML 原始碼如下 (Chrome 按 F12) :

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<html>
<head>
<title>Hello, world!</title>
</head>

<body>
Congratulations! If you can read this, <br>
you successfully downloaded this file.
</body>
</html>

將此網頁網址傳入 urllib.request.open() 即可下載此網頁並傳回 HTTPResponse 物件 : 

>>> import urllib.request    
>>> url="http://www.webbotsspidersscreenscrapers.com/hello_world.html"   
>>> response=urllib.request.urlopen(url)    
>>> type(respone)   
<class 'http.client.HTTPResponse'>       #傳回 HTTPResponse 物件
>>> response.read().decode()         #讀取網頁內容並以預設 utf-8 格式解碼
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">\r\n\r\n<html>\r\n<head>\r\n\t<title>Hello, world!</title>\r\n</head>\r\n\r\n<body>\r\nCongratulations! If you can read this, <br>\r\nyou successfully downloaded this file.\r\n</body>\r\n</html>\r\n'

此處 response.read() 預設是讀取下載檔案之全部內容, 但也可以傳入整數指定要讀取幾個 bytes. response.read() 讀回來的是 Byte 類型資料, 需呼叫 decode() 來解碼, 可傳入明確之編碼類型例如 "big5" 或 "utf-8" (預設), 也可以呼叫 Response 物件的 headers 物件之 get_content_charset() 方法從回應訊息中取得回應網頁之編碼方式. 一般正常網頁都會在 meta 標籤中設定 charset, 例如 Yahoo 股市中台積電 2330 的股利政策網頁編碼是 big5 :

>>> url="http://tw.stock.yahoo.com/d/s/dividend_2330.html"   
>>> response=urllib.request.urlopen(url)   
>>> response.headers.get_content_charset()     #編碼為 big5
'big5'     
>>> content=response.read().decode(response.headers.get_content_charset())
>>> type(content)
<class 'str'>

要注意的是, decode() 之傳入參數為字串 (可以不傳參數), 不可傳入 None, 否則會出現錯誤訊息. 下面的範例中因原始網頁的 head 中沒有設定 charset, 回應訊息標頭中也不會有 charset, 因此呼叫 get_content_charset() 時回傳值為 None, 使得 decode() 出現錯誤.

>>> url="http://www.webbotsspidersscreenscrapers.com/hello_world.html" 
>>> response=urllib.request.urlopen(url)
>>> response.headers.get_content_charset()     #沒有設定編碼方式傳回 None
>>> content=response.read().decode(response.headers.get_content_charset())
Traceback (most recent call last):
  File "<pyshell>", line 1, in <module>
TypeError: decode() argument 1 must be str, not None 

如果要用 get_content_charset() 自動取得編碼, 最好加上 if else 來判別是否會傳回 None :

>>> response=urllib.request.urlopen(url)
>>> charset=response.headers.get_content_charset()
>>> if charset:
>>>      content=response.read().decode(chasrset)   #有傳回編碼
>>> else:
>>>      content=response.read().decode()      #沒有傳回編碼

總之, decode() 要嘛不傳參數進去, 否則一定要有字串傳給它.

下載的網頁經過讀取解碼後得到純文字的 HTML 碼, 必須利用剖析技術才能從 HTML 代碼中擷取出有價值的資訊.


三. 使用 HTMLParser 剖析網頁 :

Python 內建的 HTML 剖析器為 html.parser 模組中的 HTMLParser 類別, 它具有容錯能力可處理網路上各種怪異或不對稱的 HTML 代碼. 使用 html.parser 模組剖析網頁首先必須先從 html.parser 匯入 HTMLParser 類別 :

from html.parser import HTMLParser

此 HTMLParser 類別提供下列剖析工具 :

 HTMLParser 方法 說明
 feed(data) 將字串資料 data 餵給剖析器處理
 close() 關閉剖析器, 並強迫處理緩衝區資料
 reset() 重新啟始剖析器, 緩衝區資料將喪失
 getpos() 傳回剖析器目前處理的列與 offset
 get_starttag_text() 傳回最近一次開啟標籤之文字內容

然後定義一個繼承 HTMLParser 的自訂類別例如 MyHTMLParser, 並至少覆寫常用之  handle_starttag(), handle_endtag(), handle_startendtag(), 以及 handle_data() 等方法 :

class MyHTMLParser(HTMLParser):         #繼承 HTMLParser 類別
    def handle_starttag(self, tag, attrs):            #處理起始標籤
        print("Encountered a start tag:", tag)
    def handle_endtag(self, tag):                      #處理結束標籤
        print("Encountered an end tag :", tag)
    def handle_startendtag(self, tag):               #處理起始兼結束標籤 (單一)
        print("Encountered an end tag :", tag)
    def handle_data(self, data):                         #處理資料
        print("Encountered some data  :", data)

然後建立 HTMLParser 物件, 並呼叫此物件之 feed() 方法將 HTML 代碼餵給物件去剖析. 參考 :

https://docs.python.org/3/library/html.parser.html
HTML Parser: How to scrap HTML content
Python urllib.parse.parse_qs() Examples

例如 :

import urllib.request
from html.parser import HTMLParser

class MyHTMLParser(HTMLParser):
    def handle_starttag(self, tag, attrs):
        print("Encountered a start tag:", tag)
    def handle_endtag(self, tag):
        print("Encountered an end tag :", tag)
    def handle_data(self, data):
        print("Encountered some data  :", data)

parser=MyHTMLParser()
url="http://www.webbotsspidersscreenscrapers.com/hello_world.html"
response=urllib.request.urlopen(url)
charset=response.headers.get_content_charset()
if charset:
    content=response.read().decode(chasrset)   #有傳回編碼
else:
    content=response.read().decode()
parser=MyHTMLParser()
parser.feed(content)

執行結果如下 :

Encountered some data  :

Encountered a start tag: html
Encountered some data  :

Encountered a start tag: head
Encountered some data  :

Encountered a start tag: title
Encountered some data  : Hello, world!
Encountered an end tag : title
Encountered some data  :

Encountered an end tag : head
Encountered some data  :

Encountered a start tag: body
Encountered some data  :

Congratulations! If you can read this,
Encountered a start tag: br
Encountered some data  :
you successfully downloaded this file.

Encountered an end tag : body
Encountered some data  :

Encountered an end tag : html
Encountered some data  :

參考 :

[第 16 天] 網頁解析
python 3 筆記 - 利用urllib來存取網頁
https://docs.python.org/3/library/html.parser.html
html.parser — Simple HTML and XHTML parser
How to handle response encoding from urllib.request.urlopen()
網路機器人、網路蜘蛛與網路爬蟲 PHP/CURL程式設計指南
讀後心得 : 一輩子餓不死的技能
Top 10 Best Web Scraping Books
Web Scraping in Python using Scrapy
How To Crawl A Web Page with Scrapy and Python 3
Web Scraping Tutorial with Python: Tips and Tricks
Data Analytics with Python by Web scraping: Illustration with CIA World Factbook
Apress 新書 : Practical Web Scraping for Data Science
新書 : Web Scraping for Data Science with Python (絕版)
Intro to Web Scraping with Python and Beautiful Soup
Python Web Scraping & Sentiment Analysis Tutorial For Beginners
Web-Scraping for Data Science – Part 1
Web Scraping for Data Science — Part 2
Should Data Scientists Learn Web Scraping?
5 Tasty Python Web Scraping Libraries
Web Scraping And Analytics With Python
Coursera : Using Python to Access Web Data
General Tips for Web Scraping with Python
Tutorial: How to do web scraping in Python?
Web Crawling - UCI (PDF)
Python programming — text and web mining (PDF)
Web Crawling and Scraping using Python, Selenium, and PhantomJS
Coursera : Capstone: Retrieving, Processing, and Visualizing Data with Python
How to handle response encoding from urllib.request.urlopen()
Python爬虫入门:Urllib parse库使用详解(二)
# HTML Parser: How to scrap HTML content

2018年6月18日 星期一

2018 年第 24 周記事 : 端午祭祖與年中計畫

本周末適逢端午節連假, 因二哥要補習, 姊姊上周才回來, 所以只有菁菁與水某跟我回鄉下祭祖. 從昨晚開始下了一整天雨 (典型的梅雨), 趁早上 9 點時雨勢稍歇趕緊拜拜.

週二去立志中學參觀後決定讓菁菁讀她喜歡的美容科, 6/20 還是要網路填志願, 仍然將三民填第一志願看看是否會上. 決定不讀觀光後她說李麥克也不去了, 我看她確實對英文沒有很大興趣, 就問 Loren 老師還可不可以退費, 結果說退費要扣 10% 學費, 考慮之後週五晚上還是去辦退費了. 等明年二哥學測結束後, 基本上我家的補習時代應該就正式結束了. 我討厭升學補習, 但贊同才藝補習. 小狐狸不用補習後換我去補習, 我打算去李麥克樓下的京橋學日語, 其實早在姊姊上李麥克時我就想去學了.

本周花了兩三天時間整理 Hinet 信箱, 將原先 99% 爆滿的佔用量刪除垃圾後降到 67%, 應該可以撐很久一段時間. 另外也清理了 Dropbox 內容, 將原本手機自動上傳的照片下載儲存後清空, 手機自動上傳功能也關閉, Dropbox 免費的 5GB 容量用來取代 USB 隨身碟還挺方便的.

週六早上菁菁與水某搭小姨子的車先回鄉下岳父的鐵皮屋幫忙包粽子, 我則花了一整天在整理高雄的兩間浴室與書櫥, 等下午粽子包好再去接她們回家. 今年除了岳母包的粽子外, 建興的太太小琪也送了 10 顆她包的粽子給我們. 母親過世後的這三年每年都會包粽子給我們的高樹阿姨今年母親節前仙遊, 以後再也吃不到她包的好吃粽子了.

過了端午 2018 已經去掉一半了, 下半年我打算開始做這些 :
  1. 製作門牌
  2. 製作測候站木屋
  3. 製作垂直式風機
  4. 完成菜園灌溉自動化
  5. 到京橋學日語
  6. 續寫 AutoIt 實戰
我是個射手座行動派, 崇尚即知即行劍及履及, 最缺的就是完成計畫所需的時間, free time 零散的我必須在時間碎片中打游擊才可能做得了事, 這真是在考驗我時間管理的能耐啊.

2018年6月17日 星期日

好站 : KD 的學習筆記

今天在找搜尋 Numpy 資料時找到這位 KD (張凱迪) 的部落格, 裡面整理了許多不錯的 Python 與 Javascript 學習筆記, 相信花了不少時間, 找時間來好好學習 :

https://blog.kdchang.cc/archive/

其中包含相當多的 Javascript 資料結構筆記, 這比較少見,

ESP8266 WiFi Repeater (NAT Router)

之前在網路上看到有人將 ESP8266 拿來做 WiFi 中繼器用, 我對此非常有興趣, 不過一直沒時間測試. 端午連假這幾天想要試試, 卻遍尋不著當初記下來的網址, 重新搜尋找到下列 GitHub 上的文章, 但今天忙了一整個白天可說是白忙了, 測試失敗 :

A full functional WiFi Repeater

此文目的是透過燒錄韌體將 ESP8266 變成一個 WiFi 中繼器, 或稱為 WiFi 延伸器 (Extender), 較正式的名稱應該是 NAT Router. 中繼器本身具兼具 STA 與 AP 功能, 利用 STA 功能連線 WiFi 基地台以與 Internet 連線; 另外 AP 功能則是接受其他 Station 連線, ESP8266 會在 Station 與 WiFi 基地台雙向透通傳遞封包.

其韌體可在 GitHub 下載 :

https://github.com/rubfi/esp_wifi_repeater/

燒錄韌體之前需先將 ESP-01 的 GPIO 0 接地以進入燒錄模式. 下面是 512KB Flash 的 ESP-01 板設定方式, 若使用 1MB Flash 的 ESP-01 則將 Flash  size 改為 1MByte :






但燒錄完拔除 GPIO 0 的接地後重開機, 開啟手機 WiFi 掃描基地台並沒有找到 SSID 為 MyAP 的基地台, 改用 GitHub 下載檔案中的 Flash 燒錄程式重新燒錄韌體也是一樣 :




此燒錄軟體的 Flash size 是以 Bits 為單位, 上面 4MBit 是給 512KB Flash 的 ESP-01 用的, 因為 0.5 MB*8 Bits= 4 MBits. 若使用 1MB Flash 的 ESP-01, 則 Flash size 要選 8 MBit. 但不管我用 512KB 或 1MB 的 ESP-01, 燒錄後結果都一樣沒作用, 只好暫時放棄, 有空再來研究問題在哪裡.

參考 :

DIY pendrive size WiFi repeater using ESP-01 ESP8266 module.
POWERFUL WI-FI REPEATER (NODE-MCU)
CAN ESP8266 BE MADE TO WORK AS A WIFI REPEATER?

2018年6月16日 星期六

好用的 Python 編輯器 Thonny

前天因為要幫水某焊接項鍊的鍊條, 順便將前陣子新買的六塊 D1 Mini 拿出一塊來焊一焊, 然後燒錄最新的 1.9.4 版韌體, 經測試確實為良品, D1 Mini 真是實踐物聯網的好物啊!  MicroPython 快一年沒玩了, 還真的有點生疏, 於是我找出上回在 momo 買的 "超圖解 Python 物聯網實作入門 (旗標, 趙英傑)" 來看, 當作是複習.

開卷果然有益, 我在 3-1 節讀到作者介紹的 Python 編輯器 Thonny, 雖然我慣用 EditPlus 來寫 Python 程式, 不過有新工具也不妨試用看看. Thonny 是波羅的海三小國之一愛沙尼亞 (Estonia) 的 TARTU 大學計算機科學系所研發的 Python 編輯器, 此軟體輕盈小巧, 非常適合用來開發 Python 應用, 而且此編輯器自帶 Python 3.6.4 版解譯器, 因此若電腦中還未安裝 Python, 不需要另外再安裝 Python 即可執行 Python 程式, 詳見官網介紹 :

http://thonny.org

在右上角的 Download 中可下載各平台之安裝版本, 我下載的是 Windows 最新 2.1.17 版. 安裝完成後執行畫面如下 :




可見 Thonny 有上下兩個視窗, 上方為程式編輯器, 底下是 Shell 介面. 在上方編輯好指令碼後, 按 Run/Run current script 或 F5 會先提示儲存指令檔, 然後才執行程式. 下方 Shell 介面會顯示程式逐列執行之結果. 也可以在下方 Shell 中以互動式方式執行 Python 命令.

在功能選項 Tools/Options 中可設定編輯器, 勾選 Editors 頁籤中的 "Show line numbers" 可顯示行號, 選取底下的 "Recommended maximum line" 為 80 行會在編輯框右邊第 80 行處顯示一條垂線, 避免因為程式碼超出 80 行而在列印時折回或消失 :




而 Fonts 頁籤則可設定字型與其大小 :




基本上我覺得比 EditPlus 好用, 因為它可以直接執行 Python 指令檔, 不需要另外開啟命令提示字元視窗去執行 Python 程式. 在檔案總管中點選 .py 檔案, 按滑鼠右鍵, 點選 "Edit with Thonny" 即以 Thonny 開啟此程式 :




不過要注意的是, Thonny 是在自帶的 BundledPython 環境下執行 Python 指令 (注意是 32 位元版), 並不是在本機所安裝的 Python 環境下. 此 BundledPython 安裝位置如下 :

C:\Users\user\.thonny\BundledPython36

點選 Tools/Manage packages 功能項可知, 剛剛安裝好的 Thonny 只有 pip 與 setuptools 兩個套件而已, 可透過搜尋套件名稱後進行安裝, 這些第三方套件安裝後會放在 BundledPython36 下的 Lib\site-packages 下 :

C:\Users\user\.thonny\BundledPython36\Lib\site-packages





亦即雖然本機的 Python 中已經安裝要用到的套件, 但那與 Thonny 無關, 要順利用 Thonny 執行此 Python 程式必須在 Thonny 的 Python 執行環境中安裝需要用到的套件才行. 安裝套件方式很簡單, 例如要安裝 Numpy, 就在上方文字框中輸入 Numpy, 按 Search 鍵就會幫我們找到最新的 Numpy 版本, 再按底下的 Install 鍵即可 :




安裝完成後左方的套件列表會更新, 顯示 Numpy 套件已可使用了. 此套件列表好用的地方是可以方便搜尋是否已安裝了指定之套件, 若已安裝, 按 "Upgrade" 鈕會自動檢查並安裝新版本. 若要刪除一個現有套件, 點選該套件後按 "Uninstall" 鈕即可刪除 :




安裝好 Numpy 後就可用下方的 Shell 介面測試看看 nparray 功能 :




但是在安裝技術分析套件 TA_LIB 時卻踢到鐵板, 雖然搜尋 "TA_LIB" 可順利搜尋捯此套件, 但安裝時卻出現 "error: Microsoft Visual C++ 14.0 is required" 的錯誤訊息 :




原因是 TA-Lib 是用 C++ 撰寫的, 經二次包裝成 Python 模組, 因此安裝時需要 VC++ 14.0 編譯環境, 亦即需安裝 Visual Studio 2015, 參考之前測試 TA_LIB 的這篇 :

Python Fintech 學習筆記 : 安裝技術指標套件 TA-Lib

為了 TA_LIB 卻要安裝龐大的 Visual Studio 實在太費事了, 其實只要安裝編譯過的 TA_LIB 套件之 wml 檔即可, 此 wml 檔可在加州大學網站下載 :

https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib

但要注意, Thonny 自帶的 BundledPython 3 是 32 位元版的, 因此要下載 32 位元的 wml 檔 :




將此下載之 wml 檔 TA_Lib-0.4.17-cp36-cp36m-win32.whl 放在任一個目錄例如 D:\Python\test 目錄下, 然後點選 "Tools/Open System Shell", 這會開啟一個專屬於 BundledPython 的命令提示字元視窗, 在這裡用 pip 安裝的套件會安裝到 BundledPython 裡面 :




切換目錄至存放 TA_LIB 之 wml 檔處, 然後用 pip3 install 安裝此 wml 檔 :




安裝成功後再點選 "Tools/Manage Package" 即可看到 TA_LIB 了 :




使用 Shell 介面匯入 TA_LIB 函式庫測試平滑移動平均如下 :

Python 3.6.4
>>> import talib
>>> import numpy as np
>>> close=np.random.random(100)
>>> print(talib.SMA(close, 5))




注意, Thonny 內建的 pip 模組為 9.0.1 版, 可用下列指令更新 pip 版本 :

python -m pip install --upgrade pip

You are using pip version 9.0.1, however version 10.0.1 is available.
You should consider upgrading via the 'python -m pip install --upgrade pip' command.

C:\Users\user>python -m pip install --upgrade pip
Collecting pip
  Using cached https://files.pythonhosted.org/packages/0f/74/ecd13431bcc456ed390b44c8a6e917c1820365c
bebcb6a8974d1cd045ab4/pip-10.0.1-py2.py3-none-any.whl
Installing collected packages: pip
  Found existing installation: pip 9.0.1
    Uninstalling pip-9.0.1:
      Successfully uninstalled pip-9.0.1
Successfully installed pip-10.0.1

由於打算以後改用 Thonny 開發 Python 應用, 因此我也將全部機器學習, 資料科學, 以及網路爬蟲用的要用到的套件都安裝在 Thonny 自帶的 Python 裡面了, 詳如下表所示 :
  1. numpy : 資料科學
  2. scipy : 資料科學
  3. matplotlib : 資料科學, 圖像處理
  4. pillow : 圖像處理
  5. pandas : 資料科學
  6. scikit-learn : 機器學習
  7. keras : 機器學習
  8. tensorflow : 機器學習
  9. theano : 機器學習
  10. requests : 網路爬蟲
  11. bs4 (BeautifulSoup4) : 網路爬蟲 
  12. scrapy : 網路爬蟲  
  13. selenium : 網路爬蟲, 瀏覽器自動化
  14. django : 網頁架站
  15. tesseract : OCR 圖像識別
  16. pyautogui : GUI 軟體操作自動化
  17. pyinstaller : 執行檔打包工具
  18. ggplot : 繪圖工具
  19. bokeh : 繪圖工具
  20. plotly : 繪圖工具
  21. seaborn : 繪圖工具
  22. cltk : 自然語言處理
  23. opencv : 影像辨識
  24. twilio : 傳送簡訊
  25. pygame : 音效控制, 遊戲
我在安裝 tensorflow 時出現錯誤訊息 :

"Could not find a version that satisfies the requirement tensorflow (from versions: )
No matching distribution found for tensorflow"

根據 Tensorflow 官網說明, 目前 Tensorflow 僅支援到 Python 3.5 版而已 :

"TensorFlow only supports version 3.5.x of Python on Windows. Note that Python 3.5.x comes with the pip3 package manager, which is the program you'll use to install TensorFlow."

因此要等 Tensorflow 支援 Python 3.6 版釋出才能安裝了. 職此之故, 我只好安裝 Theano 當作 Keras 的張量運算引擎, 雖然 Theano 已於今年停止更新, 但還是可用. 

參考 :

在 Windows 中安裝 Python 機器學習套件
https://keras.io/backend/

其次, 安裝 Scrapy 時出現如下錯誤, 切到 System shell 用 pip3 install scrapy 指令安裝也是一樣, 訊息如下 :

c:\users\user\.thonny\bundledpy
thon36\include\site\python3.6\Twisted" failed with error code 1 in C:\Users\user\AppData\Local\Temp\p
ip-install-qghycg7d\Twisted\

最後去 pypi 下載 whl 檔在 System shell 安裝也是失敗 :

pip3 install Scrapy-1.5.0-py2.py3-none-any.whl

從錯誤訊息看來是在安裝 Twisted 套件時失敗, 單獨安裝 Twisted 也是失敗, 最後在 Stackoverflow 找到解決辦法, 原來要到加州大學爾灣分校網站下載 Twisted 的 mhl 檔來安裝才行 (要 32 位元版的) :

https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted

到 "Tools/Open system shell" 開啟系統命令視窗以下列指令安裝 Twisted :

pip3 install Twisted-18.4.0-cp36-cp36m-win32.whl

或者在 "Tools/Package manager" 中點選左方框最上面的 <INSTALL> , 然後點中間 "Install from local file" 下的 here 超連結, 在跳出的檔案總管中選擇已下載之 Twisted 的 whl 檔案也可以. 




Twisted 單獨安裝好後再安裝 Scrapy 就成功了.

OpenCV 套件也是要到加州大學網站下載 whl 檔案來安裝 :

https://www.lfd.uci.edu/~gohlke/pythonlibs/#opencv

注意, whl 檔案全部都是 32 位元版本的 (Python 3.6).


2018-06-23 補充 :

關於用 Thonny 自帶的 Python 3.6.4 跑 Tensorflow 問題有解了, 傍晚回到鄉下, 晚飯後在鄉下的電腦上安裝 Thonny 的外掛套件時發現, 原來 Thonny 可以讓我們選擇 Python 解譯器, 如果本機已經有裝 Python 解譯器也安裝了許多外掛套件的話, 其實不需要在 Thonny 自帶的 BundledPython 上再次安裝套件, 只要在 "Tools/Options/Interpreter" 頁籤中選擇本機 Python 為 Thonnny 的解譯器即可 :




如圖所示, Thonny 自帶的 Python 為較新版之 Python 3.6.4 (標 default 者), 切換至本機安裝之 較舊之 Python 3.6.1 後, 這時底下的 Shell 視窗就會顯示 Python 解譯器版本變更, 由於本機 Python 已安裝 Keras + TensorFlow, 因此可以順利跑機器學習程式碼.

切換為本機 Python 解譯器後, 開啟 "Tools/Manage package" 視窗顯示的便是本機 Python 已經安裝的套件了, 而非自帶 BundledPython 的陽春 pip 與 setuptools 了 :




不過當切換為本機 Python 後, 搜尋到欲安裝的套件後底下不會出現 Install 按鈕; 而且點選左方已安裝套件時只會顯示其版本與下載資訊, 底下不會顯示 Upgrade 與 Uninstall 按鈕, 畢竟這是系統的 Python, 不是 Thonny 自帶的 Python, 不好做任何更動 :




當切換為本機 Python 後, 欲安裝/升版/刪除本機 Python 中的套件可開啟 "Tools/Open system shell" 視窗進行操作, 這時作用的對象就是本機 Python 解譯器了.

綜上所述, 我想如果本機原來就已經有 Python 解譯器的話, 由於早已安裝許多外掛套件, 就不需要在 Thonny 的自帶 Python 中再安裝一次套件了, 直接將解譯器改為使用本機 Python 即可, 特別是跑 TensorFlow 要求 64-bit Python 3.5 的情況. 如果電腦中本來就沒有 Python 解譯器, 又沒有類似 TensorFlow 這種特殊考量, 那麼只要在 Thonny 中安裝所需套件即可, 本機不用再安裝 Python 了.

2018年6月15日 星期五

美國憲法第一修正案

今天在雅虎新聞看到老包提到美國憲法第一修正案旨在保護言論自由, 好奇之下查了 Wiki 百科說明, 原來它的範圍不只言論自由, 事實上是含括了宗教, 新聞, 言論, 以及結社等人民的四大自由, 屬於美國權利法案 (即美國憲法修正案前十條) 的一部分, 參考 :

https://zh.wikipedia.org/wiki/美国宪法第一修正案

"第一修正案(Amendment I) 禁止美國國會制訂任何法律以確立國教;妨礙宗教自由;剝奪言論自由;侵犯新聞自由與集會自由;干擾或禁止向政府請願的權利。"

此修正案起源於維吉尼亞自治領所制定之維吉尼亞權利法案, 其中第 12 條提到 : 

"出版自由乃自由的重要保障之一,絕不能加以限制;只有專制政體才會限制這種自由。"

事實上專制政體不只限制出版自由而已, 所有四大自由全部被視為會顛覆政權的毒蛇猛獸而一律加以限制. 獨裁政權 (幾乎都號稱民主) 大都使用暴力革命取得權力 (但希特勒與墨索里尼是例外), 因此對於失去權力會因極度缺乏自信而感到無限焦慮, 因此毫無疑問都會限制與控制人民的自由. 

推翻人者恆被推翻, Oh, My God! 人類到底要輪迴幾次才學得會啊! 

2018年6月14日 星期四

農藥機器人

今天在東森新聞看到這篇 :

旋轉吧!農藥機器人 省95%除草劑 農夫笑了

真是太有創意了! 大熱天的就讓機器人幫忙做農事, 農夫就在樹下納涼吧! 反正太陽能板會產生源源不絕的電力, 連續工作 12 小時也不怕中暑.




這款機器人是瑞士 Ecorobotix 公司預計 2019 年上市的農藥機器人, 不過影片我看起來倒像是拔草機器人. 我想它的手臂尖端應該是注射筒, 精準地將適量的除草劑注入雜草根部, 這只是雛形, 實際上太陽能板下應該會有一個農藥的藥桶才對.

2018年6月13日 星期三

太陽能驅動的 WiFi 測候站

今天在 Instructable 網站看到下面這篇文章, 作者以 D1 Mini 板子為主控器, 以 BME280 感測器收集溫溼度, 大氣壓力與緯度等數據, 透過板上 ESP8266 晶片的 WiFi 功能連線到 Internet, 利用 Blynk App 與 Blynk 函式庫, 即可在手機上遠距顯示測候資料.

SOLAR POWERED WIFI WEATHER STATION




我去年也做過這項實驗, 不過我是將數據送到 ThingSpeak 網站而非 Blynk. 我也好久沒用 Blynk 了, 等 FinTech 搞定就可以回來玩 Blynk 了.

2018年6月12日 星期二

立志中學報到

昨天跟立志的鍾老師約好今晚 7:30 去學校參觀拜訪, 美容科段主任留校陪我們參觀教學設備, 並解說特色班與建教班教學計畫, 認為菁菁適合念特色班, 多一年時間可準備證照考試, 菁認同後當下就繳交畢業證書報名, 並購買美容科夏季短袖制服 (600) 一套, 運動服全套 (1915), 領帶 (150), 書包 (530) 共 3195 元. 我覺得還是親自走一趟跟老師聊聊比較好. 

2018 年第 23 周記事 : APCS 考試

因週日 6/10 要載二哥去高雄大學參加 APCS 測驗, 所以我周五晚上就提前一天回鄉下了, 週六晚上再回高雄. 為了此次 APCS, 我也把進行中的研究站停下來, 整理了 C 語言的字串函數與二元樹, 但這次試題這兩項卻出得很少. 這次鳳中高大考場只有 3 人報名, 有一人沒到, 實際只有二哥與另一位同學到考.

高雄大學位於楠梓右昌過去, 中山高中附近, 好幾年前怡秀學姊載我來她任教的西語系講解 Praat 語音分析一次, 印象中那時剛創校不久, 校園遼闊樹木甚少, 有一種蒼涼之感. 現在樹木長起來了, 蓊蓊鬱鬱青翠不少. 但我覺得草皮甚為乾燥, 綠化美化做得不好, 我想是灌溉不良所致.

但真正讓我感覺不好的是空調, 這麼大熱天竟然連圖書館都只送風沒冷氣, 我用高科大借書證換了臨時借書證進去看報紙, 可以說是在揮汗如雨狀態下看完的. 走上樓去找書, 在電腦書籍部分也沒找到一本讓我驚豔的好書. 或許好書都被借走了也說不定. 評價一所大學首先要從圖書館藏書看起, 特別是較偏僻的學校, 校外又沒有書店, 藏書一定要豐富先進才行. 以母校高科大來說, 雖然 30 年前工專時期的書都還在書架上, 但新書不斷地與時俱進買進來, 就會吸引我這老校友回校借書.

由於校內福利社/超商甚遠, 中午到校門口雙醬珈哩吃豬排飯, 因為周末的關係, 大部分餐飲店都沒開, 只有兩家做生意. 其實高大校門口也只有這一排透天店家而已, 感覺蠻荒涼的. 我覺得在少子化衝擊下, 高大應該加入高科大整併才對, 要不然以其地理交通條件而言, 將來招生率堪慮. 台灣的高等教育, 不要說私大有倒閉危機, 我看連國立大學都要上緊發條, 找尋整併出路了.

2018年6月11日 星期一

菁菁畢業典禮

今天菁菁國中畢業典禮 (34 屆), 早上請了半天假, 我七點半載她去學校, 順便就到禮堂去, 結果老師說要 8 點半, 我來早了. 於是就先回家整理雜物. 到 8:15 接到菁電話, 說後面家長區都坐滿了, 得用站的.

聽校長在台上介紹, 說本屆會考 5A 比例是左營區第一名, 大約每 6 人即有一位是 5A, 真不簡單. 畢典照例都有歌舞表演, 但老實說菁這一屆跟姊姊與二哥他們比, 舞蹈實力好像弱了點.

典禮結束時走出禮堂剛好下起雨來, 在中庭遇到三信的 Stella 老師要來向報名的學生收畢業證書, 但她沒看到我們, 由於菁說她還沒決定, 為了避免尷尬就趁雨一點先回家了. 沒想到一到家不久便接到她電話, 我只好委婉解釋尚未決定.

芸菁的爸爸特地從大陸回來, 我看她非常高興. 但我問逮勞怎沒看到她爸媽, 她竟然叫他們不要來哩. 我不知道原因為何, 其實畢典是一個過程, 不管有沒有上台領獎, 我都會去參加, 因為那表示在意與關心, 當然如果是工作關係那就沒辦法了. 

下午水某帶菁去仁杏點痣, 她在意臉上的三顆小痣很久了, 我說楊丞琳的痣不是很好看, 但她說位置又不一樣. 那麼愛美就讓她去點了.

2018年6月8日 星期五

菁菁的會考成績

今天會考成績公布, 菁菁拿到 4B1C, 那個 C 就是數學. 依照模擬考成績預期應該是 5B, 據說今年試題比較難, 但是平常都考不及格的社會與自然都能拿到 B 也是很難得了. 二哥中午傳簡訊問菁菁成績如何, 哈! 雖然以前常鬧彆扭, 其實他還蠻關心菁菁的.

晚上去還完書後就去國中體育館聽學校輔導主任講解填志願要點, 菁菁本來叫我不用去, 反正她要填的不是立志美容科就是三信美容科, 不過既然回覆單有交, 還是去聽一下好了. 三隻小狐狸大概是二哥成績最好, 會考成績是 4A1B (英文), 姊姊是 3A2B (自然數學), 菁菁在學科方面是低了一個檔次. 她對讀書沒興趣, 對化妝倒是非常熱中, 不如去念職校學技藝好了.

不過 4B1C 要上三民家商美容可能不容易, 但我還是認為填在第一志願好了. 實際要等 6/20 公布序號後再來評估.

2018-06-11 補充 :

永遠都不要因為成績而責備孩子, 我家小狐狸只有二哥成績較好, 但也讀得很辛苦. 我也曾經想當虎爸, 但 ... 那太累了.

好書 : 一步到位! RWD 網頁程式設計

前天晚上去鳳山載二哥時提早了半小時出門, 就是為了去鳳山家樂福買這本書, 因為此書主要是介紹如何用 Bootstrap 來做前端頁面布局. 本來上周六就想買, 但猶豫之下沒買, 回來後去明儀找了一本有講 Bootstrap 的書, 感覺還是下面這本寫得好 :

一步到位!RWD 網頁程式設計:用 HTML5、CSS3、Bootstrap 打造響應式網頁




Bootstrap 是前端網頁框架, 兩年前同學老張工作有用到問我會不會, 當時我還在學 jQuery, 沒時間學別的框架, 而且對 Bootstrap 發展態勢還在觀望. 最近對 RWD 突然有興趣, 而 Bootstrap 似乎有站穩腳跟, 有空可以來學學.

2018年6月7日 星期四

憤激之言必悔

昨天早上因工作搭檔出差, 工作繁多之下又突然被通知要參加一個無聊的會, 生氣之下講了激憤之語, 因為我最痛恨這種虛擲光陰的會了.

晚上就寢前想起白天這件事, 或許時過境遷了反而感到後悔. 憤激實在是我最大的毛病, 不論是對身邊事務或時局的不滿, 容易基於私憤或義憤而語出狂悖, 顯然我是沒記取昔日軍中的教訓.

一句話可以救人一命, 但也可以將人推入深淵 , 嘴巴可說是不帶刃的利器. 一時激憤固然獲得宣洩, 但所造成的傷害很有可能無法彌補而一輩子遺憾, 後悔莫及. 小事不忍憤, 大事必遺恨. 

以前一直覺得蠟筆小新裡面妮妮的媽媽一不爽就關起門來打布娃娃兔子, 她是不是有問題啊? 現在想起來反而覺得不會打兔子只會講憤激之語的我才可能有問題吧.

C 語言學習筆記 : 二元樹

今天看完 "動畫圖解資料結構第二版" 第六章的樹狀結構,  這本書真的很不錯, 以大量圖表扼要說明, 還有 CD-ROM 電子書輔助. 不過書中使用類似 Pascal 的虛擬語言來表示演算法, 而用六種程式語言實作的範例則是放在光碟裡.

此外, 我還參考了下列書籍 :
  1. 資料結構-使用 C 語言 (松崗, 陳會安)
  2. 圖說演算法-使用 C 語言 (博碩, 吳燦銘 & 胡昭民)
  3. 啊哈! 圖解演算法必學基礎 (碁峰, 紀磊)
樹狀結構主要有下列成分組成 :
  1. 樹根 (Root) : 最上層節點 (無父節點) 為根節點
  2. 節點 (Node) : 每一筆資料即為一個節點
  3. 葉節點 (Leaf Node) : 沒有子節點的節點, 即終端節點
  4. 子樹 (Subtree) : 節點以下與該節點相連的結構
  5. 樹林 (Forest) : 去除樹根後的結構稱為樹林




樹狀結構有三個主要參數 :
  1. 階度 (Level) : 節點在樹狀結構之階層位置, 樹根之階度為 1. 
  2. 高度 (Height) : 樹的最高階度稱為高度. 
  3. 分支度 (Degree) : 一個節點的子樹個數稱為其分支度. 二元樹節點分支度最多為 2.

樹 (Tree) 其實是圖 (Graph) 的一種特例, 它是一種無迴路 (loopless) 的無向圖, 常見的應用是家譜, 組織架構, 目錄, 或者是檔案總管的資料夾等等. 樹的特徵如下 :
  1. 任意兩節點僅有一條路徑.
  2. N 個節點的樹剛好有 N-1 條邊

最常見的樹狀結構是二元樹, 摘要整理如下 :

一. 二元樹特徵 : 
  1. 二元樹之每一節點分支度 (Degree) <= 2 (最多兩個子節點)
  2. 二元樹可以為空, 一般的樹不可為空且左右子樹無次序之分
  3. 非空二元樹具有根結點 (Root) 與左子樹和右子樹 (有次序之分)
  4. 二元樹最多只能有兩個子節點

二. 各種二元樹 :
  1. 斜曲二元樹 (skewed binary tree) :
    只有左子樹之二元樹為左斜曲 (left-skewed), 而只有右子樹之二元樹為右斜曲 (left-skewed), 此種二元樹適合用鏈結串列實作, 不適合用一維陣列儲存, 因較浪費儲存空間. 
  2. 嚴格二元樹 (strictly binary tree) :
    若二元樹的每個非終端節點都有非空的左右子樹稱為嚴格二元樹, 亦即每個有子樹的節點不會只有一個子樹, 一定有兩個子樹 (要嘛沒有子節點, 要嘛有兩個子節點).
  3. 完滿二元樹 (strictly binary tree)
    高度是 h 的二元樹, 若具有 2**h - 1 個節點, 稱為完滿二元樹, 亦即除最底下一層外, 每一節點均有兩個子節點. 完滿二元樹在第 k 階有 2**(k-1) 個節點. 
  4. 完整二元樹 (complete binary tree)
    高度是 h 的二元樹, 若節點不是葉節點, 一定有兩個子節點, 稱為完整二元樹. 其節點數 n 滿足 2**(h - 1) &lt b &lt= 2**h - 1 之條件.    

可見, 完滿二元樹一定是嚴格二元樹; 也一定是完整二元樹; 但完整二元樹不一定是完滿二元樹, 但. 三者關係如下 :

嚴格二元樹 <= 完整二元樹 <= 完滿二元樹






三. 二元樹的資料結構 : 

二元樹的資料結構可以使用鏈結串列 (linked list) 或一維陣列表示, 鏈結串列適合用來處理斜曲樹; 而陣列則適合處理完滿樹. 例如下面的三階二元樹, 最多有 2**3-1=7 個節點 (完滿), 因此可宣告一個 A[7] 陣列來儲存節點, 其中 A[0] 不使用, 空缺的節點填入特殊值 (例如 0) :




可見以一維陣列實作二元樹時, 節點與子節點之索引有如下關係 :
  1. 左子樹索引是其父節點索引的 2 倍 
  2. 右子樹索引是其父節點索引的 2 倍加 1
利用這兩個規律即可在陣列中建立二元樹, 若加上下列規則, 則可建立二元搜尋樹 :
  1. 每個節點值不同
  2. 每個節點的值須大於左子樹之值, 小於右子樹之值
  3. 左右子樹也是二元樹
以 28, 23, 35, 41, 22, 23 這組資料為例, 其二元搜尋樹如下 :





五. 二元樹的走訪 : 

以陣列表示的二元樹可用下列三種方式走訪 :
  1. 前序追蹤 (MLR)
  2. 中序追蹤 (LMR)
  3. 後序追蹤 (LRM)
其中 M 表示 Middle (樹根), L 表示 Left (左子樹), R 表示 Right (右子樹).

下面程式為改寫自 "動畫圖解資料結構第二版" 的完滿二元樹走訪 :

#include <stdio.h>
#include <stdlib.h>

#define Num 20

void CreateBinaryTree(int*, int);
void Postorder(int);
void Inorder(int);
void Preorder(int);

int data[Num]={0};
int BinaryTree[Num]={0};

int main(void) {
    int n;
    printf("請輸入節點個數:");
    scanf("%d", &n);
    printf("請輸入這 %d 個節點的內容:\n", n);
    for (int i=0; i<n; i++) {
       scanf(" %d", &data[i]);
       }
    CreateBinaryTree(data, n); //呼叫建立二元樹之副程式
    printf("二元樹前序追蹤的結果:\n");
    Preorder(1);   //呼叫前序之副程式
    printf("\n");
    printf("二元樹中序追蹤的結果:\n");
    Inorder(1);   //呼叫中序之副程式
    printf("\n");
    printf("二元樹後序追蹤的結果:\n");
    Postorder(1);   //呼叫後序之副程式
    printf("\n");
    system("PAUSE");
    return 0;
    }

void CreateBinaryTree(int data[], int n) {   //建立二元樹
    int node=1, temp;
    for (int i=0; i<Num; i++) {BinaryTree[i]=0;}  //初值設定
    for (int i=0; i<n; i++) {
        BinaryTree[node]=data[i];
        node=node + 1;
        }
    }

void Postorder(int node) {   //後序追蹤
    if (BinaryTree[node] != 0) {
         Postorder(2*node);    //遞迴左子樹
         Postorder(2*node+1);  //遞迴右子樹
         if (BinaryTree[node] != 0) {  //列印樹根
             printf("%d ",BinaryTree[node]);
             }
         }
    }

void Inorder(int node) {  //中序追蹤
    if (BinaryTree[node] != 0) {
        Inorder(2*node);   //遞迴左子樹
        if (BinaryTree[node] != 0) {  //列印樹根
            printf("%d ", BinaryTree[node]);
            }
        Inorder(2*node+1);  //遞迴右子樹             
        }
    }

void Preorder(int node) {  //前序追蹤
    if (BinaryTree[node]!=0) {
         if (BinaryTree[node]!=0) {
             printf("%d ",BinaryTree[node]);  //列印樹根
             }
         Preorder(2*node);    //遞迴左子樹
         Preorder(2*node+1);  //遞迴右子樹
         }
    }


執行結果如下 :

請輸入節點個數:9
請輸入這 9 個節點的內容:
38
78
10
65
19
86
33
72
29
二元樹前序追蹤的結果:
38 78 65 72 29 19 10 86 33
二元樹中序追蹤的結果:
72 65 29 78 19 38 86 10 33
二元樹後序追蹤的結果:
72 29 65 19 78 86 33 10 38
請按任意鍵繼續 . . .

--------------------------------
Process exited with return value 0
Press any key to continue . . .



四. 二元搜尋樹 : 

上面的範例僅僅是將資料依序填入陣列中, 如果填入時加入如下規則, 所建立的二元樹稱為二元搜尋樹 : 
  1. 每個節點值不同
  2. 每個節點的值須大於左子樹之值, 小於右子樹之值
  3. 左右子樹也是二元樹
範例程式如下 :

#include <stdio.h>
#include <string.h>

void CreateBTree(int btree[], int data[], int n) {
   int i, j;  //i=原始陣列索引, j=二元搜尋樹陣列索引
   btree[1]=data[1];   //以第一個資料為二元樹根節點
   for (i=2; i<=n; i++) {    //從第二個資料開始拜訪原始陣列
        j=1;   //二元樹起始索引為根結點=1
        while (btree[j] != 0) {  //拜訪二元樹陣列直到找到空節點 
            if (data[i] > btree[j]) {  //值較大往右子樹走
                j=j*2 + 1;  //右子樹節點索引
                }
            else {j=j*2;}  //左子樹節點索引
            }  //找到二元樹空節點索引就跳出來
        btree[j]=data[i];  //將原始陣列元素填入二元樹空節點 
        }
   }
 
int main(void) {
   int length=10;
   int data[]={0,5,6,4,8,2,3,7,1,9};  //第一個索引用不到填 0 
   int btree[16]={0};   //預設 0 表示無任何節點
   printf("原始陣列內容:\n");
   for (int i=0; i<length; i++) {  //顯示原始資料陣列內容
       printf("[%d] : %d\n", i, data[i]);
       }
   printf("\n");
   CreateBTree(btree, data, 9);  //建立二元搜尋樹
   printf("二元樹內容:\n");
   for (int i=0; i<16; i++) {  //顯示二元搜尋樹料陣列內容 
       printf("[%d] : %d\n", i, btree[i]);
       }
   printf("\n"); 
   return 0;
   }

執行結果如下 :

原始陣列內容:
[0] : 0
[1] : 5
[2] : 6
[3] : 4
[4] : 8
[5] : 2
[6] : 3
[7] : 7
[8] : 1
[9] : 9

二元樹內容:
[0] : 0
[1] : 5
[2] : 4
[3] : 6
[4] : 2
[5] : 0
[6] : 0
[7] : 8
[8] : 1
[9] : 3
[10] : 0
[11] : 0
[12] : 0
[13] : 0
[14] : 7
[15] : 9

--------------------------------
Process exited after 0.07975 seconds with return value 0
請按任意鍵繼續 . . .