Typecho迁移Hugo实践
Contents
前言
最近决定将博客从 Typecho 迁移到静态网站生成器 Hugo。相比动态博客,Hugo 带来的加载速度、安全性以及版本控制的便利性非常显著。本文记录了从数据库导出、自动化构建到 Algolia 搜索集成的完整过程。
一、数据迁移:从 SQLite 导出文章
1. 准备工作
- 拷贝 Typecho 的
.db数据库文件到本地。 - 拷贝
usr文件夹到本地。
2. 导出脚本
Typecho 的文章存储在 SQLite 数据库中。为了保留分类和标签层级,使用gemini编写了这个 Python 脚本。该脚本会自动处理 <!--markdown--> 标记,并按照 分类/年/月 的结构组织文件,方便后期管理。
import sqlite3
import os
import datetime
import re
import json
# 配置信息
DB_PATH = 'typecho.db'
OUTPUT_DIR = os.path.expanduser('~/blog_source/content/posts') # 直接指向你的 Hugo 目录
FIXED_AUTHOR = "mona" # 统一作者名为 mona
def clean_path_name(name):
"""过滤文件夹或文件名中的非法字符"""
if not name:
return "default"
# 替换 Windows/Linux 路径中的非法字符
name = re.sub(r'[\\/:*?"<>|\r\n]', '_', name).strip()
return name if name else "default"
def migrate():
if not os.path.exists(DB_PATH):
print(f"错误:未找到数据库文件 {DB_PATH}")
return
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
# 查询文章:cid, title, created, text
query = "SELECT cid, title, created, text FROM typecho_contents WHERE type='post' AND status='publish'"
cursor.execute(query)
posts = cursor.fetchall()
if not os.path.exists(OUTPUT_DIR):
os.makedirs(OUTPUT_DIR)
print(f"开始迁移并清理标签:共 {len(posts)} 篇文章...")
for row in posts:
curr_cid, original_title, curr_created, curr_text = row
# 1. 解析日期
dt = datetime.datetime.fromtimestamp(curr_created)
year_str = dt.strftime('%Y')
month_str = dt.strftime('%m')
date_iso = dt.strftime('%Y-%m-%dT%H:%M:%S+08:00')
# 2. 获取分类和标签
meta_query = """
SELECT m.name, m.type
FROM typecho_metas m
INNER JOIN typecho_relationships r ON m.mid = r.mid
WHERE r.cid = ?
"""
cursor.execute(meta_query, (curr_cid,))
metas = cursor.fetchall()
categories = [m[0] for m in metas if m[1] == 'category']
tags = [m[0] for m in metas if m[1] == 'tag']
# 3. 核心:合并你的清理逻辑
# 在写入前直接替换掉所有<!--markdown-->标签
content = curr_text.replace('<!--markdown-->', '')
# 4. 创建多层级目录 (分类/年/月)
main_category = categories[0] if categories else "uncategorized"
target_dir = os.path.join(OUTPUT_DIR, clean_path_name(main_category), year_str, month_str)
if not os.path.exists(target_dir):
os.makedirs(target_dir)
# 5. 确定文件路径
safe_filename = clean_path_name(original_title)
file_path = os.path.join(target_dir, f"{safe_filename}.md")
# 6. 写入 Hugo 格式文件
try:
with open(file_path, 'w', encoding='utf-8') as f:
f.write("---\n")
f.write(f'title: "{original_title}"\n')
f.write(f'author: "{FIXED_AUTHOR}"\n')
f.write(f"date: {date_iso}\n")
f.write(f"categories: {json.dumps(categories, ensure_ascii=False)}\n")
f.write(f"tags: {json.dumps(tags, ensure_ascii=False)}\n")
f.write("---\n\n")
f.write(content)
print(f"成功导出并清理: {file_path}")
except Exception as e:
print(f"处理失败 {original_title}: {e}")
conn.close()
print(f"\n迁移完成!所有文件已按分类和日期存入: {OUTPUT_DIR}")
if __name__ == "__main__":
migrate()二、环境搭建与主题配置
1. 安装 Hugo
winget install Hugo.Hugo --version 0.145.02.初始化与导入
- 运行
hugo new site my-blog创建站点 - 将导出的文章放入站点目录下的
/content/posts目录,usr文件夹放入/static目录。
3.配置hugo.toml
找个喜欢的主题放入themes文件夹,根据文档按照自己的需求进行配置。
这里我使用LoveIt主题,并开启Algolia搜索支持,配置如下:
baseURL = 'https://menma.top/'
languageCode = 'zh-CN'
title = 'MenmaTop'
theme = 'LoveIt'
languageName = "简体中文"
ignoreErrors = ["error-remote-getjson", "error-missing-instagram-accesstoken"]
# 分页配置
[pagination]
disableAliases = false
pagerSize = 20
path = "page"
#菜单配置
[menu]
[[menu.main]]
weight = 1
identifier = "posts"
pre = ""
post = ""
name = "所有文章"
url = "/posts/"
title = ""
[[menu.main]]
weight = 2
identifier = "tags"
pre = ""
post = ""
name = "标签"
url = "/tags/"
title = ""
[[menu.main]]
weight = 3
identifier = "categories"
pre = ""
post = ""
name = "分类"
url = "/categories/"
title = ""
[params]
defaultTheme = "auto"
title = "MenmaTop"
images = ["/info/favicon/favicon.svg"]
[params.author]
name = "mona"
email = ""
link = "https://menma.top"
[params.header]
desktopMode = "fixed"
mobileMode = "auto"
[params.header.title]
logo = "/info/favicon/favicon.svg"
name = "MenmaTop"
[params.footer]
enable = true
hugo = false
copyright = false
author = false
[params.section]
paginate = 20
rss = 10
[params.list]
paginate = 20
rss = 10
[params.app]
title = "MenmaTop"
noFavicon = false
svgFavicon = "/info/favicon/favicon.svg"
# Android 浏览器主题色
themeColor = "#ffffff"
# Safari 图标颜色
iconColor = "#5bbad5"
# Windows v8-10磁贴颜色
tileColor = "#da532c"
# 搜索配置
[params.search]
enable = true
type = "algolia"
contentLength = 4000
placeholder = ""
maxResultLength = 10
snippetLength = 50
highlightTag = "em"
absoluteURL = false
[params.search.algolia]
index = ""
appID = ""
searchKey = ""
#主页配置
[params.home]
rss = 10
[params.home.profile]
enable = false
[params.home.posts]
enable = true
paginate = 20
[params.page]
[params.page.code]
copy = true
maxShownLines = 100
[params.page.math]
enable = true
[params.page.share]
enable = false
[params.analytics]
enable = false
[markup]
[markup.highlight]
noClasses = false
[markup.goldmark]
[markup.goldmark.renderer]
unsafe = true
[markup.goldmark.extensions]
[markup.goldmark.extensions.passthrough]
enable = true
[markup.goldmark.extensions.passthrough.delimiters]
block = [['$$', '$$'], ['\[', '\]']]
inline = [['$', '$'], ['\(', '\)']]
[outputs]
home = ["HTML", "RSS", "JSON"]
page = ["HTML"]
section = ["HTML", "RSS"]
taxonomy = ["HTML", "RSS"]4.测试预览
使用 hugo server 命令预览效果。重点检查图片资源是否正常显示,确认无误后进行下一步。
三、配置Github Pages与Github Actions自动化
1.创建.gitignore文件
因为使用Github Action来生成文章,所以需要在项目根目录下创建.gitignore文件 排除一些不必要的目录和文件
public/
resources/_gen/
.hugo_build.log2.关联远程仓库
仓库名必须填<用户名>.github.io
在项目的根目录下执行git
# 初始化 git
git init
# 添加所有文件
git add .
#提交
git commit -m "Initial commit"
# 关联远程仓库 (替换为你自己的地址)
git branch -M main
git remote add origin https://github.com/<你的用户名>/<仓库名>.git
# 推送源代码
git push -u origin main3.配置 GitHub Actions 自动化部署
在项目根目录下创建 .github/workflows/hugo.yaml,内容如下:
name: Deploy Hugo site to Pages
on:
push:
branches: ["main"]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Setup Hugo
uses: peaceiris/actions-hugo@v3
with:
hugo-version: '0.145.0'
extended: false
- name: Build with Hugo
env:
HUGO_PARAMS_SEARCH_ALGOLIA_SEARCHKEY: ${{ secrets.HUGO_SEARCH_KEY }}
HUGO_PARAMS_SEARCH_ALGOLIA_APPID: ${{ secrets.APPLICATION_ID }}
HUGO_PARAMS_SEARCH_ALGOLIA_INDEX: ${{ secrets.INDEX_NAME }}
run: hugo --minify
- name: Upload Algolia Indexes
uses: iChochy/Algolia-Upload-Records@main
env:
APPLICATION_ID: ${{ secrets.APPLICATION_ID }}
ADMIN_API_KEY: ${{ secrets.ADMIN_API_KEY }}
INDEX_NAME: ${{ secrets.INDEX_NAME }}
FILE_PATH: "public/index.json"
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./public
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@v44.在 GitHub 网页端开启服务
开启服务
GitHub 仓库 -> Settings -> Pages,将 “Source” 改为 GitHub Actions。
提交配置
推送 hugo.yaml 后,在 Actions 选项卡查看进度。
5.修改域名
- 在 Settings -> Pages 中设置 Custom domain。
- 在 Cloudflare 中为域名添加 CNAME 记录,指向
用户名.github.io。 - 勾选 Enforce HTTPS 并更新 Hugo 的
baseURL。
四、日常更新流程
至此自动化环境搭建完成。后续更新博客只需“Git 三部曲”:
新建文章:hugo new posts/文章.md
提交修改:
git add .
git commit -m "feat: 新增文章内容"
git push