WordPress の記事を Markdown に変換した
WordPress から GatsbyJS に記事を移行したときの手順を記します。
GatsbyJS は Markdown で書かれた記事からよしなにブログを生成してくれるので、WordPress の記事を Markdown に変換する必要がありました。 Markdown には HTML を書けるので記事の内容そのものは WordPress からコピペすれば良いのですが、記事のメタデータ(投稿日時やタグなど)も適切にコピペしないといけないのが面倒だったのでスクリプトを書きました。
前提
WordPress をバックアップするプラグイン BackWPup で作成されたデータベースのダンプファイル 4423_wp.sql
が手元にあるとします。
1. データベースを復元する
手元の MySQL にデータをインポートします。
データベース名は wp
としました。
$ mysql -uroot
mysql> create database wp
mysql> \q
$ mysql -uroot wp < 4423_wp.sql
テーブルはこんな感じです。
mysql> show tables;
+-----------------------+
| Tables_in_wp |
+-----------------------+
| wp_commentmeta |
| wp_comments |
| wp_filemeta |
| wp_links |
| wp_options |
| wp_postmeta |
| wp_posts |
| wp_term_relationships |
| wp_term_taxonomy |
| wp_terms |
| wp_usermeta |
| wp_users |
| wp_wpmm_subscribers |
+-----------------------+
2. データベースから記事を書き出す
MySQL に接続して全ての記事を Markdown 形式で出力するスクリプトを Go で書きました。
package main
import (
"database/sql"
"fmt"
"os"
"strings"
"time"
_ "github.com/go-sql-driver/mysql"
)
type Post struct {
ID uint64
Date time.Time
Title string
Content string
Tags []string
}
func main() {
db, err := sql.Open("mysql", "root:@/wp?parseTime=true")
if err != nil {
panic(err.Error())
}
defer db.Close()
q := `SELECT ID, post_date, post_title, post_content
FROM wp_posts
WHERE post_status = 'publish' AND post_type = 'post'`
rows, err := db.Query(q)
if err != nil {
panic(err.Error())
}
defer rows.Close()
for rows.Next() {
var post Post
err := rows.Scan(&post.ID, &post.Date, &post.Title, &post.Content)
if err != nil {
panic(err.Error())
}
post.Tags = fetchTags(db, post.ID)
writePost(&post)
}
}
func fetchTags(db *sql.DB, postID uint64) []string {
q := `SELECT t.name FROM wp_terms AS t
JOIN wp_term_relationships AS r
ON r.object_id = %d
JOIN wp_term_taxonomy AS tax
ON tax.term_taxonomy_id = r.term_taxonomy_id
AND t.term_id = tax.term_id`
rows, err := db.Query(fmt.Sprintf(q, postID))
if err != nil {
panic(err.Error())
}
defer rows.Close()
var tags []string
for rows.Next() {
var tag string
rows.Scan(&tag)
tags = append(tags, tag)
}
return tags
}
func writePost(post *Post) {
dateJST := getDateJST(post)
filename := fmt.Sprintf("%s-%d.md", dateJST.Format("20060102"), post.ID)
file, err := os.Create("./posts/" + filename)
if err != nil {
panic(err.Error())
}
defer file.Close()
file.WriteString("---\n")
file.WriteString(fmt.Sprintf("title: \"%s\"\n", post.Title))
file.WriteString(fmt.Sprintf("path: \"/%d\"\n", post.ID))
file.WriteString(fmt.Sprintf("date: \"%s\"\n", dateJST.Format("2006-01-02")))
file.WriteString(fmt.Sprintf("excerpt: \"%s\"\n", getExcerpt(post)))
file.WriteString(fmt.Sprintf("tags: [%s]\n", getTags(post)))
file.WriteString("---\n\n")
file.WriteString(post.Content)
}
func getDateJST(post *Post) time.Time {
return post.Date.In(time.FixedZone("Asia/Tokyo", 9*60*60))
}
func getExcerpt(post *Post) string {
index := strings.Index(post.Content, "<!--more-->")
if index >= 0 {
subst := string(post.Content[0:index])
return strings.TrimSpace(strings.Replace(subst, "\r\n", "", -1))
}
return post.Content
}
func getTags(post *Post) string {
quotedTags := make([]string, len(post.Tags))
for i, t := range post.Tags {
quotedTags[i] = "\"" + t + "\""
}
return strings.Join(quotedTags, ", ")
}
このスクリプトは
---
title: "WordPress の記事を Markdown に変換した"
path: "/wp2md"
date: "2020-05-09"
excerpt: "WordPress から GatsbyJS に記事を移行したときの手順を記します。"
tags: ["WordPress", "GatsbyJS", "MySQL", "Go"]
---
記事の内容(HTML)
のようなフォーマットで 20200509-wp2md.md
というファイルを出力します。
ただし、path
の部分を上記のようないい感じの文字列にするのは大変なので(もともと WordPress をそういうパスに設定していないので)とりあえず記事の ID にしています。
おわりに
記事テーブルには改訂の履歴も記録されていたりして、データベースの仕様を把握する必要があったので、思ったより面倒でした。 記事が20件ぐらいしかないなら愚直にコピペしたほうがいいです。