WordPressに持ってきてしまった、はてなキーワードリンクを解除しました(XMLRPC + wordpress-javaを使用)

前回 はてなダイアリーから持ってきたはてなダイアリーの記事がはてなキーワードリンクを含んでしまっているので、解除するためにスクリプト組むと宣言していました。
今日そのためのスクリプトを書いてリンク解除に成功したので報告します。

変換方法

WordPressのXMLRPC機能を使ってAPI経由でデータ変換をしました。

イメージとしては一つ一つのページを手で開いて、エディタに記事を貼り付けて正規表現置換して保存を繰り返すのと同じです。

この方法のメリットは

  • インターネットに繋がっていればどの端末からも実行できる
  • 履歴データまでは置換しない(WordPressは記事を変更した履歴を保持しています)
  • 変更履歴が残る(その気になればいつでも変換前に戻せる)

デメリットは

  • 前記事分の通信が発生する
  • APIの仕様を調べなければならない
  • APIを有効にしなければならない

当初SQL一本流して変換しようとしましたがMySQLは正規表現で文字列置換をすることができないらしく、それならPHPでスクリプトを組もうとも思ったのですが、DB直接操作するより外部から触ったほうが安全だと考え、API方式を選択しました。

WordPressのAPIはXMLRPC方式と、AtomPub方式の二種類提供されていますが、後者はデフォルトで無効になっていたり、日本語の情報が皆無に近く、XMLRPC方式の場合は、もう少し状況がマシなようで、 wordpress-java というJavaクライアントライブラリもありましたので、今回はこちらを使用しました。

変換プログラム全文

public class App {
    static String user = "******";
    static String pass = "****************";
    static String url = "*************/xmlrpc.php";

    public static void main(String[] args) throws MalformedURLException, XmlRpcFault {
        WordPress wp = new WordPress(user, pass, url);
        List<Page> recentPosts = wp.getRecentPosts(1000);
        Pattern pattern = Pattern.compile("<a class=\"keyword\" href=\"[^\"]+\">([^<]+)</a>");
        for (Page page : recentPosts) {
            String contents = page.getDescription();
            if (contents.contains("keyword")) {
                Matcher m = pattern.matcher(page.getDescription());
                System.out.println(page.getPermaLink());
                while (m.find()) {
                    System.out.println("\t\t" + m.group(1) + "\t" + m.group());
                    contents = contents.replace(m.group(), m.group(1));
                }
                page.setDescription(contents);
                System.out.println("\tsaved status = " + page.getPost_status());
                wp.editPost(page.getPostid(), page, page.getPost_status());
            }
        }
    }
}

実行結果はこんなかんじです。

http://www.susumuis.info/entry/2014/03/31/103829
        コンストラクタ <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%B9%A5%C8%A5%E9%A5%AF%A5%BF">コンストラクタ</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        API <a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>
        hoge    <a class="keyword" href="http://d.hatena.ne.jp/keyword/hoge">hoge</a>
        コンストラクタ <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%B9%A5%C8%A5%E9%A5%AF%A5%BF">コンストラクタ</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        コンストラクタ <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%B9%A5%C8%A5%E9%A5%AF%A5%BF">コンストラクタ</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        Java5   <a class="keyword" href="http://d.hatena.ne.jp/keyword/Java5">Java5</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        GC  <a class="keyword" href="http://d.hatena.ne.jp/keyword/GC">GC</a>
    saved status = publish
http://www.susumuis.info/entry/2014/03/30/004202
        Mayaa   <a class="keyword" href="http://d.hatena.ne.jp/keyword/Mayaa">Mayaa</a>
続く...

キーワードの変遷を見ると当時どんなことに注目していたのか見られて面白いです。

実行後、変換された記事のリビジョンが1増加しています。差分を表示すると、たしかにキーワードの箇所のみ置換されています。

キーワードだけ置換されている

プログラム解説

解説するまでもない短いプログラムですが、正規表現によって検索をし、置換のところは正規表現を使わずに完全一致で置換しています。こうすることで、どこが変わったのかログに出力することができます。

contents = contents.replace(m.group(), m.group(1));

の部分は記事本文をまるごと置換しているのでメモリ効率の点で言えば有利ではありません。が、以前業務で作った変換ツールはこの方式でトータル数十万行のファイル群を平気で置換できてしまっていたのでこの程度気にするほどではないでしょう。ただし、件数によっては時間がかかるかもしれません。

wp.editPost(page.getPostid(), page, page.getPost_status());

の部分は第三パラメータがJavaDocで

String pubulish : publish status

となっていて、それ以上の説明がありません。WordPressのAPIの仕様書を見てもよくわからなかったので、当てずっぽうでprivateまたはpublicを返すpost.getPost_status()にしたら、うまくいきました。

newPostの場合はbooleanなので、この辺り統一性がないなと思いました。

キーワードリンクの是非について

自分がはてなブログ内にいたら、キーワード被リンクでSEO効果もあったかもしれませんが、はてな外にいますからその恩恵はありません。じゃあ、はてなブログを使用しているならどうかといえば、最近の検索エンジンは外部リンクが多すぎることに対して厳しくなってきていますから、その効果も今はどれほどなのかとも思います。

ユーザーとしてページを見ると、リンクが多すぎるとうるさい感じがしますし、キーワードリンクをクリックして便利だと思ったことはあまりありません。分からない語句があったらはてな内を探すより「ぐぐる」のが自然だと思います。

今後はてなキーワードが終了したり、使用が変わったりすると、外部のブログからのキーワードリンクは全部リンク切れになってしまいますから、引っ越してきたサイトでキーワードリンクが残ってしまっている状況は百害あって一利なしと思います。

実行にJavaが必要ではありますが、同じ状況の方がいらっしゃったら使っていただいて構いません。ただし、事前にバックアップを取るなど自己責任にてお願いします。MySQLのバックアップは

mysqldump -u ユーザー名 -p DB名 > ファイル名.dump

にて取れます (^^;

コメントを残す