tDiary から WorPress への移行の顛末 (1)

というわけで、tDiary から WordPress に移行しました。もともと自分でプログラムを書いたりカスタマイズしたりすることを考えて採用した tDiary でしたが、実際にはそんなに手を入れられるほど自分はマメではなく、バージョンアップもだんだん面倒になってました。そのうちスマホで Facebook 等に記事を書くことの方が多くなって放置状態のまま約6年…(げ、そんなに放置してたか)。

で、心機一転ブログをまたちゃんと書こうかと考え始め (やはり自分の書いたものがちゃんとストックされる場所が必要と思ったのが発端です)、やるならメンテナンスが楽でカスタマイズの情報がいろいろあるものにしよう、ということで、使用しているさくらのレンタルサーバーではコントロールパネルから簡単にインストールできる WordPress を使うことを決断。年初に思い立ってから結構時間がかかりましたが、ようやくどうにか形になりました。

WordPress のインストールや設定については他にいろいろ情報もありますし、私が改めて書くこともないと思いますが、tDiary からのデータ移行については結構試行錯誤して自分なりに得たノウハウもありますので、ここに記録を残しておこうと思います。

tDiary から WordPress への移行の情報探索

まず、tDiary から WordPress への移行について、先駆者の情報を調べてみました。その結果、見つかった例ではいずれもtDiary のデータを何らかのツールで Movable Type 形式のデータに変換し、それを WordPress で読み込む、という手順を踏んでいました。特に

tDiaryからWordPress(MovableType)への移行@2018

では tDiary → Movable Type のツール3種の比較検討がなされており、これを読んだ結果

td2mt.sh: tDiary generated HTML file to MovableType file Converter
(作者のページ:tDiaryのHTMLファイルからMovableType形式のファイルを生成する「td2mt.sh」を書いた)

を使用することに決定しました。

td2mt.sh での Movable Type 形式のデータ作成

旧ブログの設定

移行の元のサイトの URL は https://www.yoshimura-s.jp/blog で、tDiary Blogkit を用いて構築されていました。Blogkit はもともと Web 日記ツールだった tDiary を blog 風に運用するためのもので、日付に意味を持たせず記事番号として扱うようになります。ページを表示する際にもタイトル部分に日付が入らなくなります (tdiary.conf で @date_format = ‘■’ 等として日付部分の表示を変更)。

また、タイトルに [] で括った単語を付記することでカテゴリーとして扱われるような仕組みも活用していました。

これらが今回 td2mt.sh を使う上で大きな影響がありました。

各ページの HTML ファイルの作成

ツールの使い方は GitHub リポジトリに含まれる tDiaryからWordpressに移行する に詳しく書かれていますが、要約すると

  • tDiary に付属の squeeze.rb を用いて各記事の HTML ファイルを作成する
  • 作成した HTML ファイルを td2mt.sh で MovableType 形式のデータに変換する

という手順を踏む必要があります。

しかしながら、まず私がさくらのレンタルサーバーに設定した環境 (古いですが tDiary 4.0.2, Ruby 2.1.0 → tDiary 4.0.2 へのアップデート 参照) では squeeze.rb がうまく動かない、という問題が生じました。いろいろ試行錯誤しましたが、必ずしも squeeze.rb を使わなくても HTML が生成できればよい、ということで、wget で HTML をファイルに落とすことにしました。

以下は、その際のシェルスクリプトです。あらかじめサーバー上にある tDiary のデータファイルをローカルの ${blog_data_dir} にコピーしてあることが前提になっており、そこから日付を取り出して一旦 ${blog_date_list_file} に書き出した後、その各日付を date パラメータに設定して ${base_url} (tDiary で運用していたブログのベースの URL) にアクセスし、${output_dir} に書き出す、というものです。

#!/bin/bash

set -e
set -x

base_url=https://www.yoshimura-s.jp/blog
blog_data_dir=~/data/blog
output_dir=~/data/blog_html
blog_date_list_file=~/data/blog_date_list.txt

if [ ! -e ${output_dir} ]; then
    mkdir ${output_dir}
fi

grep Date ${blog_data_dir}/*/*.td2 | sed -e 's/^.*: //' | sort -u > ${blog_date_list_file}

cat ${blog_date_list_file} | while read date
do
    wget ${base_url}/?date=${date} -O ${output_dir}/${date}.html
    sleep 1
done

HTML から Movable Type 形式のファイルの作成

あとは td2mt.sh の説明に従い、

$ find (HTMLのあるディレクトリ) -type f | sort | while read; do td2mt.sh $REPLY; done > export.txt

で Movable Type 形式のファイルを作成すれば OK…のはずでしたが、上述の日付とカテゴリーの問題などいくつかうまくいかないことが起き、td2mt.sh に手を入れることにしました。以下変更箇所を git diff の出力で示します。

何らかの影響 (文字化け?) でテキストと認識されず grep が効かない箇所があったので -a オプションを追加

@@ -118,7 +118,7 @@ shift $((OPTIND - 1))
 readonly PAGE=$1

 # tDiaryが生成したHTMLのチェック
-if [ -z "$(grep 'name="generator"' "$PAGE" | grep tDiary)" ]; then
+if [ -z "$(grep -a 'name="generator"' "$PAGE" | grep tDiary)" ]; then
        echo "$(basename ${0}): ${PAGE}: It is not the tDiary Generated HTML file." 1>&2
        exit 1
 fi

タイトルに付与したカテゴリーを CATEGORY として引き継ぐ設定を追加

day_post(), markdown_post(), section_post() のそれぞれにおいて、タイトルから正規表現で “[” と “]” に囲まれた個所を抜き出し、それをさらに個々の “[…]” に分けて配列に入れます (2つ目以降のコード)。得られた配列の各要素を “CATEGORY: …” の形で書き出します (最初のコード)。

@@ -134,8 +134,12 @@ ALLOW COMMENTS: $POST_ALLOW_COMMENTS
 CONVERT BREAKS: $POST_CONVERT_BREAKS
 DATE: $POST_DATE
 TAGS: $POST_TAGS
------
 _EOT_
+       for CATEGORY in "${CATEGORY_ARR[@]}";
+       do
+               echo CATEGORY: $CATEGORY
+       done
+       echo "-----"
 }

 body(){
@@ -278,6 +287,15 @@ day_post(){
        POST_TITLE=$(${XMLLINT} "string(//h2/span[@class=\"title\"])" "$1" 2>/dev/null | tr -d '\n' | sed 's|^ *||; s| *$||')
        [ ! "${POST_TITLE}" ] && POST_TITLE=$(date -d $(${XMLLINT} 'string(//h2/span[@class="date"]/a)' "$1" 2>/dev/null) +"%Y%m%d")

+       # タイトルとカテゴリーを分離
+       ORG_POST_TITLE=${POST_TITLE}
+       POST_TITLE=$(echo ${ORG_POST_TITLE} | sed -e 's/ *\[.*\] *//')
+       CAT_STR=$(echo ${ORG_POST_TITLE} | sed -e 's/^ *\(\[.*\]\) *.*$/\1/')
+       IFS_BAK="$IFS"
+       IFS=,
+       CATEGORY_ARR=($(echo ${CAT_STR} | sed -e 's/\]\[/,/g; s/\[\|\]//g'))
+       IFS="${IFS_BAK}"
+
        # 日記1日分のタグ
        POST_TAGS=$(for _i in $(${XMLLINT} '//div[@class="tags"]' "$1" 2>/dev/null | sed 's|<[^>]*>||g; s|Tags: ||g'); do echo ${_i}; done | sort | uniq | paste -s -d",")
@@ -303,6 +321,15 @@ markdown_post(){
        POST_TITLE=$(${XMLLINT} "string(//h2/span[@class=\"title\"])" "$1" 2>/dev/null | tr -d '\n' | sed 's|^ *||; s| *$||')
        [ ! "${POST_TITLE}" ] && POST_TITLE=$(date -d $(${XMLLINT} 'string(//h2/span[@class="date"]/a)' "$1" 2>/dev/null) +"%Y%m%d")

+       # タイトルとカテゴリーを分離
+       ORG_POST_TITLE=${POST_TITLE}
+       POST_TITLE=$(echo ${ORG_POST_TITLE} | sed -e 's/ *\[.*\] *//')
+       CAT_STR=$(echo ${ORG_POST_TITLE} | sed -e 's/^ *\(\[.*\]\) *.*$/\1/')
+       IFS_BAK="$IFS"
+       IFS=,
+       CATEGORY_ARR=($(echo ${CAT_STR} | sed -e 's/\]\[/,/g; s/\[\|\]//g'))
+       IFS="${IFS_BAK}"
+
        # 日記1日分のタグ
        POST_TAGS=$(for _i in $(${XMLLINT} '//div[@class="tags"]' "$1" 2>/dev/null | sed 's|<[^>]*>||g; s|Tags: ||g'); do echo "- ${_i}"; done | sort | uniq )
@@ -326,7 +353,16 @@ section_post(){

        local _i=1
        while [ $_i -le $DIARY_SECTION_COUNT ]; do
-               POST_TITLE=$(${XMLLINT} "//div[@class=\"section\"][$_i]/h3" "$1" 2>/dev/null | tr -d '\n' | sed -e "$_title_clean_cmd")
+                POST_TITLE=$(${XMLLINT} "//div[@class=\"section\"][$_i]/h3" "$1" 2>/dev/null | tr -d '\n' | sed -e "$_title_clean_cmd")
+               # タイトルとカテゴリーを分離
+               ORG_POST_TITLE=${POST_TITLE}
+                POST_TITLE=$(echo ${ORG_POST_TITLE} | sed -e 's/ *\[.*\] *//')
+               CAT_STR=$(echo ${ORG_POST_TITLE} | sed -e 's/^ *\(\[.*\]\) *.*$/\1/')
+               IFS_BAK="$IFS"
+               IFS=,
+               CATEGORY_ARR=($(echo ${CAT_STR} | sed -e 's/\]\[/,/g; s/\[\|\]//g'))
+               IFS="${IFS_BAK}"
+
                POST_TAGS=$(${XMLLINT} "string(//div[@class=\"section\"][$_i]/div[@class=\"tags\"])" "$1" 2>/dev/null | sed 's|Tags: \(.*\) |\1|g;s| |,|g')
                POST_BODY=$(${XMLLINT} "//div[@class=\"section\"][$_i]" "$1" 2>/dev/null \
                | sed '1,2d; $d' | sed -e '/<h3>/{:loop N; /<\/h3>/!b loop; s|<h3>.*</h3>||g}' -e "${CMD_CLEAN_POST}" -e "${CMD_REPLACE_IMG_PATH}")

タイトルに日付が含まれない場合 (上述のケース) はアンカーから取得した日付を使用

date クラスの h2 の下のアンカーの href からも日付を取得して ${ANCHOR_DATE} に格納します (最初のコード)。出力時に、${DIARY_DATE} が日付の形になっていない場合は ${ANCHOR_DATE} を使用することを試みます (2番目のコード)。

@@ -194,6 +198,7 @@ _EOT_

 # 日記の日付。時間はPOST_TIME固定
 DIARY_DATE=$(${XMLLINT} 'string(//h2/span[@class="date"]/a)' "$PAGE" 2>/dev/null | sed -e 's|年|-|g; s|月|-|g; s|日||g; s|(.*)||g')
+ANCHOR_DATE=$(${XMLLINT} 'string(//h2/span[@class="date"]/a/@href)' "$PAGE" 2>/dev/null | sed -e 's|^.*date=||; s|\([0-9][0-9][0-9][0-9]\)\([0-9][0-9]\)\([0-9][0-9]\)|\1-\2-\3|')
 # Last modified
 DIARY_LASTMODIFIED=$(${XMLLINT} 'string(//span[@class="lm"])' "$PAGE" 2>/dev/null | sed 's|Update: ||g; s|更新||g; s|年|-|g; s|月|-|g; s|日||g')
 DIARY_LASTMODIFIED_HEAD=$(sed -n '/http-equiv="Last-Modified"/{s|^.* content="\(.*\)">|\1|p}' "$PAGE")
@@ -203,8 +208,11 @@ DATE_FORMAT="%m/%d/%Y %r"
 [ "${OUT_SWITCH}" = "markdown" ] && DATE_FORMAT="%F %T"
@@ -203,8 +208,11 @@ DATE_FORMAT="%m/%d/%Y %r"
 [ "${OUT_SWITCH}" = "markdown" ] && DATE_FORMAT="%F %T"

 # 日記に日付があれば投稿はその日付。無くてLast modifiedがあればLast modifiedを日付に設定
-if [ "${DIARY_DATE}" ];then
-       POST_DATE=$(LANG=C date -d"${DIARY_DATE} ${POST_TIME}" +"${DATE_FORMAT}")
+# if [ "${DIARY_DATE}" ];then
+if [[ $DIARY_DATE =~ ^[0-9]{4}\-[0-9]{2}\-[0-9]{2}$ ]]; then
+        POST_DATE=$(LANG=C date -d"${DIARY_DATE} ${POST_TIME}" +"${DATE_FORMAT}")
+elif [[ $ANCHOR_DATE =~ ^[0-9]{4}\-[0-9]{2}\-[0-9]{2}$ ]]; then
+        POST_DATE=$(LANG=C date -d"${ANCHOR_DATE} ${POST_TIME}" +"${DATE_FORMAT}")
 elif [ "${DIARY_LASTMODIFIED_HEAD}" ]; then
        POST_DATE=$(LANG=C date -d"${DIARY_LASTMODIFIED_HEAD}" +"${DATE_FORMAT}")
 else

コメントの日付の変換を修正

コメントの日付が日本語の年月日の書式になっていたので、変換ロジックもそれに合わせています。

@@ -231,7 +239,8 @@ diary_comment(){
        for _i in $(seq 1 $DIARY_COMMENT_COUNT); do

                COMMENT_AUTHOR=$(${XMLLINT} "string(//div[@class=\"commentator\"][$_i]/span[@class=\"commentator\"])" "$1" 2>/dev/null)
-               _date=$(${XMLLINT} "string(//div[@class=\"commentator\"][$_i]/span[@class=\"commenttime\"])" "$1" 2>/dev/null | sed 's|(\(.*\))|\1|g; s|(.)||g')
+               # _date=$(${XMLLINT} "string(//div[@class=\"commentator\"][$_i]/span[@class=\"commenttime\"])" "$1" 2>/dev/null | sed 's|(\(.*\))|\1|g; s|(.)||g')
+               _date=$(${XMLLINT} "string(//div[@class=\"commentator\"][$_i]/span[@class=\"commenttime\"])" "$1" 2>/dev/null | sed 's|年|-|g; s|月|-|g; s|日||g; s|(.)||g')
                COMMENT_DATE=$(LANG=C date -d "$_date" +"%m/%d/%Y %r")

                if [ "${COMMENT_AUTHOR}" = "TrackBack" ]; then

これらによって、ひとまず私の旧ブログのデータを td2mt.sh で Movable Type 形式のデータに変換することができました。ここまで来ればあと一息。

…と思ったが甘かった。それについては次の記事で。

tDiary から WorPress への移行の顛末 (1)” に対して1件のコメントがあります。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です