文章

自动备份 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/>{: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/>{:target="_blank"}

https://zhgchg.li/

上面的所有文章都是 **自动 从我的 Medium 下载所有内容&转换成 Markdown 格式&重新上传。*

附上随便一篇文章的转换成果作为比较范例:

Medium 上的原始内容 / 转换后在个人网站的结果

升级后没再出现滚动卡住的问题了,借由这次升级也多加上了客制化动态内容 (显示 Medium 追踪人数)。

一些技术纪录

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

上个月也参考这个专案的方式,做了一个新的开源专案 — Linkyee 开源版的 Link Tree 个人连结页面。

<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>{: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 授权。