tDiary から WordPress への移行の顛末 (2)

tDiary から WordPress への移行の顛末 (1) Movable Type 形式のファイルを作成できたので (以下生成したファイルを export.txt とします)、旧ブログの内容を WordPress に取り込めるようになりました。あとは旧 URL へのアクセスを mod rewrite なり何なりでリダイレクトしてやれば一件落着!と思ったのですが、そのリダイレクトのルール、どうやって書く?という問題に突き当たりました。また、サイト内の記事参照についてはできればあらかじめ書き換えておきたい、という気持ちもありました。そしてここから試行錯誤の旅が…

WordPress のパーマリンクの設定

リダイレクトするにしても内部リンクを書き換えるにしても、記事の URL がわからなくてはどうしようもありません。よって、まずは WordPress 側のパーマリンクの設定を検討しました。

デフォルトでは

https://www.yoshimura-s.jp/?p=<記事ID>

となっていましたが、パラメータで指定するのはなんかイマイチ (これまで tDiary ではパラメータ指定だったけど、この際脱却ということで)。また、記事 ID でパーマリンクを設定すると、WordPress に実際に取り込むまで記事 URL が予測不可能になります。一度旧記事をインポートしてから記事 ID を何らかの手段で取り出して対応表を作ればリダイレクトは設定できるかもしれないけど、内部リンクの書き換えのことを考えるとちょっとやりたくないです (もっとも、後述するようにリダイレクトについては最終的に対応表形式になっちゃったのですが)。

以上から、パーマリンクには投稿名 (記事タイトル) を使うこととし、投稿名のバッティングも考慮して投稿年月も追加しました (ダッシュボードの設定→パーマリンク設定で「月と投稿名」を選択)。URL 構造タグで書くと以下の通りです。

https://www.yoshimura-s.jp/%year%/%monthnum%/%postname%/

一般的に投稿名は日本語なので、URL がパーセントエンコードの嵐 (例えば https://www.yoshimura-s.jp/2020/05/%e3%83%aa%e3%83%80%e3%82%a4%e3%83%ac%e3%82%af%e3%83%88%e3%81%ae%e8%a8%ad%e5%ae%9a%e3%82%82%e5%ae%8c%e4%ba%86/) になっちゃって醜悪ではあるのですが、過去記事についてはしょうがないかと。

後処理ツール post_process.py の作成

上記のように記事のパーマリンクの設定を決めたので、記事の内部リンクを書き換える処理を行います。もはやシェルスクリプトの世界でやるのはちょっと厳しいものがあるので、td2mt.sh のさらなるカスタマイズはあきらめ、export.txt を後処理でいろいろ書き換えるツールを作成することにしました。

実際のコードは GitHub yoshimura-shunji/td2mt (オリジナルからのフォーク) の post_process.py です。第一引数に td2mt.sh で生成したファイル、第二引数に出力ファイルを設定します。下記の部分 (14~24行目) は私のサイトのファイル配置を前提とした定義なので、流用していただく場合はここを修正する必要があります (多分ここだけだと思うのですが…)。

img_src_table = [
    ['../image/', './wp-content/image/'],
    ['../photo/', './wp-content/photo/']
]

a_href_table = [
    ['../', './archives/']
]

old_base_url = 'https://www.yoshimura-s.jp/blog'
new_base_url = 'https://www.yoshimura-s.jp'

以下簡単に説明します。

内部リンクの書き換え: rewrite_link()

記事の中で出現する内部リンクを書き換えます。また画像や写真のフォルダ、固定ページについてはそれぞれ img_src_table, a_href_table に従って変換を行います。記事へのリンクについては以下の処理を使用します。

日付とタイトルの dict の生成: gen_date_title_dict()

読み込んだファイルの日付をキーとしてタイトルを格納する dict データを生成します。元データには時刻までが含まれますが、td2mt.sh が付与している時刻は固定値なので省略しています (本当は読もうとしてやめたのがコメントアウトからバレてますが)。

タイトルから WordPress の post name 生成: sanitize_title()

ここが結構面倒だった…

生成したファイルを一度 WordPress に読み込ませたところ、パーマリンクの post name はタイトルの単なる URL エンコード (パーセントエンコード) ではないことがわかりました。具体的には以下の通り。

  • 文字数が制限される (デフォルトはエンコード後の長さで最長200Bytes)
  • 一部の文字については削除や置き換えが行われる

仕様を確認すべく、WordPress のコードも読んでみたりしました。どうやら wp-include/formatting.php の sanitize_title_with_dashes(), sanitize_file_name() あたりが関与している模様です。これらのコードから関係ありそうなところを python に移植し、一部現物合わせで修正したのが sanitize_title() です。実際には不要な処理もありそうな気がしますが、まあちゃんと動いていそうなのでこんなもんでよいかと。

ちなみにここで使用している quote_string() はエンコード後の長さが設定した上限に至るまでパーセントエンコードを行う処理です。WordPress の実装はバイト値ごとに場合分けしてちゃんとやっているようですが、ここでは手抜き実装しています。

def quote_string(string, max_length):

    quoted_string = ''
    for char in string:
        quoted_char = urllib.parse.quote(char)
        if len(quoted_string) + len(quoted_char) > max_length:
            break
        else:
            quoted_string += quoted_char
    return quoted_string

新しい URL を生成: gen_new_url()

与えられた旧 URL の date パラメータから日付を生成して date_title_dict (gen_date_title_dict() で生成されたデータ) を参照し、タイトルを取得。そのタイトルから sanitize_title() で WordPress の post name を生成し、設定したパーマリンクのフォーマットに合わせて新 URL を生成します。

YouTube の埋め込みプレーヤーの書き換え: rewrite_youtube_embedding()

上記の処理で export.txt を変換して WordPress に読み込ませてみたところ、内部リンクについては正しく変換できているようでした。しかし、埋め込んだ YouTube 動画が表示されません。

当初は WordPress 上で一件一件手作業で修正することも考えたのですが、export.txt で grep をかけたら優に100件以上あったため、これについても変換処理を追加することにしました。

いろいろ試行錯誤しましたが、結局以下のような形に統一すれば大丈夫との結論に達しました。

<figure class="wp-block-embed-youtube wp-block-embed is-type-video is-provider-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe width="750" height="422" src="https://www.youtube.com/embed/VQLIQWSFtpY?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div></figure>

ここで src の https://www.youtube.com/embed/ に続く VQLIQWSFtpY は YouTube のコンテンツ ID で、ここだけ書き換えれば見せたいコンテンツのプレーヤーが埋め込まれます。ちゃんと HTML の要素を解析してプロパティを書き換えるような処理を書こうかとも思いましたが、面倒になって単なる文字列処理でお茶を濁しています。

以上の処理で export.txt を変換することにより、WordPress に読み込んでも内部リンク切れが発生せず、また YouTubue 埋め込みプレーヤーも正しく表示できるようになりました。

リダイレクトの設定

内部のリンクについては解決したものの、外部から旧 URL へのアクセスがこのままではリンク切れになります。これについては リダイレクトの設定も完了 に書いた通り、Redirection プラグインを使用しました。

プラグインにファイルのインポート機能があったため、旧 URL と新 URL の対応一覧を生成して CSV に落とすことにしました。本当は pre_process.py の中でこのファイルも作ってしまえばよかったのですが、実装中はそこまで気が回っておらず…。実行時に標準エラー出力をログとして取ってあったので、gen_date_title_dict() を実行した際のログ出力を切り出して加工して日付とタイトルの CSV ファイルを作り、これを読み込んで新 URL を生成して旧 URL と新 URL の対応一覧を作成するプログラム gen_redirection.py を新たに実装しました。

これによって生成された CSV ファイルを Redirection プラグインで読み込むことにより、外部からの旧 URL でのアクセスも新 URL にリダイレクトされるようになりました。

ついでに固定ページのリダイレクトも追加して、ようやく一通り設定完了となりました。

後からこうすればよかったと思ったことなど

Movable Type 形式のデータの WordPress へのインポートにおいて、WordPress の post name には必ず TITLE の値が引き継がれるのだ、と思っていたのですが、実は BASENAME という項目がある場合はこちらの値が post name に引き継がれるようです。ということは、今回のように日本語タイトルを苦労して扱わなくても、export.txt を作成する段階で BASENAME を扱いやすい値に設定すればそれがそのまま post name になる、ということです。

少なくともこれまでの記事で SEO 云々をどうにかしようという気はないので、通し番号か何かにしておけば処理は楽でした。ちょっと凝ったことをやるなら KAKASI 等で日本語タイトルをローマ字変換してしまってもよかったかも。

また、既存ツールを使いたかったので tDiary から一旦 Movable Type 形式のデータを経由したのですが、結局それなりの量のコードを書いたことを考えると、WordPress 形式の XML に直接変換するツールを自前で作ってしまってもよかったかもしれない、とも思っています。とはいえそれは今回の一連の作業を経験したから言えることで、いきなり最初からその道に進んだらまだまだ試行錯誤は終わっていなかったかもしれません。

以上、tDiary から WordPress への移行の顛末でした。おしまい。

コメントを残す

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