侧边栏壁纸
博主头像
小斯小站 博主等级

用心分享技术

  • 累计撰写 36 篇文章
  • 累计创建 73 个标签
  • 累计收到 6 条评论

目 录CONTENT

文章目录

使用PHP快速导出Halo文章为Markdown

SCH小斯
2024-04-25 / 0 评论 / 0 点赞 / 8 阅读 / 7721 字
温馨提示:
本文最后更新于 2024-04-25,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

前言

Halo是一个非常不错的博客程序,使用起来非常方便,但是在里面写的文章却不能导出为Markdown文件,那么我就实现了一个导出Halo文章为Markdown的程序

安装依赖

这个功能中,需要将HTML转换为Markdown,所以需要安装转换工具库,这个库的名称为league/html-to- markdown,使用命令安装这个库

composer require league/html-to-markdown

分析

如果你的博客使用了MySQL作为数据库,那么就可以比较方便的读取数据,如果是使用了默认的H2数据库,就需要导入到MySQL中。Halo的数据在数据库中存储比较特殊,采用了blob格式,我们能轻松读取数据,但是却难以修改,导出文章只要获取数据就足够了。Halo的数据全部存储于一张表:extension,每一行数据有name、data、version三个字段,其中name和data是我们要用到的字段。文章的name一般为:/registry/content.halo.run/posts/······,文章的data是一个JSON数据,数据中有一个字段为releaseSnapshot,存储着发布后的文章资源,我们需要读取这个资源并转换为Markdown。

编写代码

连接数据库

首先需要连接到博客的数据库,这里我采用了面向对象的写法

$db = new mysqli($hostname,$username,$password,$database);
if ($db->connect_error){
    die("Connect error ".$db->connect_error);
}

获取文章列表

$sql = "select * from extensions where name like '/registry/content.halo.run/posts/%'";
$result = $db->query($sql);
while($row = $result->fetch_assoc()) {
    // ···
}

这里的$row就是每一篇文章的数据

获取文章的标题

需要获取这行数据中的data->spec->title

$title = str_replace("|","-",json_decode($row["data"],true)["spec"]["title"]);

如果文章标题有|符号,在写入文件的时候会出现错误,需要替换

获取文章发布ID

文章的发布ID在这行数据中的data->spec->releaseSnapshot

$release = str_replace("|","-",json_decode($row["data"],true)["spec"]["releaseSnapshot"]);

获取文章内容

当拿到文章的发布ID后,就可以通过这个发布ID获取文章内容了

$releasePath = "/registry/content.halo.run/snapshots/$release";
$sql = "select * from extensions where name='$releasePath';";
$result1 = $db->query($sql);
while ($rowData = $result1->fetch_assoc()) {
    // ···
}

注意这里$result1不能和前面的$result同名,$row1不能和$row同名,否则会出现问题

分析数据结构

文章的主要数据在这行数据的data->spec->rawPatch,需要获取这个数据并进行处理,如果你的博客导入过Markdown文章,那么可能会有所不同,存储格式会比较特殊。首先获取正常格式的文章

$rawPatch = json_decode($rowData["data"], true)["spec"]["rawPatch"];

至于判断是否是正常文章,需要判断这个变量中的第一个字符是否为<,如果是就可以直接转Markdown,如果是[,那么是个JSON格式,里面是多行的数据需要进行拼接,如果都不是,那么文章可能是Markdown,就不需要进行转换,如果这个rawPatch不存在,说明文章没有发布。接下来处理JSON格式

if(str_starts_with($rawPatch, "[")) {
    $rawPatch = join("\n",json_decode($rawPatch, true)[0]["source"]["lines"]);
}

将HTML转换为Markdown

将HTML转换为Markdown就需要用到开头提到的那个库了

include "vendor/autoload.php";
use League\HTMLToMarkdown\HtmlConverter;
$converter = new HtmlConverter();
$rawPatch = $converter->convert($rawPatch);

将结果写入到文件中

@mkdir("posts");
@touch("posts/$title.md");
file_put_contents("posts/$title.md", $rawPatch);

这里我将文件存储到脚本目录同级的posts目录中

完整代码

注意 :使用这个代码需要检查数据库信息是否和博客数据库一致

<?php
$hostname = "127.0.0.1";
$username = "root";
$password = "123456";
$database = "root";
include "vendor/autoload.php";
use League\HTMLToMarkdown\HtmlConverter;
$converter = new HtmlConverter();
$db = new mysqli($hostname,$username,$password,$database);
if ($db->connect_error){
    die("Connect error ".$db->connect_error);
}
@mkdir("posts");
$sql = "select * from extensions where name like '/registry/content.halo.run/posts/%'";
$result = $db->query($sql);
while($row = $result->fetch_assoc()) {
    msg("Name {$row["name"]}");
    $data = json_decode($row["data"], true);
    $spec = $data["spec"];
    $title = str_replace("|","-",$spec["title"]);
    msg("Got $title");
    $release = $spec["releaseSnapshot"];
    msg("Release $release");
    $releasePath = "/registry/content.halo.run/snapshots/$release";
    $sql = "select * from extensions where name='$releasePath';";
    $result1 = $db->query($sql);
    while ($rowData = $result1->fetch_assoc()) {
        $data = json_decode($rowData["data"], true);
        if ($spec = $data["spec"]) {
            if ($rawPatch = $spec["rawPatch"]) {
                if (str_starts_with($rawPatch, "<")) {
                    $rawPatch = $converter->convert($rawPatch);
                } else {
                    if (str_starts_with($rawPatch, "[")) {
                        $_ = json_decode($rawPatch, true)[0];
                        if ($source = $_["source"]) {
                            if ($lines = $source["lines"]) {
                                $rawPatch = join("\n", $lines);
                                $rawPatch = $converter->convert($rawPatch);
                            } else {
                                if ($target = $_["target"]) {
                                    if ($lines = $target["lines"]) {
                                        $rawPatch = join("\n", $lines);
                                        $rawPatch = $converter->convert($rawPatch);
                                    } else {
                                        die("!!! No lines");
                                    }
                                } else {
                                    die("!!! No target");
                                }
                            }
                        } else {
                            die("!!! No source");
                        }
                    }
                }
                @touch("posts/$title.md");
                file_put_contents("posts/$title.md", $rawPatch);
                msg("Wrote $title.md");
            } else {
                msg("文章未发布");
            }
        } else {
            die("!!! No spec");
        }
    }
}
echo "All done";
function msg($message){
    if (@$_SERVER["argc"]) {
        echo "$message" . PHP_EOL;
    }else{
        echo "$message<br>";
    }
}

运行代码后,将可以在脚本同级目录下看到posts目录,目录中就是文章的Markdown文件了

结尾

这篇文章到这里就结束了,感谢耐心阅读,如果遇到问题可以在下方评论区中讨论。

0

评论区