Zerojudge stats

動機

繼上篇如何打造好看的Github Profile,就是看到許多大佬的GH首頁都很炫炮的統計紀錄!

又翻了翻GH,發現各大解題平台如:leetcodecodeforce(但我覺得沒有到很好看)、TIOJ等都有人做過專門放在GH首頁好看的解題統計了

唯獨我刷最多水題的Zerojudge沒有

那只好自己做一個了(⊙ω⊙)

如何辦到的?

但是仔細看一下發現:Profile page竟然是一個README.md,那他們是怎麼在靜態的頁面顯示即時的資料的?

接著看一下他們的raw markdown,可以看到那些統計紀錄都是一個個圖片,而圖片的src則是連到某個網站的url

如:這個可以顯示在Github活動狀態的project

他的使用方法:

![GitHub Streak](https://github-readme-streak-stats.herokuapp.com/?user=DenverCoder1&theme=dark)

大概揣摩一下,應該是:

當瀏覽者訪問作者頁面時,將上面的url當作圖片source,並且透過GET parameter來查詢用戶資料或指定主題。而他的server在收到request後,就向Github查詢用戶資料再包成一個SVG顯示在頁面上,並且頁面的content-type要指定為SVG(這樣才能當成圖檔來用)。

為什麼Zerojudge沒有人做?

我猜想應該是:

  1. Zerojudge需要登入才能查詢用戶資料 如:leetcode、TIOJ不需要登入,而codeforce則是提供API查詢
  2. Zerojudge的登入方式 目前ZJ的登入方式有兩種:
    • 通過recaptcha 2驗證(防機器人ㄉ那個驗證)
    • 登入google帳戶
  3. 大佬都不想寫Zerojudge(畢竟題目太雜了)

而要讓server登入Zerojudge並讓某一個帳號持續維持登入狀態是最大的困難點

整體架構概念

所以我要先搭建一個app能夠即時查詢Zerojudge的資料,並將統計資料包成SVG回傳,還有要能夠方便地登入Zerojudge維持登入狀態

又因為比起PHP(我還沒學過)、Javascript(我沒有學過他的後端,也比較不熟悉它的語法)、Ruby(也沒學過)等我比較熟悉Python

所以就用flask來寫這個專案的後端

登入Zerojudge

通過recaptcha驗證

目前要破解recaptcha最有效率的方式是透過音檔辨識(畢竟圖像辨識的技術沒有到音檔辨識那麼精準?而且還需要額外訓練一個模型才有辦法)

音檔辨識的python library有SpeechRecognition(它可以接到Google Cloud Speech API,算是以其人之道,還治其人之身)

登入google帳號

要透過python登入google帳號,我有試過beautifulsoup 4但是一直會有bug..

另一種方式是透過selenium + webdriver(可以算是透過程式操控一個瀏覽器)但是也很容易被擋

嗯…

這兩個方式都會需要將webdriver塞到server裡面,登入方式算是透過程式操控一個瀏覽器然後在指定的位置按按鈕、送出張號密碼來登入

我覺的這個這兩種方法都笨笨ㄉ而且效率也不高,但是在構思階段也沒有想到什麼好方法

靈光一閃

突然想到:當一個帳號登入到Zerojudge時,會產生一個JSESSIONID並且發出request時只需要改變headerJSESSIONID就能以該登入帳號的身份進入網站。

那代表:我只要拿一個帳號作為專門向Zerojudge查詢使用者資料的帳號,並且我可以在自己的電腦登入該查詢帳號再將JSESSIONID傳到我的app中,就可以讓我的server以登入帳號的身份發出request!

實做

工具:

  • Flask、Flask login (後端server)

  • beautiful soup 4 (解析向ZJ的request,擷取關鍵的解題數)

  • 手刻SVG(那時候沒有找到什麼很好用的SVG online editor)

  • boostwrap 5(因為手刻cssjs並沒有比較好看…)

為什麼不用Node.jsPHPrust

因為我對python比較熟w

必要功能:

  • 登入界面 (for Admin user)

  • Admin 界面 (讓我更改JSSESSIONID)

  • GET 回應點 (透過query parameter傳用戶id、主題…然後給好看的badge回來)

困難:

大致的架構都寫好後,在最關鍵的將圖片顯示在瀏覽者頁面有問題

我先將SVG的架構寫成模板,只要將用戶的name、解題數套入就可以生成新的圖片

然而要將GET回應點顯示的SVGcontent-type改成image相關的才有辦法讓瀏覽器以該url的回應點當成圖片的source

(我那時候不知道,又因為render_templates()的預設content-type都是html,所以我在localhost試了幾百遍還是沒有辦法將看的到的圖片當成圖檔來用…)

我看了Flask的document還是沒有找到解法(其實是技術知識不夠,那時候我還不知道mime type的相關知識,所以將content-type改成image就可以解決了,但是我大概卡了兩天…

最後的解決方法:

只好厚臉皮的去Stackoverflow問問題

https://stackoverflow.com/questions/71529064/is-there-any-solution-that-can-show-svg-without-html-tags-using-flask

部署app:

Heroku

免費的真香!

Heroku就是最簡單可以部屬app的serverless雲端服務,它提供每個月500小時的免費時數(但是大概算一下就發現30×24=720 > 500 ,它提供的免費時數不足夠讓你的server一整天都是開機的狀態)

而且他的主機在美國,也就是在瀏覽者頁面顯示前,必須在台灣與美國經過四次來回!(Zerojudge的主機應該也在台灣吧?

清況如下:

我從台灣美國

我從台灣美國

我從台灣美國

我從台灣美國

大概這種感覺


  1. 瀏覽者(Taiwan)→(瀏覽器加載圖片,GET我的app)→

  2. 我的app(USA)→(向ZJ查詢用戶解題統計)→

  3. ZJ(Taiwan)→(回應請求,回傳給app)→

  4. 我的app(USA)→(將資料包成SVG圖檔回喘給使用者)→

  5. 瀏覽者(Taiwan)在瀏覽器看到Zerojudge stats

這樣造成嚴重的延遲,很常就runtime error了(而且應該不適合設定CDN,畢竟是要查詢即時的資料,預先加載到附近網路交換點很像沒什麼用?

缺點:

  • 延遲

不用多說ㄌ

  • 程式穩地性

目前還是Beta版本,很常app就掛掉…還有許多細節可以調整!

  • 網頁暫存

跟同學測試後發現有這個問題,但也不知道要怎麼解決。

畢竟瀏覽器為了加載速度會將曾經訪問過的資料暫存起來,那該使用者可能看到的還是舊的圖檔…

改進&加強:

目前想到的解決方案如下:

  • Google Cloud Platform

可是在GCP弄一個server只為了,部屬類似API功能的app感覺有點浪費?

  • 自己架server

技術門檻高,還有供電、網路…要考慮

  • 在台灣有主機的cloud server

品質一定沒問題的只有GCP,其他太便宜的不敢用

  • Google app script

目前還在研究,主要透過JS來寫,但是這還是比較適合跟Google提供的app做互動。目前遇到的問題的向其他網站request的時候沒辦法改header中的Cookie

  • Worker

3/26更新,湊巧在查為什麼那麼多人的subdomain都是worker.dev看起來超帥?大概了解一下才知道:workercloudflare提供的serverless服務,但是完全支持的語言只有JavascriptTypescript(不過官方說也可以將pythonPHP…編譯成Javascript,細節還需要再研究一下)