音符·代码·脑洞

不想当程序员的歌手不是好指挥。

0%

Ghost博客迁移Hexo不完全教程

最近开始尝试通过把原本的Ghost博客迁移到Hexo。这是第一篇用Hexo写的文章(本地测试),目前体验还不错。
如果迁移顺利,这篇文章会一起放上服务器,作为纪念。

迁移计划

  1. 本地测试
    • 文章编辑测试
    • 从Ghost博客数据导入原有文章
    • 主题选择、调整
  2. 部署到服务器
    • 静态网页
    • 图床
    • 一些特殊页面(Minecraft地图等)

不完全迁移教程

从Ghost后台下载博客数据

Ghost博客的后台有一键导出的功能,会把所有的博客数据导出为一个巨大的单文件json。这个功能本来是用于Ghost不同版本升级时的数据备份,现在我们正好用这些数据来进行平台之间的迁移。
首先分析一下数据结构:

  • posts表:记录所有的文章(post)和页面(page)数据,其中,未发布的草稿也在此表中,通过不同的状态字段区分
  • tags表:记录所有的标签
  • posts_tags表:记录文章与标签的一对多关系
    其他的表格,如authors,posts_authors表,在个人博客不需要用户系统的背景下,其中的数据并不是特别重要,直接忽略即可。

Hexo博客系统的文件结构

Hexo博客有着独特的文件结构。
其中,每个页面(page)都对应着source文件夹下的独立文件夹,文件夹的名字即为网页的路径(对应Ghost数据中的slot字段)。
而每篇博文(post)则是在source/_posts/文件夹下的单独文件,文件名称就对应着网页路径(默认配置中,页面路径是日期+文件名,为了与Ghost的路由逻辑匹配,在配置文件中设置为直接对应文件名)。

1
permalink: :title/

数据迁移代码实现

遍历Ghost数据库中的posts表,对于每篇文章:

  • 判断是否为已发表文章,如果是草稿则直接跳过,不进行迁移
  • 判断文章类型,是post还是page
    • 如果是page,建立新的目录
    • 如果是post,则查询文章对应的标签,并建立相应的文件
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
const generatePost = function(data){
// 搜索tags
// console.log(data.title+data.id)
var tags = jsonQuery(`posts_tags[*post_id=${data.id}]`, {
data:db.db[0].data
})
var tagList = []

for(tag of tags.value){
// console.log(tag)
var tagName = jsonQuery(`tags[id=${tag.tag_id}].name`, {
data:db.db[0].data
})
// console.log(tagName)
tagList.push(tagName.value)
}

// 在post文件夹下新建文件
var fileContent = `---
title: ${data.title}
date: ${data.created_at}
date_update: ${data.updated_at}
date_publish: ${data.published_at}
description: ${data.custom_excerpt}
tags: ${tagList.toString()}
---
${data.html}
`
fs.writeFileSync(`source/_posts/${data.slug}.md`,fileContent)

}



const generatePage = function(data){
// 新建文章名字的文件夹
fs.mkdirSync('source/'+data.slug)
// 创建文件index.md
var fileContent = `---
title: ${data.title}
date: ${new Date(data.created_at)}
date_update: ${data.updated_at}
date_publish: ${data.published_at}
---
${data.html}
`
fs.writeFileSync('source/'+data.slug+'/index.md',fileContent)
}

// init 创建文件结构

try{
fs.rmdirSync('source',{
recursive:true
})
fs.mkdirSync('source')
fs.mkdirSync('source/_posts')
}catch(e){
console.log(e)
}


var posts = jsonQuery('posts', {
data:db.db[0].data
})

for(post of posts.value){
if(post.status =='published'){
if(post.type=='post'){
generatePost(post)
}
else if(post.type == 'page'){
generatePage(post)
}
else{
console.log("未能找到对应的文章转换类型"+post.title)
}
}
}

在这一步遇到了非常无语的问题。Ghost作为自带markdown编辑器,支持原生markdown的博客系统,居然不能直接方便地导出markdown格式的博客内容,而是记录了html格式的信息。还好在markdown中直接使用html也是能够识别渲染的,于是这里偷懒了一下,直接使用了Ghost导出的html数据。

处理内链失效的问题

转换后,大部分文章和页面都能够正常显示了,比想象中的顺利许多。然而,原来的文章中我用到了很多网站的内部链接,在转换后都失效了。
经过测试发现,Ghost中的文章数据在存储时,会把内链的链接存储为__GHOST_URL__/:slot。在转换后字符串中多了__GHOST_URL__,所以没有办法正常跳转。
解决方案也很简单,把这段文字替换为空字符串即可。

图片链接失效

这个问题也是由于图片地址变化了,所以原先通过Ghost编辑器直接插入上传的图片都无法显示,通过图床外链添加的图片不受影响。这个问题暂时不处理了,因为没有找到能够方便地一键导出所有图片的选项,除非去服务器后台备份,有点麻烦不想搞了。而且原先只有很少一部分图片是直接上传的,影响有限,都是一些很老的文章。没有图片的话也能节省服务器带宽。

之后使用新blog时都用图床上传图片,就不会有这个问题。当然图床是否稳定就是另外的话题了……

部署

这一步不赘述了,导出静态文件,然后一股脑丢到服务器上,配置一下即可。