2020/01/29

[自己的 Hackathon] L10 盃黑客松之即時開票系統 - 技術分享 (二) - 學習 Google Sheet API 與 Crawler

在上一篇依著微軟官方教學文件
學會了利用 Visual Studio Code 建立以及發佈 Azure Functions
接著就是要寫出我想要的 Crawler 以及即時更新 Google spreadsheet 的功能

流程簡圖

建立測試 Endpoint 來做 POC


因為每個部份我都沒有實際自己寫過
所以方便起見我建立了一個 HTTP trigger 的 Azure Function
在裡面寫一些 POC 的小片段
再把可行的部分搬移到對應的 Crawler 或是 Update Handler 中
我簡單盤點了一下我需要完成的幾項工作

T1. 從中選會網頁上爬到票數
T2. 將票數轉換成我定義好的 JSON 後,塞入 Azure Queue Storage 中
T3. 從 Azure Queue Storage 取出 JSON
T4. 依 JSON 中的對應票數,使用 Google Sheet API 填入 Google spreadsheet 對應的格子中

首先很快的利用 Azure Functions extension 建立好三個 Azure Functions
分別為

1. Timer trigger 的 Crawler
2. Azure Queue Storage trigger 的 Update Handler
3. HTTP trigger 的測試 Azure Function

最先完成的就是最簡單的 T2 與 T3
因為在教學文件中都有介紹到
唯一不同的就是要設計自己的 JSON 樣子而已
{
  "time": "1580121166",
  "votes" : {
    "president" : { "blue" : 1, "green" : 1 },
    "taipei3" : { "blue" : 1, "green" : 1 },
    "taipei4" : { "blue" : 1, "green" : 1 },
    "taipei5" : { "blue" : 1, "green" : 1 },
    "taichung3" : { "blue" : 1, "green" : 1 },
    "hualien" : { "blue" : 1, "green" : 1 }
  }
}


使用 Google Sheet API 填入對應票數


將 Crawler 與 Update Handler 串起後
下一件事情是學習怎麼在 Update Handler 中
用 Google Sheet API 去更新 spreadsheet
我是使用 pygsheets 這個 package
說明文件也算清楚
可以在 requirements.txt 中填入要使用的其他套件
在執行 debugger 的時候會自動用 pip install 幫你安裝

使用 pygsheets 套件前得先取得 Google API 的授權
我是使用 Service Account 的方法
要注意的就是 credentials.json 的檔案位置
一開始一直傳不對的路徑進去
導致卡關了一小段時間

學習怎麼寫 Crawler


我主要是看這個影片
[爬蟲實戰] 如何透過 Python 網路爬蟲 抓取並整理 2018 公投選舉資料?
非常仔細也非常詳盡
裡面介紹了兩個 packages, BeautifulSoup 與 pandas
我只需要用到 pandas 就好
就先用候選人簡歷的頁面練習
原則上是沒有遇到太大的問題
有錯誤的時候稍微再到 pandas 的官網查一下文件
或是利用 Google 都可以找到解答

利用條件式格式設定 highlight 第一名


我本來就會使用 RANK() 這個函式來排名
這次有兩個題目的排序需要預先處理,再餵入 RANK()
又很自以為的不想使用輔助欄 (就是先在其他格子內算出結果)
沒想到也搞了很久

ARRAYFORMULA() 應用


第一個是我要依 "絕對值" 由小到大排名
在這裡學到的一個公式是 ARRAYFORMULA()
ARRAYFORMULA() 可以想成本來 ABS() 這個函式只能計算一個格子的絕對值
套用 ARRAYFORMULA() 後,我們可以計算一個範圍內所有格子個別的絕對值
因此我們可以使用 ARRAYFORMULA() 的結果
再傳入 RANK() 的第二參數裡面使用
這樣我就不必使用一個輔助欄來先行計算絕對值再套用 RANK() 函式了

ARRAYFORMULA() 的結果


第二個是我要依 Max( |∆KMT|, |∆DPP| ) 由小到大排名
上圖中可以看到我用 ARRAYFORMULA() 先取得了一個二維的絕對值結果
下一步我需要把結果套入一個公式來取得兩欄中的最大值
這裡學到的是 QUERY() 這個公式
利用 QUERY() 我可以選出兩欄中最大的那一欄
不過 QUERY() 第二參數是類似 SQL 的語法
因此必須先做一次 TRANSPOSE() 把列與行對換
才能用 MAX() 語法取得最大值 (非常自找麻煩)

QUERY() 應用

最後就是再把結果 TRANSPOSE() 一次然後選出數字的那欄
就能餵到 RANK() 的第二參數中
非常囉嗦的方法,但是這樣就不必有輔助欄了

2020/01/18

[自己的 Hackathon] L10 盃黑客松之即時開票系統 - 技術分享 (一) - 學習 IDE 與 Azure Functions 基礎

這篇文章主要是我依著微軟官方文件
"使用 Visual Studio Code 在 Python 中建立和部署無伺服器 Azure Functions"
學習一些 Azure Functions 基礎的過程
分享我遇到的一些問題
如果你跟我一樣習慣英文介面的 Visual Studio Code
同樣的教學文件也有英文版 (較容易找到對應的 IDE 功能)

必要條件申請與安裝


文件中有三個前置要先準備好的東西
1. Azure 訂用帳戶 (Azure Subscription)
2. Azure Functions Core Tools
3. Visual Studio Code 以及對應的 extensions

首先最重要的當然是申請 Azure 帳號
新帳號在期限內能享有蠻多福利的
像是前三十天有 NTD 6,100 的額度可以使用
我多年前已經申請過了所以直接跳過這一步

新帳號可享的一些福利

再來就是安裝 Azure Functions Core Tools
先到 Node.js 下載頁面取得 v12.14.1 x64 的 Windows Installer 並安裝
接著依文件再輸入
npm install -g azure-functions-core-tools@3
文件中有提醒 "安裝 Node.js 時,請使用預設選項,「不要」 選取自動安裝必要工具的選項。"

最後就是安裝 Visual Studio Code
以及兩個對應的 extensions - Python extensionAzure Functions extension
我是使用 python 作為實作這次系統的程式語言
所以要記得安裝 python 3.7.5 64bit

執行 Azure Functions Core Tools 的錯誤

以上三個前置都準備好後
依著文件說明在 vscode 中登入你的 Azure 帳戶
然後在 vscode 裡的 Terminal 執行 func
這時候我遇到了一個問題

"因為這個系統上已停用指令碼執行,所以無法載入 XXXX\funcc.ps1 檔案。
  如需詳細資訊,請參閱 about_Execution_Policies,網址為 https:/go.microsoft.com/fwlink/?LinkID=135170。
"

幸好隨便 Google 就找到了解答
(錯誤訊息中的官方連結也是有解答, 但是找到的這篇比較直接)
用 administrator 權限執行 PowerShell 並執行
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
然後再執行一次 func 應該就可以看到 Azure Functions Core Tools 的執行畫面了

看到斗大的 Azure Functions Icon 就是成功啦

建立第一個使用 HTTP trigger 的 Azure Function


終於開始要寫程式啦
再次稱讚文件介紹的非常清楚
工具也是相當的好用
點幾下選一選就建立好了一個 HTTP trigger 的 Azure Function
啟用 debugger 也沒甚麼問題
第一個範例是你可以透過 GET 在 URL 裡傳一個 name 參數
Azure Function 則是把你傳入的名字字串回傳
文件也有附上用 curl 執行 POST 的指令
但是我使用 PowerShell 指令又遇到了問題
最後在多方搜尋嘗試下
修改成下面這段
$body = @{"name"="Visual Studio Code2"} | convertto-json
curl http://localhost:7071/api/HttpExample `
 -Headers @{"Content-Type"="application/json"} `
 -Method Post -Body $body
這樣就測試到 GET 與 POST 兩種方法了

但是在執行 debugger 練習測試的時候
一直有一個很惱人的問題
就是每當要停止 debug 的時候
整個 vscode 就會 hang 在那邊, 只能重開
幸好也有人反應這個問題
解決方式就是在 vscode 裡面把 azureFunctions.stopFuncTaskPostDebug 關掉
但是這會造成離開 debugger 後
本機上的 Azure Functions 仍會繼續執行 (等於是你只是 detach 該 process 而以)

發佈你的第一個 Azure Functions


也還是跟著文件做就好
唯一不同的是, 我會建議選擇 "Create New Function App in Azure... Advanced"
因為這個選項才能選擇 hosting plan 為 consumption plan
發佈好了就能直接用公開的 URL 來觸發你的 Azure Function

有三種計價方式

文件後面還有教怎麼使用 queue 來觸發 Azure Functions
基本上照著做都沒甚麼問題
做完這些就等於學會了怎麼用 vscode 來開發並佈署簡單的 Azure Functions 了

2020/01/13

[自己的 Hackathon] L10 盃黑客松之即時開票系統 - 過程 (不含技術介紹)

決定好了題目後
接下來就是要決定實作的系統樣貌跟需要用到的技術
好幾年前 (大約是林傑學長還在做微軟技術傳教士時)
我有用過 Azure Functions 做過一個整合 slack 與 trello 的 bot
當時是直接在 Azure 提供的網頁上寫 python 來達成
這次也是想利用 serverless 的技術試試看
所以設計了一個簡單的架構


流程簡圖

Crawler 跟 Update Handler 就用兩個 Azure Functions 實作
Queue 的部分使用 Azure Queue Storage

先來重新學習現代化的開發方式


這次想順便試試看目前使用 IDE 開發 Azure Functions 能做到多方便
所以先以 "使用 Visual Studio Code 在 Python 中建立和部署無伺服器 Azure Functions"
這篇微軟官方的教學文件開始
星期五晚上把小鬼們都弄去睡覺之後
就開始依著這篇文件一步步的學習
文件還算詳細
雖然還是有遇到一點點小問題
不過都能透過 Google 大神找到解決方法
走過整篇文件後
就可以學會幾件事情

1. 如何在本機上開發 Azure Functions 且啟動 local debugger
2. 如何建立 HTTP 觸發的 Azure Functions 並測試
3. 如何把資料用 Azure Functions 送到 Azure Queue Storage 中
4. 如何部屬本機開發好的 Azure Functions 到雲端上

我做完整個教學後就去睡覺了
準備明天早上起床後再新建立一個專案來做真正的系統

把系統雛型產生出來


一早八點起床
老婆出門上班而小鬼們還在睡
趁機加緊腳步把 Crawler, Update Handler 以及 Azure Queue Storage 都先建好
定義好要塞進 queue 的 JSON 樣子後
先把組合出 JSON 後塞入 queue
以及被 queue 觸發後取得 JSON 的部分做好

當然中間少不了排解小鬼們的紛爭
還有被女兒甩了門後進行諄諄教誨的時間
接著開始準備兩位少爺小姐的午餐
等到老婆回家後我趁隙出門投票
直到他們開始午睡
我再繼續推進我的進度

最後在投票截止前約半小時把基本的功能完成
Update Handler 可以依 JSON 的內容, 把票數填寫到對應的儲存格中
因為尚未開票所以沒有票數頁面可以爬
於是我用中選會網站的 "候選人簡歷" 確認我會使用 package 寫出可行的 Crawler
接著就是守在電腦前等待得票數頁面出現

有人投票日一早開 vscode 的嗎?

最刺激的時刻總會出現問題


我重新整理中選會網站到約下午四點十七分
開始出現得票數頁面了
馬上著手修改 Crawler 的部分
結果竟然遇到無法印出爬到的內容 (因為我想先用 print 看看爬到的內容是否正確)
搜尋了半天還是沒找到解法
靈機一動想說先印出爬到表格的欄數與行數
這樣我可以知道哪一張才是得票數表格
果然有用, 馬上針對我想知道的候選人票數做處理 (對不起宋杯杯)
確定爬到總統得票數且能透過 Azure Queue Storage 觸發 Update Handler
也寫了正確數字到 spreadsheet 後信心大增
就開始把剩下的區域立委做完
同時使用本機的方式先確定可以一直爬到最新票數
終於在五點半時按下 Deploy 按鈕送上雲端

一開始先以每一分鐘觸發一次的頻率觀察一下
前幾分鐘好像一切正常
結果出現了票數回朔的現象
只好馬上來 debug
用著不熟悉的 Azure 介面看 Application Insights
赫然發現我的本機仍在執行 Azure Functions
所以才會有被修改兩次的現象
但是關掉本機之後又發現不會取得最新的票數
Azure 雲端上的 Crawler 似乎會一直拿到一樣的內容
查了半天查不出結果
幸好透過 Application Insights 了解到原來在本機執行也可以
只好在出門前把雲端上的都關掉
開啟本機上的開發環境讓他自己跑了
萬幸的是在外面用手機看 spreadsheet
有一直在更新票數
才鬆了一口氣

最後收尾的 spreadsheet 排名


回到家後幫孩子們沐浴更衣完後
把最後 spreadsheet 上的結果表格
依照之前討論的勝利方式
利用條件式格式設定功能
將猜測最準的人 highlight 出來
這裡也學到了幾個 spreadsheet 公式
整個只有自己的 hackathon 就這樣告一段落
收穫真的很多

複雜的 spreadsheet 公式

2020/01/12

[自己的 Hackathon] L10 盃黑客松之即時開票系統 - 前言

這次 2020 總統立委選舉很激烈
我的某個朋友群中也開啟了一些對於選舉結果的猜測
起初是我很雞婆的幫忙整理了每個人的預測
一直到星期五的時候突發奇想
如果用 Google Sheet 來顯示結果以及大家預測的準不準
好像蠻有意思的
所以我花了點時間做了一個 spreadsheet
(已經去識別化, 請相關人士不要擔心)

spreadsheet 的樣子

簡單說明一下表格上的資訊
朋友們討論後決定一起來預測三個題目
1. 關注的區域立委當選與否
2. 兩大黨不分區的席次
3. 兩大黨總統的票數差
可以看到這些人關注的選區有五個

表的左邊我是安排放票數跟席次的資訊
右邊的上半部是放個別的預測
下面做了一個結果的表格
計算出各題大家的預測結果
1. 猜中的選區數 (多者得勝)
2. 猜測席次與實際席次的差, 少一席跟多一席為一樣的計分
    (這題相對複雜, 最後大家的共識是 Max( |∆KMT|, |∆DPP| ) 最小者勝
    請原諒我用類似數學的式子表示, 中文太差了 Orz)
3. 猜測票數差與實際票數差的落差 (絕對值小者得勝)

做好了後分享給朋友們
大家覺得蠻不錯的很好懂

一開始是跟大家說 "你們就自己更新票數吧"
後來下班開車回家的路上就在想

如果能夠爬中選會的開票結果網站
然後即時的填入票數到 spreadsheet 中
好像更有臨場感了

於是這小小個人黑克松就在我的內心裡慢慢萌芽