文章

自動備份 Medium 文章到 Github Pages|Jekyll 靜態備份與部署全攻略

透過自製工具將 Medium 文章自動轉成 Markdown,並利用 Jekyll Chirpy Theme 部署到 Github Pages,解決文章遺失風險與滾動卡頓問題,實現免費且穩定的持續備份與客製化展示。

自動備份 Medium 文章到 Github Pages|Jekyll 靜態備份與部署全攻略

基於 SEO 考量,本文標題與描述經 AI 調整,原始版本請參考內文。

文章目錄


自動備份 Medium 文章到 Github Pages (Jekyll) 的那些事

個人 Medium 文章備份鏡像站搭建、維護、升級、客製化的一些紀錄

前言

經營 Medium 來到了第 6 年,文章總數在去年突破 100 篇;隨著經營時間越長、文章越多,越怕哪天 Medium 突然關閉或是帳號異常造成所有文章心血付之一炬,有的文章含金量不高道無妨,但更多的是記錄技術架構跟當時的解題思維,我時常也會回來看之前寫的文章,重新複習知識;另外後面幾年也開始記錄出國旅遊遊記,都是回憶並且流量表現不錯;這些內容一但遺失就不可能再重新撰寫了。

自行開發備份工具

我習慣都是直接在 Medium 平台上撰寫文章,沒有自己的備份,因此在 2022 年過年期間花時間開發了一個 Medium 文章下載&轉換成 Markdown 文件(包含文章圖片、文章內嵌的程式碼…等內容) 的工具 ZMediumToMarkdown

並延伸使用此工具將下載下來的 Markdown 使用 Jekyll (Chirpy Theme) 做為靜態備份鏡像網站部署在 Github Pages 上 — https://zhgchg.li/

[https://zhgchg.li/](https://zhgchg.li/){:target="_blank"}

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/medium-to-jekyll/

成果

[https://zhgchg.li/](https://zhgchg.li/){:target="_blank"}

https://zhgchg.li/

*上面的所有文章都是 自動 從我的 Medium 下載所有內容&轉換成 Markdown 格式&重新上傳。

附上隨便一篇文章的轉換成果作為比較範例:

Medium 上的原始內容 / 轉換後在個人網站的結果

升級後沒再出現滾動卡住的問題了,藉由這次升級也多加上了客製化動態內容 (顯示 Medium 追蹤人數)。

一些技術紀錄

Jekyll (Chirpy Theme) 在 Github Pages 上的部署設定方式主要是直接參考官方 Start Repo:

上個月也參考這個專案的方式,做了一個新的開源專案 — Linkyee 開源版的 Link Tree 個人連結頁面。

[https://link.zhgchg.li/](https://link.zhgchg.li/){:target="_blank"}

https://link.zhgchg.li/

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](https://jekyllrb.com/docs/plugins/hooks/#built-in-hook-owners-and-events){:target="_blank"}

Built-in Hook Owners and Events

Hook 事件 有很多,這邊只附上我用到的 site:pre_renderpost:pre_render

新增方式也很簡單,只要在 ./_plugins 新增一個 Ruby 檔案即可。

posts-lastmod-hook.rb 是原本就有的 Plugin

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](https://github.com/gjtorikian/html-proofer/issues/727#issuecomment-1334430268){:target="_blank"}

https://github.com/gjtorikian/html-proofer/issues/727#issuecomment-1334430268

其他文章補充

有任何問題及指教歡迎 與我聯絡


🍺 Buy me a beer on PayPal

👉👉👉 Follow Me On Medium! (1,053+ Followers) 👈👈👈

本文首次發表於 Medium (點此查看原始版本),由 ZMediumToMarkdown 提供自動轉換與同步技術。

Improve this page on Github.

本文由作者以 CC BY 4.0 授權。