自動備份 Medium 文章到 Github Pages (Jekyll) 的那些事
個人 Medium 文章備份鏡像站搭建、維護、升級、客製化的一些紀錄
自動備份 Medium 文章到 Github Pages (Jekyll) 的那些事
個人 Medium 文章備份鏡像站搭建、維護、升級、客製化的一些紀錄
前言
經營 Medium 來到了第 6 年,文章總數在去年突破 100 篇;隨著經營時間越長、文章越多,越怕哪天 Medium 突然關閉或是帳號異常造成所有文章心血付之一炬,有的文章含金量不高道無妨,但更多的是記錄技術架構跟當時的解題思維,我時常也會回來看之前寫的文章,重新複習知識;另外後面幾年也開始記錄出國旅遊遊記,都是回憶並且流量表現不錯;這些內容一但遺失就不可能再重新撰寫了。
自行開發備份工具
我習慣都是直接在 Medium 平台上撰寫文章,沒有自己的備份,因此在 2022 年過年期間花時間開發了一個 Medium 文章下載&轉換成 Markdown 文件(包含文章圖片、文章內嵌的程式碼…等內容) 的工具 — ZMediumToMarkdown :
並延伸使用此工具將下載下來的 Markdown 使用 Jekyll (Chirpy Theme) 做為靜態備份鏡像網站部署在 Github Pages 上 — https://zhgchg.li/
那時候把這整套整合成一個 Github Template Repo 給有同樣需求的朋友可以快速部署使用 — ZMediumToJekyll ,在此之後(2022),我就沒有再更新過 Jekyll (Chirpy Theme) 的版本跟設定了; ZMediumToMarkdown 持續有在維護,偶爾會發現格式解析錯誤就會立刻修正,目前趨於穩定。
那時候使用的 Jekyll (Chirpy Theme) 版本是 v5.x 沒有太大的問題,該有的功能也都有(e.g. 置頂、分類、標籤、封面圖、留言…);只有在畫面滾動時很常會出現無法滾動問題,但是在滑個幾下又正常,一個操作體驗缺憾,曾經嘗試升級到 v6.x 還是有、回報給官方也沒得到回應;再加上隨著版本提升升級會遇到的衝突就越多,因此後來完全放棄升級這個念頭。
近期才下定決心要解決 Jekyll (Chirpy Theme) 問題、升級版本、順手重新優化快速部署工具 ZMediumToJekyll 。
New! medium-to-jekyll-starter 🎉🎉
medium-to-jekyll-starter.github.io
我將 Jekyll (Chirpy Theme) 最新版 v7.x 加上我的 ZMediumToMarkdown Medium 文章下載轉換工具重新整合成新的 — medium-to-jekyll-starter.github.io Github Template Repo。
大家可以直接使用這個範本 Repo 快速設定搭建自己的 Medium 鏡像內容備份網站, 一次設定永久持續自動備份、部署在 Github Pages 上完全免費 。
手把手設定教學請參考此篇文章: https://zhgchg.li/posts/zh-TW-medium-to-jekyll/
成果
*上面的所有文章都是 自動 從我的 Medium 下載所有內容&轉換成 Markdown 格式&重新上傳。
附上隨便一篇文章的轉換成果作為比較範例:
升級後沒再出現滾動卡住的問題了,藉由這次升級也多加上了客製化動態內容 (顯示 Medium 追蹤人數)。
一些技術紀錄
Jekyll (Chirpy Theme) 在 Github Pages 上的部署設定方式主要是直接參考官方 Start Repo:
上個月也參考這個專案的方式,做了一個新的開源專案 — Linkyee 開源版的 Link Tree 個人連結頁面。
Jekyll 客製化方式 (1) — Override HTML
Jekyll 是一套很強大的 Ruby 靜態內容網站生成引擎, Jekyll (Chirpy Theme) 只是一套基於 Jekyll 的主題,比較過其他主題還是 Chirpy Theme 最有質感跟操作體驗優異、功能俱全。
Jekyll 的頁面具有繼承性,我們可以在 ./_layouts
新增 與 Jekyll 相同的頁面檔案名 ,引擎在產生網站內容時就會使用你自訂的頁面內容取代掉原本的。
例如我希望在每個文章頁末尾加上一行文字,我先把原本的文章頁面檔案( post.html )複製出來,放到 ./_layouts
目錄下:
使用編輯器打開 post.html 在相應的位置加上文字或客製化,重新部署網站就能看到客製化結果。
也可以建立一個 ./_include
目錄,放一些想要共用的頁面內容檔案:
然後再 post.html
中我們就可以直接使用 {% include buymeacoffee.html %}
引入剛檔案的 HTML 內容重複使用。
複寫 HTML Layout 檔案的優點是 100% 客製化,頁面內容、排版要怎麼呈現都可以隨意調整;缺點是這次在升級的過程就會遇到衝突或是預期外結果,要自己重新檢視一次客製化的內容。
Jekyll 客製化方式 (2) — Plugin
第二種方式是使用 Plugin 中的 Hook 方法,在 Jekyll 產生靜態內容階段注入自己想要的客製化內容。
Built-in Hook Owners and Events
Hook 事件 有很多,這邊只附上我用到的 site:pre_render
跟 post:pre_render
新增方式也很簡單,只要在 ./_plugins
新增一個 Ruby 檔案即可。
posts-lastmod-hook.rb 是原本就有的 Plugin
我想要幾個「偽」動態內容功能,第一個是在個人資料下顯示 Medium 追蹤人數還有在頁底顯示頁面內容最後更新時間。
在 ./_plugins
下建立了一個 zhgchgli-customize.rb
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#!/usr/bin/env ruby
#
require 'net/http'
require 'nokogiri'
require 'uri'
require 'date'
def load_medium_followers(url, limit = 10)
return 0 if limit.zero?
uri = URI(url)
response = Net::HTTP.get_response(uri)
case response
when Net::HTTPSuccess then
document = Nokogiri::HTML(response.body)
follower_count_element = document.at('span.pw-follower-count > a')
follower_count = follower_count_element&.text&.split(' ')&.first
return follower_count || 0
when Net::HTTPRedirection then
location = response['location']
return load_medium_followers(location, limit - 1)
else
return 0
end
end
$medium_url = "https://medium.com/@zhgchgli"
# could also define in _config.yml and retrieve in Jekyll::Hooks.register :site, :pre_render do |site| site.config
$medium_followers = load_medium_followers($medium_url)
$medium_followers = 1000 if $medium_followers == 0
$medium_followers = $medium_followers.to_s.reverse.scan(/\d{1,3}/).join(',').reverse
Jekyll::Hooks.register :site, :pre_render do |site|
tagline = site.config['tagline']
followMe = <<-HTML
<a href="#{$medium_url}" target="_blank" style="display: block;text-align: center;font-style: normal;/* text-decoration: underline; */font-size: 1.2em;color: var(--heading-color);">#{$medium_followers}+ Followers on Medium</a>
HTML
site.config['tagline'] = "#{followMe}";
site.config['tagline'] += tagline;
meta_data = site.data.dig('locales', 'en', 'meta');
# only impletation in en, could impletation to all langs.
if meta_data
gmt_plus_8 = Time.now.getlocal("+08:00")
formatted_time = gmt_plus_8.strftime("%Y-%m-%d %H:%M:%S")
site.data['locales']['en']['meta'] += "<br/>Last updated: #{formatted_time} +08:00"
end
end
- 原理是註冊一個 Hook 在網站 Render 前,對 config 中的
tagline
個人資料下方介紹內容區塊,多塞上 Medium 追蹤人數顯示 HTML。 - Medium 追蹤人數會在每次執行都去爬取拿到最新數字
- 頁底最後更新時間邏輯也差不多,就是對 locales->en->meta 在產生網站時多塞上最後更新時間字串
- 補充如果是 Hook 文章產生前,可以拿到 Markdown、Hook 文章產生後,可以拿到產生後的 HTML
儲存後可以先在本機下 bundle exec jekyll s
測試結果:
用瀏覽器打開 127.0.0.1:4000
查看結果。
最後在 Github Pages Repo 上的 Actions 加上排程定時自動重新產生網站,就完成了:
在 Jekyll (Chirpy Theme) Repo 專案中的 Actions 找到「 pages-deploy.yml
」在 on:
新增:
1
2
schedule:
- cron: "10 1 * * *" # 每天 UTC 01:10 自動執行一次, https://crontab.guru
Plugin 的優點是可以達到動態內容效果(排程更新內容)、不影響網站架構不會在升級時遇到衝突;缺點就是能調整的內容、顯示位置有局限。
Jekyll (Chirpy Theme) v7.x 後的 Github Pages 部署問題
除了網站架構的調整外,v.7.x 的部署腳本也有改變;移除了原本的 deploy.sh 部署腳本,直接使用 Github Actions 的部署步驟:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# build:
# ...
- name: Upload site artifact
uses: actions/upload-pages-artifact@v3
with:
path: "_site${{ steps.pages.outputs.base_path }}"
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
但是我在部署的過程遇到了問題:
Uploaded artifact size of 1737778940 bytes exceeds the allowed size of 1 GB
因為我的網站內容太大了,導致 Upload Artifact 失敗;但是之前的部署腳本是可以的,所以只好退回去用原本的 deploy.sh + 註解掉上面這一段 。
Github Pages 部署時 Test Site 步驟一直不通過
Jekyll (Chirpy Theme) 部署有一個步驟是 Test Site 自檢測網頁內容是否正確,例如連結是否正常、HTML 標籤是否有缺漏…等等
1
2
3
4
5
6
7
8
9
# build:
# ...
- name: Test site
run: |
bundle exec htmlproofer _site \
\-\-disable-external \
\-\-no-enforce-https \
\-\-ignore-empty-alt \
\-\-ignore-urls "/^http:\/\/127.0.0.1/,/^http:\/\/0.0.0.0/,/^http:\/\/localhost/"
我自己多加了 --no-enforce-https
--ignore-empty-alt
忽略 https、html tag沒有 alt 的檢查, 忽略這兩條讓檢查通過(因為暫時無法去改內容) 。
htmlproofer 的 CLI 指令官方文件沒有提,翻了好久才在某個 Issue 的 Comment 找到規則:
https://github.com/gjtorikian/html-proofer/issues/727#issuecomment-1334430268
其他文章補充
有任何問題及指教歡迎 與我聯絡 。
===
本文首次發表於 Medium ➡️ 前往查看