Justin's Words

从 Typecho 迁移到 Wordpress

迁移首要原因必然是 Wordpress 的普及性更加广,Typecho 的确是很轻量级,是个不错的博客程序。

迁移其实说白了就是数据库的迁移,再简单点就是文章的迁移,包括文章标题、文章作者、文章内容、文章创建时间、文章修改时间等,差不多这几样是最重要的,标签的话我没有迁移,因为我的标签不算多。

Typecho 的文章表结构

Typecho 存储文章的表是 typecho_contents,它的结构是这样的:

enter image description here

Wordpress 存储文章的表是 wp_posts,它的结构是这样的:

enter image description here

获取文章数据

我的建议是使用 phpmyAdmin 的 export 功能将 typecho_contents 表用 php array 格式导出,这样可以更方便地给 PHP 调用,如果使用其他格式导出当然是可以的,但比这个要麻烦。

转换 Markdown 为 HTML

由于 Typecho 的文章存储方式是 Markdown 格式,我们把其迁移到 Wordpress 数据表中得首先将其转换为 HTML 格式,这里我用的是 Parsedown,当然你也可以用其他的,我觉得 Parsedown 挺好用的。

Parsedown 用法

安装:

Include Parsedown.php or install the composer package.

例子:

$Parsedown = new Parsedown();

echo $Parsedown->text(‘Hello Parsedown!’); # prints:

Hello Parsedown!

Migrate 类

我写好了一个 Migrate 类用于获取导出数据的各个值,不是获取所有值,因为我觉得有的值没必要获得:

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
require('./app_youngdzeblog.php');    // 导出的文章表数据
require('./Parsedown.php'); // 引用 Parsedown

class Migrate
{
private $reg_remove_md_comment = "/<!--markdown-->/i"; // 去除<!--markdown-->是因为 Parsedown 不能正常处理这个 comment,应该是 bug

public function __construct($data) {
$this->data = $data;
}

function isArticle() {
return ($this->data['type'] === 'post') ? true : false; // 判断是否文章
}

function getVisualTime($timestamp) {
return date("Y-m-d H:i:s", $timestamp); // 把时间戳换为可阅读时间字符串,这是因为 Typecho 和 Wordpress 存储时间格式不同所致
}

function removeMDComment() {
return preg_replace($this->reg_remove_md_comment, '', $this->data['text']);
}

function parseMD() {
$Parsedown = new Parsedown();
$post = $this->removeMDComment($this->data['text']);
return $Parsedown->text($post);
}
}

INSERT

接下来就是直接操作 Wordpress 的数据库了。

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
require('./Migrate.php');

function generateSQLWithoutGuid($blog) {
$query = "INSERT INTO `wp_posts` (`ID`, `post_author`, `post_date`, `post_date_gmt`, `post_content`, `post_title`, `post_excerpt`, `post_status`, `comment_status`, `ping_status`, `post_password`, `post_name`, `to_ping`, `pinged`, `post_modified`, `post_modified_gmt`, `post_content_filtered`, `post_parent`, `guid`, `menu_order`, `post_type`, `post_mime_type`, `comment_count`) VALUES ";

foreach (range(0, (sizeof($blog) - 1)) as $i) {
$article = $blog[$i];
$mig = new Migrate($article);

if (!$mig->isArticle()) {
continue;
}

$post_date = $mig->getVisualTime($article['created']);
$post_date_gmt = $mig->getVisualTime($article['created']);
$post_content = addslashes($mig->parseMD());
$post_title = $article['title'];
$post_name = implode('-', explode(' ', $article['title']));
$post_modified = $mig->getVisualTime($article['modified']);
$post_modified_gmt = $mig->getVisualTime($article['modified']);

$query_insert = "(NULL, '1', '$post_date', '$post_date_gmt', '$post_content', '$post_title', '', 'publish', 'open', 'open', '', '$post_name', '', '', '$post_modified', '$post_modified_gmt', '', '0', '', '0', 'post', '', 0)";

$query = $query . $query_insert;

if ($i != (sizeof($blog) - 1)) {
$query = $query . ', ';
} else {
$query = $query . ';';
}
}

return $query;
}

$query_insert = generateSQLWithoutGuid($typecho_contents);

// 保证你的 PHP 版本为 5.0 以上才能使用 Mysqli 扩展,否则请查看官方文档使用其他方法。
$conn = mysqli_connect(SAE_MYSQL_HOST_M . ':' . SAE_MYSQL_PORT, SAE_MYSQL_USER, SAE_MYSQL_PASS, SAE_MYSQL_DB); // 这里应该换成你自己的数据库连接信息,我用的是 Sina App Engine 所以这么写

if ($conn->errno) {
printf("Connect to MySQL error: %s", $conn->error);
exit();
}

$conn->query("SET NAMES utf8"); // 必须使用 utf-8,这是一种好习惯

if ($conn->query($query_insert)) {
print("INSERT successfully.<br>");
} else {
printf("INSERT ERROR: %s<br>", $conn->error);
exit();
}

$query_select_all = "SELECT * FROM wp_posts";

// 这里设置文章的访问 URL,由于其是和 ID 一致的,所以得先全部 INSERT 成功才能 UPDATE 这个列
if ($result = $conn->query($query_select_all)) {
printf("SELECT return %d rows.<br>", $result->num_rows);
while($item = $result->fetch_array()) {
$id = $item['ID'];
$guid = "http://youngdze.co/?p=" . $id;
$updateSQL = "UPDATE `wp_posts` SET `guid` = '$guid' WHERE `ID` = " . $id;
if ($conn->query($updateSQL)) {
print("ID " . $id . " update successfully<br>");
} else {
printf("UPDATE ERROR: %s", $conn->error);
exit();
}
}

/* free result set */
$result->close();
} else {
printf("fetch data failed");
exit();
}

mysqli_close($conn);

那么,至此你的 Typecho 文章主要数据就能迁移到 Wordpress 了。

不过迁移完成后文章显示还是有各种问题,比如反斜杠滥用,这个我没想到办法解决,只能靠你手动处理了。