从 Wordpress 到 Hexo 进行数据迁移

tech

This article was last updated on <span id="expire-date"></span> days ago, the information described in the article may be outdated.

之前博客部署在阿里云的轻量服务器上,因为不想在国内备案的原因,选择了香港地区的服务器,在外面套了一层 Cloudflare 的 CDN,这样可以给站点加上小绿锁,也不用自己担心证书的问题。

之前的博客访问起来速度只能说一般般,毕竟离开大陆的服务器少有访问速度快的,Cloudflare 的 Anycast 在国内经常被绕到美国,这也是没办法的,国内的网络环境太复杂了。

但这些都不是迁移博客的原因,前一段时间在更新了 Wordpress 之后,貌似它们又搞了一个什么新的编辑器?在那之前我还可以把在本地写好的 Markdown 进行少量修改的复制过去发布文章,这次之后就完全不能了;而且对 LaTex 的支持还必须启用插件。这对只想安安静静写两篇帖子的我来说简直是太难过了 —— 折腾来折腾去,过一段时间还得升级,升级之后你也不知道会发生什么。

那就迁移吧,Hexo 是个不错的选择;毕竟折腾一次,以后轻松。

迁移牵扯到几个问题:

  1. WordPress 是动态的博客程序,我将它部署在VPS上;但是对于 Hexo 这种静态的博客程序来说,一个 VPS 来说就太浪费了;经过查询之后,我选择了 Vercel —— 它支持免费的静态页面部署,并且大陆的访问速度也还不错。

  2. 博客内包含了大量的图片,如果将这些直接部署在静态内容托管平台上,在进行 Markdown 写作的时候会有不少的麻烦,因此我们需要一个自建的图床。

  3. 之前 WordPress 的文章需要迁移至 Hexo,尽管 Hexo 有插件从 WordPress 导出的 XML 可以生成博客文章,但是这些恢复的文章经过实测,有着严重的格式问题;

    并且更重要的,这样生成的文章没有办法从旧站点生成 301 跳转,这对于依赖搜索引擎结果的访问者们来说,无异于重新建站。

  4. 博客我还是希望启用评论的,但是对于 Hexo 这样的静态博客程序,需要动态处理的评论程序往往是一个难题;另外,之前的评论我也不想直接丢掉,这也是我 7 年博客记忆的一部分。

这几个问题我们来一步步解决。

部署 Hexo 到 Vercel 上

这可能是最简单的一步,网上有大量部署 Hexo 的文章;部署到 Vercel 的步骤几乎差不多:

  1. 首先在本地部署一个 Hexo,这个如果不会可以参考 Hexo 官网的部署教程

  2. 之后在你的 GitHub 上新建一个 repository 可以是 Private 的,也可以是 Public 的;我这里选择了 Private 的,因为这里面会有很多隐私信息

  3. 将你的本地 Hexo 程序 Push 到刚才的 Repos 上,但是一定注意

    • 排除 node_modules 文件夹,这个文件夹是本地的 Hexo 程序,没有必要推送给托管方,并且这个文件夹的存在会导致 Vercel 的部署出现问题
    • 排除 Public 文件夹,这个是你在本地使用 hexo generate 生成的静态文件,没有必要上传
    • 如果你有使用 git clone 从线上克隆主题程序,请一定删除 themes 对应文件夹下的 Git 仓库信息,否则会导致你的本地项目无法正常 Push 到 GitHub 上
  4. 之后在 Vercel 上创建一个账户,使用 New Project 新建项目,并按照指导授权你的 GitHub 信息访问,并选择你刚刚创建的博客 Repos 给 Vercel 访问

  5. 之后在 settings 部分更改项目的 build / server 命令如下:

  6. 之后完成 Import,如果没有问题,Vercel 就会开始对项目进行部署并分配给你一个他们的二级域名,你也可以在 settings - domain 部分绑定自己的域名;

    我使用 Cloudflare 只需要在 Console 里面修改 DNS A 记录到 Vercel 的地址即可

完成了上面的步骤之后,你的 Hexo 博客就部署在了 Vercel 上,并且在你本地更新完成之后,只需要将改动 Push 到你的 GitHub 仓库,Vercel 类似 Travis CI 的 Action 就会自动执行,更新部署你的博客。

在绑定了自己的域名之后你就可以看到自己的博客正常运行了。

使用阿里云 OSS 与 CDN 自建一个图床

这里我使用了阿里云 OSS 与 CDN 自建图床,这样可以最小化成本,使用 OSS 一年的成本也就 9 元,40GB 的存储空间我相信足够大部分博客使用了;

为了最小化流量成本,我们在外面再套一层 CDN,这样按照阿里云的计费方式,我们每小时几 MB 的流量是不会收费的。

使用 OSS 和 CDN 的教程太多了,如果你不会的话更可以联系阿里云的售前支持,就说你是企业用户,我相信他们会很高兴给你解答的。

但是一样的,我需要提醒几个点:

  1. OSS 需要设置防盗链的 Referer,但是不要相信阿里云的鬼话:

    他们的防盗链对通配符支持很差!

    他们的防盗链对通配符支持很差!

    他们的防盗链对通配符支持很差!

    重要的事情说三遍,例如:

    你想允许 Referer 为 https://init.blog 的请求通过,如果编写规则为 https://*init.blog 那么恭喜你,你会得到一大堆的 403 错误。

    并且在设置 Referer 时记得需要完全的匹配:协议、端口号、域名

  2. 因为我们的图床域名往往与博客主域名不同,那么在 CDN 的设置里面需要解决 CORS 问题;

    我们可以在 CDN 设置里面设置 HTTP 的指定 Headers,类似如下设置即可:

  3. 之后我们可以开启压缩之类的优化,进一步节省流量

在这之后我们的自建图床应该就可以使用了,因为是图床,我使用 PicGo 管理上传;如果在前面的防盗链里面我们没有允许空 Referer,那么可能上传之后在本地查看会出现裂图的情况,但这并不影响我们在博客部署后的正常使用。

旧文章迁移与跳转

首先,我想告诉大家的是,旧文章迁移只能够手动进行,至少我没有发现有什么好的方法。

好在我们可以直接从原站 Copy HTML 的内容,像是 Typora 这样的 Markdown 对粘贴内容的支持很好。

下来就是需要从旧站点设置文章跳转,我这里的方法仅限于原站使用 postid 的方式

  1. 首先在迁移文章时,根据 Hexo-Migrate 插件导出的原文章 id,修改文件名为这个 id,这样在 generate 之后我们就有了如下的 URL:

    • /postid

      对比之前文章的 URL:

    • /archives/postid

      我相信大家已经有了思路了

  2. 在进行如上的文章迁移之后,我们有两种方式进行原文章 URL 跳转:

    1. 使用 Cloudflare 的朋友可以使用内建的 Page Rules 进行规则跳转:

      我们这里选择 301 进行永久跳转告诉搜索引擎我们已经废弃了原来的地址,部署规则之后就可以实现跳转了。

      但是需要注意的是,要使用 Page Rules 必须要使得流量经过 Cloudflare,如果部署的静态托管有国内或者香港之类更快的节点,这样就会对我们的博客速度造成很大影响。

      所以我选择了仅 DNS 的服务,并使用下面的方法解决跳转问题。

    2. 如果使用的静态托管是 Vercel,我们可以使用 vercel.json 进行跳转,它类似 Apache Server 的 rewrite 功能,我们在站点根目录新建一个 vercel.json,同样的有两种方法进行设置:

      • 使用 redirect 进行,它的配置式类似:

        1
        2
        3
        4
        5
        6
        {
        "redirects": [
        { "source": "/user/:id", "destination": "/api/user", "permanent": true },
        { "source": "/proxy/:match*", "destination": "https://example.com/:match*" }
        ]
        }
      • 使用 routes 进行,我使用的配置式如下:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        {
        "routes": [
        {
        "handle": "filesystem"
        },
        {
        "src": "/archives/(.*)",
        "status": 301,
        "headers": {
        "Location": "/$1"
        }
        }
        ]
        }

        这样就可以实现跳转,但是需要注意两个点:

      • 如果启用了 redirects 或者其他的高级功能,那么不能使用 routes 功能,这是因为 routes 属于较为低等的功能。

      • routes 的多个功能需要仔细调整顺序,因为他们的执行顺序是按照你的编写顺序来执行的,不正确的执行顺序会导致错误的跳转问题。

        如果需要正确的配置式,那么需要将更广泛的配置式放在后面,我使用的整体配置式实现了:

        • 两个管理页面的 Redirect & Rewrite

        • 404 页面自定义 - 在 404/index.html 使用 hexo pages 404 生成

        • CORS 问题解决

          整体的配置式如下:

          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
          {
          "routes": [
          {
          "handle": "filesystem"
          },
          {
          "src": "/archives/(.*)",
          "status": 301,
          "headers": {
          "Location": "/$1"
          }
          },
          {
          "src": "/xxxx",
          "dest": "/xxxxx"
          },
          {
          "src": "/xxx",
          "status": 302,
          "headers": {
          "Location": "xxx"
          }
          },
          {
          "src": "/(.*)",
          "status": 404,
          "dest": "/404/index.html"
          },
          {
          "src": "/(.*)",
          "headers": {
          "Access-Control-Allow-Credentials": "true",
          "Access-Control-Allow-Origin": "*",
          "Access-Control-Allow-Methods": "GET,OPTIONS,PATCH,DELETE,POST,PUT",
          "Access-Control-Allow-Headers": "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version"
          }
          }
          ]
          }

          管理页面的具体信息隐去了,大家可以自由更改。

        我更推荐第二种方式,因为这样更方便我们管理,并且可以自定义 404 页面之类的。

如果源站点使用了其他的 Permanent-link 格式,可能需要修改上面的方法。

建议大家部署完成之后先测试一下在绑定自己的域名,有什么问题也方便继续调整。

评论启用与旧数据迁移

才开始我使用 Disqus 启用评论支持,但是后来发现 Disqus 使用 JS 劫持站内的外部跳转链接到一个 Redirect 域名:

1
redirect.viglink.com

这就意味着你站内的外部链接被全部劫持了,这就很恶心了,我在 Disqus 的后台也没有找到如何关闭它,所以放弃。

之后发现了一个不错的自托管评论程序:Valine

它基于 Node.js 开发,基于 LeanCloud 的存储功能实现了无后端的评论支持,具体的使用方法大家可以去 Valine 的官网进行查看,有很多支持它的主题,一般来说我们只需要配置 API Key 即可。

需要注意的是,在申请 LeanCloud 时一定要使用国际版,因为国内版:

  • 不支持二级域名分配
  • 绑定国内域名需要备案

这会给我们带来很多不必要的麻烦。

但是对于评论的管理与迁移,Valine 就没有办法帮到我们了,因为这是一个无后端的系统,我们如果需要管理评论,可以使用这个项目:Valine-Admin

将它部署在与 Valine 同一个 Web 实例上,具体的部署方法按照上面的 README.md 说明即可,在这之前可能需要申请自己邮箱的 SMTP 服务,具体的步骤就不详述了,我这里使用的是 QQ 企业邮箱,在 SMTP_SERVICE 使用 exQQ 即可。

如果需要使用管理功能的 Akismet 垃圾评论审查,那么去申请一个免费的 API Key 填入即可。

下面就是原站评论数据的迁移了,我想到的这个方法只能够恢复所有的评论数据,而对于评论的层级关系(即回复关系)则进行了舍弃,具体的操作方法倒也不复杂:

  1. 因为 WordPress 没有办法直接导出评论数据,我们需要一个插件:WordPress Comments Import&Export

    安装之后在导出数据处对 Columns 进行如下别名设置并导出

  2. 导出之后的应该是一个 CSV 文件,我使用了 Python 的以下库进行数据处理:

    • csv 库,使用其中的 DictReader 类读取数据

    • datetime 库,处理 insertedAt 列的数据进行设置,其中的 strptime/strftimeformat 设置为:

      1
      2
      "%Y/%m/%d %H:%M"
      "%Y-%m-%dT%H:%M:%S:fZ"
    • json 库,导出文件

      任务很简单,代码量也不多,大家可以使用自己喜欢的语言进行数据处理,处理之后的数据结构大概如下:

  3. 之后将导出的 Json 文件导入到 LeanCloud 上评论的 Web 实例即可,注意导入的时候选择合适的 Class - 在这里我们选择 Comment 类即可:

导入之后如果没有问题就可以在 Valine Admin 的后台看到自己之前站点的评论了。

现在 LeanCloud 对免费版的自唤醒机制进行了限流,可能会有部分的时候评论邮件难以发送出去,我们有空可以自己看看后台,手动管理一下。

总结

现代的互联网服务越来越 “分布” 了,想想在整个部署过程中用到了哪些服务?

  • Namesilo - 域名注册
  • Cloudflare - DNS 管理
  • 阿里云 OSS 与 CDN - 图床搭建
  • LeanCloud - 评论存储与管理
  • Tencent 企业邮箱 - SMTP 邮局
  • GitHub - 代码托管
  • Vercel - 静态内容部署与托管

这样的方式相比古老的一台 VPS 管全部更加复杂,但也使得我们的服务更难在出现单点故障时全部瘫痪。

这里面最重要的是 Vercel,但是如果它出现问题,我们可以迅速的回退使用 GitHub Pages / Netlify 同类的静态内容托管服务。

迁移之后我放弃了部分(其实有很大部分)的古老文章,它们大部分都是在2017年之前的,放弃他们有两个原因:

  1. 它们过于古老了,我更希望把他们全部 Down 下偶尔自己看看小时候的自己
  2. 它们能提供的有用信息有限

希望以后我能有动力和时间继续更新博客,也感谢大家的关注与支持。

Author: 桂小方

Permalink: https://init.blog/migrate-wp-hexo-vercel/

文章许可协议:

如果你觉得文章对你有帮助,可以 支持我

Comments