2019年8月14日 星期三

2019 年第 32 周記事

上週五 (8/9) 早上上班經過素食店, 進去買午餐時, 意外發現葉菜怎麼這麼多, 平常快八點時幾乎只剩一項葉菜, 很高興地選了三種葉菜, 外加最愛的破布子豆腐才 40 元. 照例要從公司後門進停車棚, 咦, 後門怎麼還沒開? 於是繞一圈到前門, 前門警衛則一臉狐疑地看我. 到機車停車棚一看, 哇, 空位怎麼這麼多? 難不成今天放颱風假? 不可能啊! 明明吃早餐時看新聞只有台北放假啊? 看一下手錶, 咦, 是 07:05! 哈, 我提早一小時上班了! 從起床後一整個早上都只看分沒看時, 難怪要上班時手機鬧鐘響, 還以為是之前按了貪睡鈕. 真是一個烏龍颱風上班天. 但因為台北的同事放假, 所以一整天也沒電話.

爸爸節收到菁菁寫的卡片讓我超開心, 以前小狐狸們寫的卡片我都有收在盒子裡或貼在臥室牆上, 打程式時抬頭看到就覺得心情很好. 週六水某與菁菁一起回鄉下, 買了提拉米蘇回去給爸過父親節, 而且菁菁也寫了卡片給阿公哩! 週日婷婷有輪休, 本來想載小舅與舅媽去吃牛肉麵, 但水某想吃粄條, 所以去市集買完菜就去廟口, 剛好遇到小媛姊姊在店裡幫忙, 真是好久不見了, 還要我改天帶菁菁來店裡給她看.

去買粄條順路在種子行買了兩株絲瓜苗 :




這兩株是短胖型絲瓜, 我想買長條型的, 但老闆說他們沒有, 目前菜園中的兩株是阿泉伯母給的, 我覺得湯汁濃郁味道很甜美, 現在有一條這兩天就可以採收了 :




如果想再種這種長絲瓜, 爸說剛好有一條躲在芒果樹葉中, 發現時太老了, 正好拿來留種. 巡菜園發現從公司移回來種的香蕉樹 (這是第三代了) 也結果囉!



整理了一周的網頁設計筆記 HTML 篇今天應該可以寫完了!

# 《德魯納酒店》原聲帶這裡聽!

2019年8月12日 星期一

iPad2 無法開機問題

上周水某拿我 iPad2 看韓劇要下載程式, 但一時忘了我的 Apple ID 與密碼, 結果卡在驗證畫面, 拖了一周未處理電池用光, 充飽電竟然無法開機. 上網查詢發現只要同時按住右上方的 Power 鍵與下方的 Home 圓鈕超過 10 秒即可重開機, 我之前也有試過, 但不知道要按長達十秒. 參考 :

#  iPad無法開機怎麼辦?就用這4個方法讓iPad順利啟動!

這件事情凸顯拖拉不處理的壞習性又犯了.

2019年8月10日 星期六

123apps.com 的免費線上視訊剪輯

今天在寫 HTML 筆記時, 為了剪輯 video 要用的 mp4 上網找到 123apps.com 所提供的免費線上剪輯服務, 非常方便好用 :

# https://online-video-cutter.com/tw/

只要從檔案總管中拖曳上傳 mp4 檔案, 即可在瀏覽器中播放影片, 設定好要剪下的片段的起迄點時間, 按儲存即可下載所要的片段 (.mp4) 至本機電腦 (也可以從 Dropbox, Google Drive 或其他網路 URL 取得要剪輯的視訊來源) :





其實 123apps.com 不只提供視訊剪輯, 還有錄音錄影與解壓縮等服務, 參考 :

# https://123apps.com/tw/

此外還有一款不錯的剪輯軟體 LosslessCut, 有 portable 免安裝版, 已用 VirusTotal 60 款資安軟體掃過 ok :

# 超快速、好用、不破壞畫質又免安裝的免費影片剪輯工具《LosslessCut》 1.14版 (Windows/Mac/Linux)

全能女神 IU

最近在愛奇藝追的韓劇是我的偶像 IU 主演的德魯納酒店, 已到第 8 集, 具燦星抱著童年的 13 號房客人中煞昏睡三天後醒來, 趕回酒店卻發現華麗的德魯納突然消失, 張滿月社長, 金書生, 客房長都不見了, WHY? 這一集老實說有幾個片段看不太懂, 比如百歲人瑞洪子奶奶過世的情節是想傳達甚麼? 好期待周末的來臨!

一直想搜尋 IU 以前演過的劇集來看, 今天在 Youtube 找到網紅分享的 "IU韓劇角色類型整理-德魯納酒店高冷傲嬌女 vs. 清純無害李純信", 將 IU 過去的作品做了完整的回顧, 得來全不費工夫 :

# https://www.youtube.com/watch?v=Eu8DX6PpRQw




原來德魯納中的那首歌就是 IU 唱的啊! 等德魯納看完, 就先來看製作人的那些事好了, 金秀賢與少女時代也有客串.

市圖還書 4 本

本周還書四本 :
  1. 機器學習入門 :從玻爾茲曼機械學習到深度學習
  2. 商用微積分
  3. 微積分
  4. 網頁應用程式設計 :使用Node和Express
微積分是借給二哥看的, 但這暑假剩下三周, 我書架上的 Python 書二哥已看完三本, 下周要學 HTML 網頁設計, 微積分應該沒時間. 開學前的讀書計畫 (Python, 網頁, 物聯網) 大概能完成前兩項吧!

今天早上高雄大學宿舍開放網路申請, 已填寫申請書.

2019年8月9日 星期五

HTML 模版語言 PUG

姊姊上回提到這學期 (大二下) 交網頁設計的老師用了一個稱為 PUG 的工具, 老實說碰網頁這麼久了還沒聽過這是啥? 沒想到谷歌大神一找還真的有誒, 我真的是孤陋寡聞了.

我還找到一個不錯的 pug 教學網頁, 參考 :

# HTML模版語言-pug介紹與安裝

Apress 出了一本網頁設計的書 (2017), 在第六章裡面有介紹 PUG 以及如何在 Node.js 的 Express 框架中使用 PUG 檔案 :

# Programming Web Applications with Node, Express and Pug (Apress, 2017)


Source : Apress


對非理工科系學生來說或許 PUG 是個好工具, 不過我認為直接從 HTML 學習網頁設計比較好, 比較有真實感, 其實那不是程式設計, 只是內容與排版而已, 沒有想像中難學.

2019年8月7日 星期三

申請免費 PHP 虛擬主機 000a.biz 與 000webhost.com

這幾天在撰寫二哥的 HTML 教材, 需要申請一個免費伺服主機, 想起之前用過兩家操作介面還不錯的 PHP 虛擬主機, 它們現在都已經有線上 File Manager 工具了, 後台管理全部都可在網頁上完成, 上傳網頁檔案不需要再使用 FTP 了 :
  1. 000a.biz  [cPanel 後台管理]
  2. 000webhost.com 
參考 :

# Joomla 學習筆記 (一) : 系統安裝 (000a.biz)
# 在虛擬主機 000webhost 安裝 WordPress

這兩個 PHP 主機用來學習網頁技術很好用, 可將網頁實際上傳到伺服器, 再用電腦或手機檢視效果. 雖然不一定要用到 PHP, 但等需要用到時也可立即派得上用場 (其實 PHP 語法與 Javascript 很類似, 也很容易學習).

不過畢竟是免費主機, 所以網站內容必須遵循其使用規定 (TOS, Terms Of Service), 例如內容不可違反法律與造成伺服器不堪負荷. 用 crontab 觸發執行 PHP 的高級功能雖然有, 但受到只能有一個的限制. 000a.biz 的 crontab 要求必須在 5 秒鐘內完成, 否則會自動被刪除, 而 0000webhost 則時限不清楚.


1. 申請 000a.biz 帳號 :

首先介紹 000a.biz 免費主機帳號申請方式. 首先到 http://000a.biz/ 首頁按 "Sign Up" 並於申請表單中填寫網址 (xxxx.000a.biz), 管理密碼, E-mail, 語系, 以及安全碼, 按 "Register" 鈕即可, 只要網址沒有被使用, 就會顯示申請成功, 並提示要到所登錄的 E-mail 信箱收取確認信 :





開啟確認信, 按信中的確認超連結即完成主機帳號申請 :




完成後會再收到一封信件 :




前面的 Username 與 Password 以及管理後台 cPanel (固定都是 http://cpanel.000a.biz) 與最後面自己申請的網址要記下來.

這時我們申請的網址已生效, 瀏覽此網址會看到一個預設的首頁 :





輸入 cPanel 網址輸入管理帳密即登入管理後台了 :




點選 "FILES" 選單內的 "Online File Manager" 即進入檔案管理頁面根目錄 :





注意, 這是我們的帳號所分配到的檔案系統根目錄, 不要將網頁上傳到這裡, 而是要放在 htdocs 子目錄下才有效, 點 htdocs 進入子目錄, 裡面預設已有兩個檔案, 其中的 index2.html 就是上面所看到的預設首頁 :




可以點選檔案後, 按滑鼠右鍵選 "Delete" 或下方的垃圾桶全部刪除.

按左下角的上傳鈕, 可選擇上傳檔案, 目錄, 或者 zip 壓縮檔. 如果上傳 zip 檔, 完成後會自動解壓縮.

將下面這個簡單的自訂首頁複製到記事本, 然後以 utf-8 編碼存成 index.htm 首頁檔 :

<!DOCTYPE html>
<html>
  <head>
    <title>我的首頁</title>
    <meta charset="utf-8">
  </head>
  <body>
    <h1>歡迎拜訪我的首頁</h1>
  </body>
</html>




上傳至上面的 htdocs 目錄下後重新整理我的網站首頁 :





以上便是 000a.biz 免費虛擬主機的用法.


2. 申請 000webhost.com 帳號 :

這個 000webhost.com 事實上是我過去三年租用的英國虛擬主機 Hostinger 的免費版, 其版面基本上做得比 000a.biz 要精美些.

先到 000webhost.com 首頁按右上角的 "Free Sign Up" 鈕申請帳號 :





它的申請表單非常簡單, 可以用 FB 或 Google 帳號申請, 但最好用左邊的 E-mail 申請. 註冊帳號後一樣需要去信箱收信, 按下信中的超連結會開啟 000webhost.com 網頁來確認 :




確認後就可以按 000webhost 首頁右上角的 "Log in" 登入後台管理頁面, 先按右邊的 "+ Create new site" 鈕新增一個網站 (000webhost.com 一個帳號可以建立好幾個網站) :





注意喔, 這裡第一欄位的 Website name(optional) 就是網站帳號, 如果輸入 mysite 經 000webhost 檢查可用, 則我們的網站網址就是 mysite.000webhostapp.com, 所以最好要自己填, 沒填的話就會由 000webhost 自動幫我們決定. 密碼只要填與後台管理密碼一樣比較不會搞混. 按 Create 鈕後左下角出現此網站管理頁面的入口 :




左下角顯示網站名稱 mysite (狀態為 Running) 以及此網站的網址. 點右上角的 "Quick Actions" 出現兩個選項, "View Site" 會瀏覽網站, 而 "Details" 則列出網站的各項細節如使用了多少頻寬與硬碟等資訊. 按中央的 "Manage Website" 則進入網站管理的頁面 :






按最右邊有上傳符號的按鈕或 Dashboard 裡面 "Files" 的 "File Manager" 會進入檔案管理頁面 :





檔案操作按鈕在最上面, 如同 000a.biz 那樣可進行新增目錄, 移動/刪除/複製/上傳檔案等作業. 將上面的 index.htm 上傳後瀏覽首頁可看到相同的歡迎網頁.

以上兩個免費虛擬主機不論是帳號申請還是後台操作都非常簡單, 用來測試學習網頁前端技術非常方便, 若再學一些 PHP 基本語法便能用來設計動態的 .php 網頁檔了 (可惜它們不支援 Python 動態網頁).

2019-11-02 補充 :

000webhost 的檔案管理介面較難用, 特別是上傳 zip 檔不會自動解壓縮, 而手動解壓縮時按 extract 鈕常常沒反應, 重整網頁多做幾次才會開始解壓縮.

2019年8月4日 星期日

2019 年第 31 周記事

追憶過去沒有甚麼用, 因為它缺乏積極意義, 只會讓人感傷而已. 金剛經云 : 過去心不可得, 現在心不可得, 未來心不可得. 這句話我想了很多年始終無法契入. 我近來的體會是 :

"未來不存在, 因為未來的每一分每一秒不斷襲來化為現在, 而現在又不斷地飛逝成為過去, 所以現在也不存在. 眼前的一切當下就幻滅, 就像所有過去的回憶一樣, 只存在於腦海中. 你能感知的只有當下的一瞬而已. 所以放掉過去的回憶吧! Set you free! 綁在過去是沒有用的, 只有當下的一瞬為真實, 怎麼會讓思緒沉浸在過去而不好好把握這一瞬呢? "

人生不滿百, 但最弔詭的是, 卻花了很多時間幹一些沒有用的事. 別再幹些沒用的事了, Good old days 就封印起來吧.

要常提醒自己的是 :
  1. 管好自己的嘴巴, 如果不想被後悔追一輩子. 
  2. 人一生只有 30000 天可用, 你用掉多少了?
  3. 不要再說等我有空, 擇日不如撞日.
  4. 時間可以換金錢, 但金錢買不到一分一秒. 
本周 Swift 5 筆電因為 5 Win10 1903 升版發生 WiFi 顯示消失問題, 本來要回復出廠設定, 還好 Win10 有回到舊版功能, 但因此花了點時間研究這台筆電的復原方式, 找了一顆之前順發會員日 99 元買的隨身碟製作了救援有備無患, 同時停止自動更新服務, 改手動.

Learning queue :
  1. 安裝 Git 
  2. Flask 測試 (一)
  3. Django 測試 (二)
  4. Numpy
  5. Tkinter
Task queue :
  1. 錄製 Python SQLite 影片
  2. 錄製 Python 檔案處理影片
  3. 錄製 Python tkinter 影片 (一) 
  4. 演練 Win10 USB 修復程序
Project list :
  1. Fintech infomath 
  2. Praat 大量語音數據處理 Python 版
  3. 自動導航空拍機
  4. 菜園灑水系統
  5. 大門 & 菜園監控告警
  6. 水車
今天下午去小舅家看婷婷的雙寶胎兒子, 四個月大七公斤重非常愛玩, 仔細看, 哥哥比較像婷婷小時候. 今天清理臥室, 清出一堆小狐狸們以前的故事書套書與 CD/DVD, 以後就給兩兄弟吧!

德魯納酒店今天看到第 6 集, 越來越好看, IU 演技真的好棒. 裡面演員有幾個似曾相識, 還是水某記性好, 劇中的麻姑神演出過我身後的陶斯裡面的國情院次長權英世, 而公主美羅 (朴宥娜) 演出過天空之城裡冒充哈佛學生的車世麗, 我就想不起來.

市圖還書 1 本 : 松果體的奇蹟

此書前兩周剛借來就被預約了, 可見非常熱門. 看完之後又覺得這本書的理論有點玄, 有些詞句似乎前有有點矛盾. 我是抱著對松果體的好奇而看得, 基本上我對任何左腦覺得不可思議的東西都抱著 open-minded 的態度, 因為我們很可能只是井底之蛙而已.
  1. 松果體的奇蹟 : 覺醒內在潛能, 改寫人生與身體的劇本 /


Source : 博客來


在大腦科學中, 松果體是腦中分泌睡眠荷爾蒙褪黑激素 (一種色胺酸衍生物), 調節生理時鐘的內分泌器官, 位於大腦深處的腦部中央, 解剖學上為第三腦室後方, 左右第四腦室中間, 上面是腦下垂體, 下面是下視丘, 大小約穀粒般大, 紅灰色, 形狀像小的松果. 演化生物學家認為脊椎動物的松果體細胞與眼睛的感光細胞來自相同的演化原型, 早期的脊椎動物頭頂的顱頂眼 (第三隻眼) 具有感光功能, 故推測哺乳類動物在演化中失去顱頂眼後, 松果囊被保留下來並被壓縮成松果體. 在動物學研究中, 松果體在性成熟, 冬眠, 新陳代謝以及季節性繁殖上明顯扮演著重要的角色.



Source : 圓神


哲學家笛卡兒曾經對松果體做了大量研究, 他發現松果體是腦內唯一不成對的器官, 來自雙眼與雙耳的感官印象就是在松果體進行與靈魂溝通之前的統合. 他認為世界是由物質與靈性兩種要素構成, 而人類的靈魂與肉體是透過松果體產生連結, 因此松果體被笛卡兒稱為 "靈魂之座".

參考 :

# Wiki : 松果體


摘要如下 :
  1. 當白天曬到太陽時, 松果體會分泌俗稱幸福荷爾蒙的血清素, 到了晚上則會分泌睡眠荷爾蒙褪黑激素. 另外, 人體中被稱為天然迷幻劑的二甲基胺酸 (色胺類原型的生物鹼) 也是松果體分泌的. 
  2. 松果體之所以被稱為第三隻眼, 一是因為他是對光有所反應的器官, 二是因為它的組成物質是矽. 而矽是人眼水晶體主要成分就是矽. 蛇的松果體位於頭頂, 螳螂與蜻蜓的松果體則位於兩眼中間且暴露於外. 
  3. 古代壁畫中出現許多松果的圖形, 例如古埃及的真知之眼 (荷魯斯之眼) 是最具代表性的古埃及標誌, 與人類大腦剖面圖中松果體周圍組織圖一模一樣. 梵諦岡聖彼得大教堂中庭則有一個西元一世紀的巨大松果銅雕, 耶路薩冷聖母安眠堂內也有一顆松果雕像, 背後也隱含了深刻的意義. 松果體在古代已受到相當重視, 為何現代人卻對它一無所知? 因為松果體隱藏著極為驚人的力量, 一般人若知道真相並因此擁有特殊能力將變得很難控制, 因此從某個時期起它便被封印了. 
  4. 對醫療產業而言, 若人們的松果體進入沉睡狀態, 則力量削弱之後便無法與宇宙智慧能量連結, 讓免疫系統保持衰弱狀態對其最有利, 對當權者而言, 讓人覺得自己無能為力, 甚麼都辦不到, 必須仰賴現代醫學, 依靠社會與國家, 就更容易操控了. 一味地遵循他人的說法, 仰賴他人生存, 這已偏離生命的正軌, 我們必須喚醒自己的松果體. 
  5. 松果體是一個憑科學與醫學手段無法衡量的器官.
  6. 人類與一切生命的本質, 是那些眼睛看不見的能量, 是比基本粒子還小的超基本例子粒子所具有的極微小能量. 它打從一開始就存在於沒有時間與空間的地方, 因此我們一直存在於無限的時間裡, 並非從何時開始, 而是原本就存在. 
  7. 超基本粒子原本不具意識, 但當它震動時產生螺旋振動波 (右旋), 在與其他物碰撞時產生了意識. 當超基本粒子以螺旋狀打轉時, 碰巧選擇了右旋, 所以我們靈魂的意識能量呈現右旋的漩渦狀, 這世界是一個右旋能量宇宙. 
  8. 人生真正的目的是導正紊亂的靈魂意識能量, 這只有靠活化松果體才能達成. 讓松果體活化最快的捷徑是--明白 : 我現在的人生與身體經歷的一切都是為了導正自己紊亂的靈魂意識能量而選擇的最佳劇本. 並且抱持--我現在的人生與身體只要保持這個狀態就好了. 亦即, 接受眼前的這一切是活化松果體的第一步. 若對自己的人生與身體狀況感到悲觀, 自我否定, 松果體是無法活化的. 無論事情的發展順不順利, 都要記得抱持--"這樣就好了" 的態度, 百分之百接納眼前的狀況. 
  9. 活化松果體的第二個方法是日出時帶著優閒輕鬆的心情凝視早晨溫和的太陽, 松果體會分泌俗稱 "幸福荷爾蒙" 的血清素. 
  10. 活化松果體的第三個方法--常念海豚語句 :
    (1) 現在的自己肯定沒問題
    (2) 我很感謝現在的自己
    (3) 我好喜歡現在的自己
    (4) 我非常了解現在的自己
    (5) 現在的自己就是宇宙的一切
  11. 活化松果體的第四個方法--戴正 12 面或正 20 面的水晶.
  12. 活化松果體的第五個方法--海豚運動.
  13. 活化松果體的第六個方法--海豚碰觸. 
  14. 活化松果體的第七個方法--海豚歸零, 用腹式呼吸法想像宇宙能量導正了偏離正軌的部位, 消除大腦雜念能量. 
  15. 活化松果體的第八個方法--海豚和諧, 用腹式呼吸法, 吸氣時想像地球能量從脊椎下方往上吸進體內, 身體周圍的自我宇宙泡泡不斷擴大, 身體則漸漸消失; 呼氣時想像智慧能量從松果體順著脊椎往下流動. 
感覺有點像基督徒的信念, 不管成敗順逆都接受, 因為這是上帝旨意. 能接受現況之已然, 才能坦然面對下一步. 不過我對書中有些看法持保留態度, 例如醫療產業界掩蓋事實以謀取長期利益之說. 正如自然能源被石油業核電幫等攔截, 以至於一般人無法接觸之說, 在資訊流通無國界的今天, 實在難以想像. 但天下沒有甚麼不可能之事, 對吧?

2019年8月2日 星期五

Python 學習筆記 : 用 Flask 架站 (一) 請求處理

上週在測試 MicroPython on ESP32 的 picoweb 架站框架時在文件中讀到, 原來 picoweb 其實就是嵌入式設備上的 Flask, 實際用過 picoweb 後讓我對 Flask 有了好感, 似乎比 Django 要簡單些.

昨天中午去高科大取兩本 Flask 的預約書 :

# Flask web development / (2014) (Oreilly)
# Mastering Flask / (2015) (Packt)

其中 Oreilly 這本我覺得寫得很淺顯易懂, 適合給初學者當學習教材, 也是社群最推薦的自學書籍. 以下測試以這本書中的範例為主. 我借到的是第一版, 此書已在 2018 出第二版 :


Source : 博客來


作者 Miguel 是住在美國奧勒岡州的資深工程師, 原先在部落格寫 Flask 教學大受歡迎, 後來歐萊里找他合作將部落格內容出版為這本書. 有強國人將其部落格翻譯成簡體中文, 還有學習筆記 :

# Flask 教程,第一部分:Hello,World!
# Flask 学习笔记

參考 :

# [分享] Flask 網路開發經典書籍: Flask Web Development

除了這本書之外, 本篇測試也參考了如下書籍 :

# Flask : Building Python Web Services (Packt, 2017)
# Learning Flask Framework (Packt, 2015)
# Flask blueprints (Packt, 2015)

學過 Django 之後再來學 Flask 覺得簡單多了, 因為在 Flask 中 views.py 與 urls.py 合在一起, 不需要兩邊配合. 當然 Django 好處是一應俱全, 適合需要快速開發的中大型專案, 但技術上的選擇彈性較小; 而 Flask 則適合小型專案或需快速實作的網站雛形 (Prototyping), Flask 擁有較大自由度, 但技術配合要求較高, 用來架構中大型專案挑戰性也較高.

如果沒時間學 Django 又要很快看到雛形的話就用 Flask, 快又有效. 有人比喻說 Django 相當於 Ruby on Rails; 而 Flask 則相當於 Ruby 的輕量級網頁框架 Sinatra.

關於 Flask 簡介如下 :
  1. Flask 是以 Python 實作的輕量級網站框架 (micro framework), 它是一個極小化 (minimalistic)網站架構 , 只實作了核心的網頁應用程式功能 (包含 routing 路由功能), 而將較進階的功能 (例如認證與資料庫 ORM) 交給擴充套件 (extensions).
  2. 源自一個 Python 社群 Pocoo 成員 Armin Ronacher (原作者) 的一個愚人節玩笑, Flask 的風格受 Ruby 的 Sinatra 影響很大.
  3. Flask 基本上由負責 HTTP 路由的 Werkzeug 工具集與負責 HTML 網頁生成的 Jinja2 模板引擎組成, 另外還包含了 MarkupSafe 字串處理函式庫, 以及用來保存 Session 與 Cookie 資料的資料安全序列化函式庫 ItsDangerous.
  4. Flask 基本上是以 WSGI 介面與 Jinja2 模板為基礎, 內建一個實作 WSGI 伺服器與路由功能的 Werkzeug 工具集, 核心非常輕巧, 沒有預設的資料庫與表單驗證工具, 使用者的選擇性大, 內建的 Jinja2 網頁模板甚至可以替換成其他模板引擎例如 Mako. Flask 大部分功能靠延伸套件 (extension) 擴增. 
  5. Flask 具有 HTTP 請求剖析與彈性的回應處理, 支援 session 管理與安全性 cookies 等功能. 
  6. Flask 不是完整的 MTV 結構, 因為它只實作了 T (Template) 與 V (View) 的功能, 沒有實作 M (Model) 資料庫部分. 
WSGI 是 Python 標準的網頁伺服器介面協定, 定義於 PEP333, 參考 :

# https://www.python.org/dev/peps/pep-0333/

WSGI 協定是 Python 網頁應用程式與伺服器溝通的介面, 來自客戶端的請求會被伺服器以 WSGI 協定封裝後傳送給應用程式, 而應用程式的回應也會用 WSGI 回傳給伺服器, 運作架構如下 :




實作 WSGI 協定的框架事實上具有兩個介面, 一個是面向 Web 伺服器的介面 (Server side), 另外一個是面向應用程式的介面, 架構圖如下 :




Flask 內建的 Werkzeug 伺服器即實作了 WSGI 協定介面, 同時它本身也是一個實作 HTTP 的 Web 伺服器, 可直接與客戶端溝通, 但其效能只能做為開發使用. 實際運營使用例如 Nginx 或 Apache 等伺服器, 它們也都支援 WSGI 介面.

Flask 的相關網站如下 :

# https://palletsprojects.com/p/flask/ (官網)
# https://flask.palletsprojects.com/en/1.1.x/ (教學文件)
# https://github.com/pallets/flask (原始碼)
# https://pypi.org/project/Flask/ (Pypi 發布)
# https://flaskbook.com/ (範例檔)
# http://docs.jinkan.org/docs/flask/quickstart.html (Flask 快速入門)


一. 安裝 Flask 套件 :

在 Windows 下安裝 Flask :

pip3 install Flask

C:\Users\Tony>pip3 install Flask 
Collecting Flask
  Downloading https://files.pythonhosted.org/packages/9b/93/628509b8d5dc749656a9641f4caf13540e2cdec85276964ff8f43bbb1d3b/Flask-1.1.1-py2.py3-none-any.whl (94kB)
Requirement already satisfied: click>=5.1 in c:\python36\lib\site-packages (from Flask) (6.7)
Collecting Jinja2>=2.10.1 (from Flask)
  Downloading https://files.pythonhosted.org/packages/1d/e7/fd8b501e7a6dfe492a433deb7b9d833d39ca74916fa8bc63dd1a4947a671/Jinja2-2.10.1-py2.py3-none-any.whl (124kB)
Collecting Werkzeug>=0.15 (from Flask)
  Downloading https://files.pythonhosted.org/packages/d1/ab/d3bed6b92042622d24decc7aadc8877badf18aeca1571045840ad4956d3f/Werkzeug-0.15.5-py2.py3-none-any.whl (328kB)
Collecting itsdangerous>=0.24 (from Flask)
  Downloading https://files.pythonhosted.org/packages/76/ae/44b03b253d6fade317f32c24d100b3b35c2239807046a4c953c7b89fa49e/itsdangerous-1.1.0-py2.py3-none-any.whl
Collecting MarkupSafe>=0.23 (from Jinja2>=2.10.1->Flask)
  Downloading https://files.pythonhosted.org/packages/b9/82/833c7714951bff8f502ed054e6fbd8bd00e083d1fd96de6a46905cf23378/MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl
Installing collected packages: MarkupSafe, Jinja2, Werkzeug, itsdangerous, Flask
  Found existing installation: Werkzeug 0.14.1
    Uninstalling Werkzeug-0.14.1:
      Successfully uninstalled Werkzeug-0.14.1
Successfully installed Flask-1.1.1 Jinja2-2.10.1 MarkupSafe-1.1.1 Werkzeug-0.15.5 itsdangerous-1.1.0

可見 Flask 中的兩個主角就是 Werkzeug 網頁伺服器與 jinja2 模板引擎. 安裝好後匯入 flask 套件, 檢視版本與套件內容如下 :

>>> import flask 
>>> flask.__version__   
'1.1.1'
>>> type(flask) 
<class 'module'> 
>>> dir(flask) 
['Blueprint', 'Config', 'Flask', 'Markup', 'Request', 'Response', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', '__version__', '_app_ctx_stack', '_compat', '_request_ctx_stack', 'abort', 'after_this_request', 'app', 'appcontext_popped', 'appcontext_pushed', 'appcontext_tearing_down', 'before_render_template', 'blueprints', 'cli', 'config', 'copy_current_request_context', 'ctx', 'current_app', 'escape', 'flash', 'g', 'get_flashed_messages', 'get_template_attribute', 'globals', 'got_request_exception', 'has_app_context', 'has_request_context', 'helpers', 'json', 'json_available', 'jsonify', 'logging', 'make_response', 'message_flashed', 'redirect', 'render_template', 'render_template_string', 'request', 'request_finished', 'request_started', 'request_tearing_down', 'safe_join', 'send_file', 'send_from_directory', 'session', 'sessions', 'signals', 'signals_available', 'stream_with_context', 'template_rendered', 'templating', 'url_for', 'wrappers']

其中 config 類別用來設定網站, request 類別用來處理客戶端的要求, 而 Flask 類別則是整個 Flask 套件的核心, 用 Flask 類別所建立的應用程式物件 (application instance) 就是網站運作的中央註冊站 (central registry), 舉凡 view 回應處理函數, URL 路由, 模板設定等等都是透過此物件實例.

建立一個 Flask 網站首先須從 flask 套件匯入 Flask 類別, 再呼叫 Flask() 建構子, 它會傳回一個 Flask 物件 (application instance), 用 dir() 可顯示其所提供的豐富方法, 這些方法即是讓 Flask 網站應用程式運作的工具集. Flask() 的傳入參數 __name__ 代表此應用程式名稱, 用來讓伺服器知道應用程式的位置, 並以此做為存取網站其他資源如靜態檔或模板位置之參考點.

>>> from flask import Flask 
>>> app = Flask(__name__)     #app 是任意取的, 慣例都用 app
>>> type(app) 
<class 'flask.app.Flask'> 
>>> dir(app) 
['__call__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_before_request_lock', '_blueprint_order', '_find_error_handler', '_get_exc_class_and_code', '_got_first_request', '_register_error_handler', '_static_folder', '_static_url_path', 'add_template_filter', 'add_template_global', 'add_template_test', 'add_url_rule', 'after_request', 'after_request_funcs', 'app_context', 'app_ctx_globals_class', 'auto_find_instance_path', 'before_first_request', 'before_first_request_funcs', 'before_request', 'before_request_funcs', 'blueprints', 'cli', 'config', 'config_class', 'context_processor', 'create_global_jinja_loader', 'create_jinja_environment', 'create_url_adapter', 'debug', 'default_config', 'dispatch_request', 'do_teardown_appcontext', 'do_teardown_request', 'endpoint', 'env', 'error_handler_spec', 'errorhandler', 'extensions', 'finalize_request', 'full_dispatch_request', 'get_send_file_max_age', 'got_first_request', 'handle_exception', 'handle_http_exception', 'handle_url_build_error', 'handle_user_exception', 'has_static_folder', 'import_name', 'inject_url_defaults', 'instance_path', 'iter_blueprints', 'jinja_env', 'jinja_environment', 'jinja_loader', 'jinja_options', 'json_decoder', 'json_encoder', 'log_exception', 'logger', 'make_config', 'make_default_options_response', 'make_null_session', 'make_response', 'make_shell_context', 'name', 'open_instance_resource', 'open_resource', 'open_session', 'permanent_session_lifetime', 'preprocess_request', 'preserve_context_on_exception', 'process_response', 'propagate_exceptions', 'raise_routing_exception', 'register_blueprint', 'register_error_handler', 'request_class', 'request_context', 'response_class', 'root_path', 'route', 'run', 'save_session', 'secret_key', 'select_jinja_autoescape', 'send_file_max_age_default', 'send_static_file', 'session_cookie_name', 'session_interface', 'shell_context_processor', 'shell_context_processors', 'should_ignore_error', 'static_folder', 'static_url_path', 'subdomain_matching', 'teardown_appcontext', 'teardown_appcontext_funcs', 'teardown_request', 'teardown_request_funcs', 'template_context_processors', 'template_filter', 'template_folder', 'template_global', 'template_test', 'templates_auto_reload', 'test_cli_runner', 'test_cli_runner_class', 'test_client', 'test_client_class', 'test_request_context', 'testing', 'trap_http_exception', 'try_trigger_before_first_request_functions', 'update_template_context', 'url_build_error_handlers', 'url_default_functions', 'url_defaults', 'url_map', 'url_map_class', 'url_rule_class', 'url_value_preprocessor', 'url_value_preprocessors', 'use_x_sendfile', 'view_functions', 'wsgi_app']

建立 Flask 物件後只要為來自客戶端 (例如瀏覽器) 的各種可能 URL 指配對應之回應處理函數 (稱為 view function) 以便做出適當的回應即可.


二. 用 Flask 建置 Hello 網站 :

開發 Flask 網頁應用程式的基本架構非常簡單 :
  1. 建立一個 Flask 物件 (習慣上通常取名為 app)
  2. 呼叫 app.route() 定義 URL, 並利用 Python 裝飾器 @ (decorator) 註冊 (register) 一個回應處理函數 (稱為 view 函數), 也就是將 URL 與 view 函數綁定在一起.
  3. 呼叫 app.run() 啟動內建的網頁伺服器服務
此架構利用了 Python 設計模式 (design pattern) 中的裝飾器, 所謂的裝飾器是一種特殊的函數 (function), 特別之處是其傳入參數為一個函數, 傳回值也是同一個函數, Python 使用 @ 符號作為裝飾器的語法糖 (syntax sugar), 關於裝飾器參考 :

# Python Decorator 入門教學

不過要注意的是, 透過 app.run() 來啟動內建的伺服器只能用在開發階段 (因為它的效能不高, 只能應付少數流量), 實際運營時應改用正式的伺服器, 例如 uWSGI. 為了讓網頁伺服器只能透過 python xxx.py 啟動, 避免被其他 Python 模組 import 時啟動, 通常會將 app.run() 放在 if __name__ == __main__ 區塊中, 例如 :


測試 1 : 回應純文字 Hello World!

#hello.py
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run(debug=True)

此程式中的裝飾器 @app.route('/') 就是在為 URL '/' (根目錄) 註冊一個 view (回應處理) 函數, 也就是緊接著定義的 hello() 函數. 事實上比較傳統的做法是用 Flask 物件的 add_url_rule() 方法, 它有三個參數, 第一個是 URL, 第二個是端點名稱, 第三個是 view 函數 :

def hello():
    return "Hello World!"
app.add_url_rule("/", "hello", hello)

裝飾器的好處就是簡潔.

另外值得一提的是, Flask 的 view 函數不需要像 Django 那樣傳入 request 參數, Flask 是用全域變數 request 來處理請求訊息. 

將上面程式存成 hello.py 後用 python 執行, 這會啟動 Flask 的開發伺服器 :

D:\test>python hello.py 
 * Serving Flask app "hello" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 194-439-956
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)


可見 Flask 內建的 Werkzeug 開發伺服器預設會監聽本機 localhost (127.0.0.1) 的 5000 埠, 亦即只限本機連線, 不接受外部存取. 開啟瀏覽器連線 127.0.0.1:5000 即顯示 Hello World! 網頁 :




命令提示字元視窗出現兩筆 GET 請求訊息, 第一筆為我們的瀏覽動作所觸發之請求, 第二筆是瀏覽器自行向伺服器發出的網頁小圖示 favicon.ico 的請求 :

127.0.0.1 - - [02/Aug/2019 16:43:46] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [02/Aug/2019 16:43:46] "GET /favicon.ico HTTP/1.1" 404 -

上面測試 1 程式中, app.run() 傳入參數 debug=True 會開啟偵錯功能, 這有兩個效果, 一是當程式有錯誤會在網頁中輸出除錯訊息以便研判錯誤出在哪裡. 二是當程式有改變時會自動重新啟動伺服器. 當發生錯誤時, 訊息除了回應給客戶端, 同時會輸出到 Console 上. 

除了可在 app.run() 中開啟偵錯功能外, 也可以用 debug 屬性或 config[], 例如 :

app.debug=True   或
app.config['DEBUG']=True

除了回應純文字外, 當然也可以回應 HTML 字串 (這才是常態), 例如 :


測試 2 : 回應 HTML 格式的 Hello World!

#hello.py
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "<h1>Hello World!</h1>"

if __name__ == "__main__":
    app.run(debug=True)


此例 hello() 回應了一個 HTML 碼, 將 Hello World! 放在 h1 標籤裡面, 使其成為字體粗大的標題 :




當然 app 最好是回應一個完整的網頁架構 (即有 doctype 宣告, html, body 等標籤元素) 較好, 不過大部分的瀏覽器容錯性很高, 都能接受 HTML 結構不完整的回應.

Flask 內建的 Werkzeug 開發伺服器預設監聽 port 5000, 但可以在呼叫 app.run() 時傳入 port 參數來改變, 例如 :


測試 3 : 指定 host 與 port

from flask import Flask
app = Flask(__name__)
app.debug=True 

@app.route("/")
def hello():
    return "<h1><i>Hello World!<i></h1>"

if __name__ == "__main__":
    app.run(host='127.0.0.1', port=8080)


此例將開啟偵錯改用 app.debug 屬性指定, 而 app.run() 則傳入 host='127.0.0.1' 與 port=8080 兩參數, 因此在用瀏覽器連線伺服器時要改為存取 8080 埠 (改為 127.0.0.1 表示只限本機可以存取).




Flask 內建的 Werkzeug 開發伺服器預設監聽網址 127.0.0.1 的 port 5000, 亦即只能讓本機的客戶端連線而已, 如果要讓本機以外的主機能連線此網站, 必須指定網址為 0.0.0.0, 這表示本機不論被指派的 IP 為何, 區網中的其他主機都可以拜訪, 這可以在呼叫 app.run() 時傳入 host 參數來改變, 網站正式運營時一定要傳入 port='0.0.0.0' 並取消 debug. 

Flask 的 view (回應處理) 函數可以被多個 URL 要求註冊, 或者說, 多個 URL 要求可以綁定到同一個 view 函數上, 只要將這些 URL 的裝飾器串接在一起即可, 例如上面的 hello.py 修改為下面測試 4 :


測試 4 : 多個 URL 註冊同一個 view 函數 

#hello.py
from flask import Flask
app = Flask(__name__)
app.debug=True

@app.route("/") 
@app.route("/hello1") 
@app.route("/hello2") 
def hello():
    return "<h1>Hello World!</h1>"

if __name__ == "__main__":
    app.run(host='127.0.0.1', port=8080)

此程式為 "/", "/hello1", 以及 "/hello2" 三個 URL 註冊了同一個 view 函數 hello, 所以在瀏覽器上拜訪這三個 URL 時都會得到相同的回應網頁 :






但是, 如果拜訪未註冊 view 函數的任何 URL 都會被 Flask 回應一個預設的 404 Not found 網頁 :




三. 用 URL 傳遞變數 :

上面的 hello.py 程式都只有固定的 URL 字串, Flask 支援透過傳遞 URL 變數來達到動態網頁的目的. 傳遞 URL 變數有兩種方式 :
  1. RESTful : /hello/Tony/Huang   
  2. QueryString : /hello?firstname=Tony&secondname=Huang    
在 RESTfull 方式中, 變數是 URL 的一部分, 以階層式目錄串接在 app 名稱後面, 因此在 route() 中要定義各參數之順序. 而 QueryString 方式則是在 URL 後面以 "?" 起始, 各變數以 "&" 串接的 key=value 字串.

首先來看 RESTful 的範例, 將上面的 hello.py 修改為如下 :


測試 5 : 傳遞 URL 變數 : RESTful (單一變數)

#hello.py
from flask import Flask
app = Flask(__name__)
app.debug=True

@app.route("/")
def hello():
    return "<h1>Hello World!</h1>"

@app.route("/hello/<name>") 
def hello_name(name): 
    return "<h1>Hello, %s</h1>" % name 

if __name__ == "__main__":
    app.run(host='127.0.0.1', port=8080)

此程式註冊 view 函數 hello_name(name) 的 URL 攜帶一個變數 name, 預設為字串. 回應字串也可以用 format() 來格式化 :

    return "<h1>Hello, {}</h1>".format(name)

以 URL=localhost:8080/Tony 拜訪此網頁結果如下 :




只要在 URL 後面附帶不同變數, 網頁中也會顯示那個變數.

如果有多個變數可以在 URL 中用 forward slash ("/") 隔開 :

/app/param1/param2/param3 ...

其中第一個是 app 名稱, 後面的都是參數.

在 view 函數中必須傳入同數目之參數 :

def app(param1, param2, param3, ...):

例如 :


測試 6 : 傳遞 URL 變數 : RESTful (多個變數)

#hello.py
from flask import Flask
app = Flask(__name__)
app.debug=True

@app.route("/")
def hello():
    return "<h1>Hello World!</h1>"

@app.route("/hello/<name>/<surname>")   #傳入兩個參數
def hello_name(name, surname):
    return "<h1>Hello, {} {}</h1>".format(name, surname)

if __name__ == "__main__":
    app.run(host='127.0.0.1', port=8080)

此程式為攜帶兩個參數的 URL 註冊了兩個參數的 view 函數 hello_name, 注意, 參數在 route() 的 URL 與 view 函數中的順序不需要一樣, 因為它是根據名稱對應的, 亦即若寫成 def(surname, name) 也是可以的. 在瀏覽器中輸入如下網址 :

localhost:8080/hello/Tony/Huang 

結果如下 :




除了使用 URL 本身來傳遞變數, 還可以用查詢字串 (query string) 來傳遞, 此功能在 Flask 裡由 request 這個全域物件處理, Flask 會將來自客戶端的 HTTP 請求封裝在 request 物件中, 其主要成員如下表 :


 flask.request 成員 說明
 method HTTP 請求方法 (str), 如 'GET','POST', 'PUT' 等
 path URL 路徑, 例如 /hello
 headers HTTP 請求標頭, request.headers.get('attr') 傳回標頭屬性值 (str)
 args URL 傳遞參數 (查詢字串), request.args.get('param') 傳回參數值 (str)
 form 表單內容 (dict), 例如 request.form['pwd'] 取得表單中 name=pwd 之值
 files 取得上傳檔案之暫存位置, 例如 request.files['the_file']
 cookies HTTP cookies, request.cookies.get('username') 傳回參數值 (str) 


使用 request 前須先匯入 :

from flask import request

呼叫 request.args.get() 即可取得帶在 URL 後面查詢字串中的變數, 例如 :


測試 7 : 傳遞 URL 變數 : Query string (多個變數)

#hello.py
from flask import Flask, request

app = Flask(__name__)
app.debug=True

@app.route("/")
def hello():
    return "<h1>Hello World!</h1>"

@app.route("/hello/")
def hello_name():
    name=request.args.get("name") 
    surname=request.args.get("surname")     
    return "<h1>Hello, {} {}</h1>".format(name, surname)

if __name__ == "__main__":
    app.run(host='127.0.0.1', port=8080)

注意, 應用程式名稱後面一定要有斜線 "/", 否則會找不到路由. 但在瀏覽器輸入網址時, 不論有無加 / 都可以, 瀏覽器最後都都會顯示有 / 的 :

localhost:8080/hello?name=Tony&surname=Huang 或
localhost:8080/hello/?name=Tony&surname=Huang

結果如下 :




以上不論是用 RESTfull 或 Query string 傳遞變數, Flask 取得的都是 str 字串型態之資料, 如果是內容是數值, 則需先經 int() 或 float() 等函數轉換才能進行計算.

Flask 在用 RESTful 方式傳遞變數時可以指定變數的資料類型, 這樣就可以不需要額外的型態轉換.

在網頁設計中最常用的 HTTP 方法是 GET 與 POST, 在用裝飾器 @app.route() 設定路由時可以傳入一個 methods 參數 (這是字串串列) 來指定 HTTP 方法, 預設是 GET (故以上測試均使用 GET),  例如 :

@app.route('/', methods=['POST'])                # 只受理 POST 請求
@app.route('/', methods=['GET', 'POST'])     # 同時受理 GET 與 POST 請求

例如 :


測試 8 : 指定 HTTP 方法 : 使用 route() 的 methods 參數

#hello.py
from flask import Flask, request

app = Flask(__name__)
app.debug=True

@app.route("/", methods=["GET", "POST"])    # GET 與 POST 皆可受理
def hello():
    return f"<h1>Hello World!({request.method})</h1>"

if __name__ == "__main__":
    app.run(host='127.0.0.1', port=8080)
    
此程式使用 flask.request.mothod 屬性取得客戶端的 HTTP 請求方法, 然後將其嵌入 f  字串中回應客戶端, 首先直接在瀏覽器網址列輸入 127.0.0.1:8080, 這樣是用 GET 方法, 結果如下 :




測試 POST 的方法可以用網頁表單設定 method="post" 來達成, 寫一個簡單網頁如下 :

<!DOCTYPE html>
<html>
  <head>
    <title></title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
  </head>
  <body>
    <form method="post" action="http://127.0.0.1:8080">
       <button type="submit">Hello (POST)</button>
    </form>
  </body>
</html>

表單 form 裡面只有一個按鈕, method 設為 post, 而 action 則設為上述程式執行後的網址與埠號, 將此網頁儲存後用瀏覽器開啟, 然後按其中的按鈕就會用 POST 方法提交表單 : 





測試 POST 方法也可以利用第三方的 requests 套件 : 

>>> import requests   
>>> res=requests.post("http://127.0.0.1:8080")  
>>> res.text 
'<h1>Hello World!(POST)</h1>'

Flask 2.0 版之後, Flask 物件增加 get() 與 post() 方法可單獨處理 GET 與 POST 方法 : 

@app.get('/')       # 只受理 GET 請求
def hello():
    pass

@app.post('/')     # 只受理 POST 請求
def hello():
    pass

如果要同時處理 GET 與 POST 方法, 可以連續用 @app.get() 與 @app.post() 來裝飾函式, 效果就跟呼叫 route() 時傳入 methods=['GET', 'POST']) 參數一樣 :

@app.get('/')
@app.post('/')    
def hello():
    pass

例如 : 

測試 9 : 指定 HTTP 方法 : 使用 get() 與 post()

#hello.py
from flask import Flask, request

app = Flask(__name__)
app.debug=True

@app.get("/")    # 處理 GET 請求
@app.post("/")   # 處理 POST 請求
def hello():
    return f"<h1>Hello World!({request.method})</h1>"

if __name__ == "__main__":
    app.run()

結果與上面用 @app.route() 相同. 注意, 如果程式中只定義 GET 處理函式或 POST 函式, 則對於另一個未定義處理函式的請求將回應 405 (Method not allowed), 例如 :


測試 10 : 指定 HTTP 方法 : 不允許的請求方法 (405)

#hello.py
from flask import Flask, request

app = Flask(__name__)
app.debug=True

@app.get("/")    # 處理 GET 請求
def hello():
    return f"<h1>Hello World!({request.method})</h1>"

if __name__ == "__main__":
    app.run()

此程式只定義了 GET 請求之處理函式, 如果對它提出 POST 請求將得到 405 回應 :

>>> import requests  
>>> res=requests.post("http://127.0.0.1:3000")    
>>> res  
<Response [405]>
>>> res.text  
'<!doctype html>\n<html lang=en>\n<title>405 Method Not Allowed</title>\n<h1>Method Not Allowed</h1>\n<p>The method is not allowed for the requested URL.</p>\n'


2022-09-17 補充 :

前陣子上了高啟昌老師的 Flask 課程, 其 GitHub 的課程檔案如下 :



2024-02-23 補充 : 

今天找到張宗彥的 iT 邦幫忙文章值得參考 :