惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

G
Google Developers Blog
Apple Machine Learning Research
Apple Machine Learning Research
小众软件
小众软件
Recent Announcements
Recent Announcements
阮一峰的网络日志
阮一峰的网络日志
IT之家
IT之家
A
About on SuperTechFans
量子位
Engineering at Meta
Engineering at Meta
B
Blog
The Cloudflare Blog
博客园 - 【当耐特】
Hugging Face - Blog
Hugging Face - Blog
Y
Y Combinator Blog
J
Java Code Geeks
D
DataBreaches.Net
aimingoo的专栏
aimingoo的专栏
T
Tailwind CSS Blog
H
Help Net Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
V2EX
Stack Overflow Blog
Stack Overflow Blog
C
Check Point Blog
酷 壳 – CoolShell
酷 壳 – CoolShell

韩小韩博客

你还在用真实邮箱注册网站?等等,你真的想清楚了吗? IPFS星际文件系统 最新二合一收款码 - 物理合并版 从Hexo到Astro博客1分钟迁移指南 Astro 添加 Waline 评论组件 腾讯云 EdgeOne Pages 实测对比:能否成为国内开发者的 Cloudflare Pages 最佳平替? Astro 中使用 Lenis 增加鼠标滚动阻尼感 一组手机和电脑动态壁纸分享【分享】 Astro 添加 Twikoo 评论组件 Astro主题-优雅的vhAstro-Theme【使用文档】 Fetch的GET、POST简单HTTP请求封装 一些实拍的手机壁纸 大理And威海【图】 很喜欢西湖的水【转自:狮子狮子鱼】 Tarot-塔罗牌占卜 Web Watermark 图片添加水印在线小助手 HanAnalytics访问分析Web统计托管于(Cloudflare Pages) 基于AI的微博动态心情分析 阿里云免费用9年ecs教程【适合轻量化服务如frp】 Cloudflare优选IP➕DnsPod的DDNS自动切换 混沌神器Clash全家桶 卷王都在用的变态休息法 NodeJs文本相似度去重脚本 骤雨重山无限存储图床托管于(Cloudflare Pages) 分享好看的天空和云(长期更新) Typecho评论导出为Hexo的Valine、Twikoo等评论所支持的JSON文件 Safari浏览器内容被地址栏、菜单栏或工具栏遮挡导致的兼容问题 拼夕夕快速提现100元攻略 2024 平安喜乐 通义灵码:重塑编程的未来,引领技术革新
Typecho转到Hexo(主题由Typecho-Joe-Theme转Butterfly主题)
.𝙃𝙖𝙣 · 2026-04-11 · via 韩小韩博客

avatar

.𝙃𝙖𝙣

605 3分钟

Code

Typecho转到Hexo(主题由Joe主题转Butterfly主题)

前言

使用了好久好久好久的动态博客,身为一个前端的我,总是想着把动态博客转为静态博客,但是一直没有动手,最近博客被持续的攻击,无法正常访问等因素,终于决定把博客迁移到Hexo上,下面就来分享一下迁移过程。

文档导出

因为之前使用的是TypechoTypecho-Joe-Theme主题,主题中包含了很多自定义标签,所以我们在导出的过程中要处理一下数据,把Typecho-Joe-Theme的标签转换成HexoButterfly主题的标签,身为前端的我无脑选择使用NodeJs手撸一个简单的脚本来导出Typecho博客的文章数据。

const mysql = require("mysql");
const fs = require("fs");
const moment = require("moment");
var con = mysql.createConnection({
  host: "数据库地址",
  database: "数据库名",
  user: "数据库用户名",
  password: "数据库密码"
});

// 数据库前缀
const prefix = "typecho_";

con.connect(function (err) {
  if (err) {
    return console.log(err);
  }
});

// 数据库查询语句
const sql = `select title,slug,text,created,modified,category,tags from ${prefix}contents c,(select cid,group_concat(m.name) tags from ${prefix}metas m,${prefix}relationships r where m.mid=r.mid and m.type='tag' group by cid ) t1,(select cid,m.name category from ${prefix}metas m,${prefix}relationships r where m.mid=r.mid and m.type='category') t2 where t1.cid=t2.cid and c.cid=t1.cid`;

con.query(sql, function (err, posts) {
  if (err) {
    console.log(err.message);
    return;
  }

  posts.forEach(function (post) {
    const { title, slug, tags, created: oldCreated, modified: oldModified, text: oldText, category: cat } = post;
    let text = oldText.replace("<!--markdown-->", "");
    const created = moment.unix(oldCreated).format("YYYY-MM-DD HH:mm:ss");
    const updated = moment.unix(oldModified).format("YYYY-MM-DD HH:mm:ss");
    // 封面图
    const regex = /https:\/\/jsd\.onmicrosoft\.cn\/gh\/uxiaohan\/uxiaohan\.github\.io@master\/v2\/(\d{4}\/\d{2}\/\d+)\.(png|jpg|jpeg|gif|mp4)/;
    const match = text.match(regex);
    const cover = match ? match[0] : "";

    // Typecho Joe主题标签 转 Hexo 的 Butterfly 主题标签
    text = text
      .replace(/\{message type="([^"]+)" content="([^"]+)"\/\}/g, (match, type, content) => `{% note purple 'fas fa-info-circle' flat %}\n${content}\n{% endnote %}`)
      .replace(/\{anote\s+icon="([^"]+)"\s+href="([^"]+)"\s+type="([^"]+)"\s+content="([^"]+)"\s*\/\}/g, "[$4 ]($2)")
      .replace(/\{abtn\s+icon="([^"]*)"\s+color="([^"]*)"\s+href="([^"]*)"\s+radius="([^"]*)"\s+content="([^"]*)"\s*\/\}/g, "[$5]($3)")
      .replace(/\{cloud\s+title="([^"]+)"\s+type="([^"]+)"\s+url="([^"]+)"\s+password="([^"]*)"\/\}/g, "[$1]($3)")
      .replace(/\{callout color="([^"]+)"\}/g, "{% note default no-icon %}")
      .replace(/\{gird\s+column="([^"]+)"\s+gap="([^"]+)"\}/g, "{% note default no-icon %}")
      .replace(/\{dplayer\s+src="([^"]+)"\/\}/g, '{% dplayer "url=$1" %}')
      .replace(/{\/callout}/g, "{% endnote %}", "{% gallery %}")
      .replace(/\{\/gird\}/g, "{% endnote %}", "{% endgallery %}")
      .replace(/\{alert type="([^"]+)"\}/g, "{% note default modern %}")
      .replace(/{\/alert}/g, "{% endnote %}")
      .replace(/\{gird-item\}/g, "")
      .replace(/\{\/gird-item\}/g, "")
      .replace(/#([\u4e00-\u9fa5]+)/g, "# $1");

    const fileName_ = title.replace(/\//g, " ").replace(/-/g, " ").replace(/\?/g, " ").replace(/\[/g, "(").replace(/\]/g, ")");
    const filename = "./post/" + fileName_ + ".md";
    fs.appendFileSync(filename, "---\n");
    fs.appendFileSync(filename, "title: " + title.replace(/\[/g, "(").replace(/\]/g, ")").replace(/#/g, " ") + "\n");
    fs.appendFileSync(filename, "date: " + created + "\n");
    fs.appendFileSync(filename, "updated: " + updated + "\n");
    fs.appendFileSync(filename, "categories: " + cat + "\n");
    fs.appendFileSync(filename, "tags:\n- " + tags.replace(/,/g, "\n- ") + "\n");
    fs.appendFileSync(filename, "id: " + slug + "\n");
    fs.appendFileSync(filename, "cover: " + cover + "\n");
    fs.appendFileSync(filename, "---\n");
    fs.appendFileSync(filename, text);
  });
});

con.end();

// 导出完毕后,在脚本目录的`post`目录下,可以看到所有文章的markdown文件。

博客评论导出移步

Typecho评论导出为Hexo的Valine、Twikoo等评论所支持的JSON文件
Typecho Hexo Typecho-Joe-Theme Butterfly 主题