8年お世話になったMovableType(以下MT)からWordpress(以下WP)へ移行した。
パーマリンクをそのまま引き継ぐ形での移行方法を取る人が多いようだが、MTはVer.2から使っていて、旧記事を残したままパーマリンクを変更したりもしているので検索して出てくる方法では移行できない。今回は301リダイレクトで移行してみた。移行元と移行先はそれぞれ
- MTでの旧ブログ:”http://mmt.silvermoon.jp/”
- WPでの新ブログ:”http://mmt.silvermoon.jp/”
- MTでの旧ブログ移動先”http://mmt.silvermoon.jp/old_blog”
手順は
- MT管理画面で”記事のバックアップ”を行う。
- “http://mmt.silvermoon.jp/” で表示していたMTの記事を ”http://mmt.silvermoon.jp/old_blog” に移動。(失敗時にすぐ元に戻せるように再構築はせず、ディレクトリのコピーもしくはWebサーバの設定変更のみ。)
- http://mmt.silvermoon.jp/ でWPをセットアップ。サイトのアドレス (URL)、パーマリンク等を設定した後インポート。
とごくごく普通に移行。『Movable Type から WordPress への移行 – WordPress Codex 日本語版』の工程2まで、パーマリンク云々の前まで行う。
ここまで勢い良く進んでからruby 1.8.7で移行ツールを作成。まずはMySQL上のWordpressのDBの中身を眺めてみる。どうやら”wp_posts”テーブルの”guid”カラムに移行後のアドレスが入っているっぽい。これを利用して301リダイレクトの設定を自動生成してみる。
いじってみてわかったが、ツールを動かす前にWPでの過去記事の編集はしないこと。編集をすると自動セーブで大量に同じタイトルのレコードが生成されるらしい。また、”guid”は最初に設定された条件で生成されるらしく、途中でサイトURL、パーマリンクを変更しても変更されない(細かい仕様は知らないが)ため、気が変わった場合はDBごとまっさらにしてインストールしなおす方が良いと思う。
まずは旧ブログの記事一覧を取得する。サーバ内のMTトップディレクトリへのパスを”/var/ほげほげ”とすると
find /var/ほげほげ -name "*.html" > posts.list
これでMTの全記事が”posts.list”に入る。このうち”index.html”なものは、カテゴリーor月別アーカイブなので除外。
grep -v index.html posts.list | grep -v mt-static > postsonly.list
これでindex.html以外のリストができたはず。最後に適当なエディタで開き”archives.html”やMTの”readme.html”、テーマ関連ファイル”~themes/ほげほげ”等を削除。
スクリプトの処理内容は下記の通り。
- ディレクトリ名を元に各記事のMT公開URLを再構成
- 各MT記事へアクセスして記事名と公開日時を取得
- 記事名と公開日時を元にwp_postsからguidを取得
- guid(WPでのURL)へアクセスして記事名を取得、旧記事名と比較して正しければRedirect文を作成
下記のソースコードをmt2wp.rbとして保存。
※追記 Ruby 1.8.7と書くのを忘れてた(苦笑)
require 'rubygems'
require 'mechanize'
require "mysql"
require "kconv"
agent = Mechanize.new
agentComp = Mechanize.new
my = Mysql.init
my.options(Mysql::SET_CHARSET_NAME,'utf8')
# WordPressのDB情報(wp-config.phpと同じ)を書く
my.connect("localhost", "WordpressのDB名", "DBのユーザー名", "パスワード")
# ブログの名称
blogName = "MMT's Blog"
# ↓はタイトルが「記事名 - ブログ名」の場合。例えば「ブログ名 : 記事名」
# だった場合は『(blogName + " : ")』に変更。
reBlogName = Regexp.new(Regexp.quote(" - " + blogName))
# サーバー内のMovableTypeトップディレクトリへのパス
dirName = "/var/ほげほげ"
reDirName = Regexp.new(Regexp.quote(dirName))
# 新しいWordpressのURL
newURL = "http://mmt.silvermoon.jp"
re = Regexp.new(Regexp.quote(newURL))
# 旧MovableTypeのURL
oldBlog = "http://mmt.silvermoon.jp/old_blog"
while line = gets
begin
# ファイルへのパスからURLへ変換
agent.get(line.sub(reDirName, oldBlog))
rescue Timeout::Error
puts " Timeout::Error with " + line.sub(reDirName, oldBlog)
rescue Mechanize::ResponseCodeError => e
case e.response_code
when "404"
puts " 404 Notfound with " + line.sub(reDirName, oldBlog)
else
puts " HTTP Error:" + e.response_code + " with " + line.sub(reDirName, oldBlog)
end
end
# 正しく書くならURLがおかしくてアクセス不能な場合はここ以下をスキップすべきであるが実害はないし面倒くさいのでやらない。
# httpファイルのtitleから記事名取得
s = agent.page.title.to_s.strip
# 「記事名 - ブログ名」が取得されるはずなので、記事名のみに。
s = s.sub(reBlogName, "")
# RSSフィールドから公開日時を取得
# 各環境で若干変わるはずなので、旧ブログのソースを見て下記の「dc:date=」などを適宜変更
elem = agent.page.root.inner_html.scan(/dc:date=\"\d{4}-\d{1,2}-\d{1,2}T\d{1,2}:\d{1,2}:\d{1,2}\+09:00\"/).to_s()
d = elem.scan(/\d{4}-\d{1,2}-\d{1,2}/).to_s() + " " + elem.scan(/\d{1,2}:\d{1,2}:\d{1,2}/).to_s()
# puts "Search with " + "\t" + agent.page.title.to_s.strip + "\t" + s.strip + "\t" + d # Mysql検索パラメータ表示(デバッグ用,挙動がおかしい場合はコメントアウトして確認)
# DB検索
res = my.query("select guid from wp_posts where post_date = \'#{d}\' and post_title like \'%#{Mysql.quote s.strip}\%'")
# puts "#{res.num_rows}" # 検索結果の数表示(デバッグ用)
case res.num_rows
when 0
# 一意な記事が無いということなので、検索パラメータがおかしいか記事のインポートに失敗しているはず
puts "No post name of:" + s + "\t" + "No post ID" + "\t" + line.strip + "\t" + "No URL on wordpress"
else
if res.num_rows >= 2
#同名、同時刻の記事が二つ以上インポートされてる。もしくはバグによる検索ミス
puts "Result # " + "#{res.num_rows}" + ": Search Err"# 検索結果の数表示
end
# 念の為、移行先のURLへアクセスできるか確認。必要なければputs "Redirect~"行意外をコメントアウト
res.each do |row|
begin
agentComp.get(row[0].sub(re, newURL))
rescue Timeout::Error
puts " Timeout::Error " + row[0].sub(re, newURL)
rescue Mechanize::ResponseCodeError => e
case e.response_code
when "404"
puts " 404 Notfound " + row[0].sub(re, newURL)
else
puts " HTTP Error:" + e.response_code + " " + row[0].sub(re, newURL)
end
end
# 移行先の記事名と元記事名の比較
comp = agentComp.page.title.to_s.strip.split(" | ")
if comp[0].strip.toutf8 == s.strip.toutf8
# 結果出力
# 301リダイレクトにしたくないときは下記の"permanent"を削除
puts "Redirect permanent " + line.strip.to_s().sub(reDirName, "") + " " + row[0]
else
puts "URL is invalid " + row[0] + ": title : " + s.strip.toutf8
end
end
end
end
必要箇所(DB情報、blogName、reBlogName、newURL、oldBlog)を変更して実行。
ruby mt2wp.rb postonly.list
これで処理結果が出てくる。エラーを潰してきれいな結果のみになったら
ruby mt2wp.rb postsonly.list > Redirect.list
これで”Redirect.list”にmod_alias形式のリストが出来るので中身を確認。
wc -l postsonly.list Redirect.list
正しくインポートされていれば行数が同じになるはず。”.htaccess”に書くなりなんなりして終了。
※mt2wp.rbはGPLとする。






