はてなブログに引っ越しました!

こんにちは、久保田です。

さて、我々Adwaysエンジニアブログですが、デザインリニューアルも兼ねて、
実はこっそりライブドアブログからはてなブログに移行致しました。

今回はこの移行に至るまでの軌跡をお話したいと思います。

まず、前提として、ブログ移行に関して以下のような課題がありました。

- ドメインは「blog.engineer.adways.net」から変えない。  
- 過去のブログの記事は捨てない。  
- ドメインは変わらないが、パスがどうしても変わってしまうので、過去のリンクを辿ってきた人をリダイレクトさせる。  

以上を踏まえて、やるべき作業は以下のようになりました。

- デザインの決定   
- 記事、画像の移動  
- 過去のリンクを踏んだユーザーをリダイレクトをさせるjsの用意  
- ドメインの移管  

今回は、デザインの決定、ドメインの移管以外の2つのお話をさせていただきます。

- 記事、画像の移動    
- 過去のリンクを踏んだユーザーをリダイレクトをさせるjsの用意    

のお話をさせていただきます。

記事、画像の移動

まず、旧ブログに投稿した記事を全て新ブログに移動させます。

ブログは、大抵の場合記事データのエクスポート機能とインポート機能があり、フォーマットに従っていれば簡単に移動ができます。

しかし、問題になるのは、画像です。

旧ブログにいつまでも画像を残しておくわけにはいかないので、画像を新ブログにアップロードし直す必要があります。
そうなると、記事データに書かれた画像のパスも書き換えてあげないといけないわけです。
しかし、画像をすべて手動でアップロードし、手動でパスを書き換えるというのは現実的ではないので、幾つかのプログラムを通して、新しいブログにインポートする記事のデータを作ります。

その点を踏まえた上で、作業は以下のような手順で行います。
1, 旧ブログから記事データをエクスポート
2, 旧ブログから全画像をダウンロード
3, 新ブログに全画像をアップロード&新画像パスの取得
4, 1でエクスポートした記事の画像のパスを3でアップロードした画像のパスに置換
5, 4で画像のパスを置換した記事データを新ブログにインポート

このステップをこなせば、画像も一緒に移動できるはずです。

旧ブログから記事データをエクスポート

これはブログの管理画面でできるはずです。
ライブドアブログでは、設定 > エクスポート と画面を辿っていくと、エクスポートができました。

旧ブログから全画像をダウンロード

画像のダウンロードは、先ほど取得した記事データを使用します。
記事データから画像のパスを抜き出し -> ダウンロードする
という手順です。
この作業は、PythonのBeautiful Soapというモジュールがうまくはまりそうだったので採用しました。

from bs4 import BeautifulSoup
from urllib import request
import re
import os.path
import wget
from urllib import request

# 画像のダウンロード
def downloadImageFromInternet(source, output):
    try:
        response = request.urlopen(source)
        if response.code != 200:
            return -1
        with open(output, "wb") as fout:
            fout.write(response.read())
        return 11
    except Exception as err:
        print("NotFoundError!!! : ", source)

# main処理
f = open("backup.txt") # 記事データをopenする
blogs = f.read().split("-----\n--------") # 記事データを一つの記事ごとに分ける
n = 0
for i, blog in enumerate(blogs):
    imgs = BeautifulSoup(blog).find_all('img') # omgタグを探す
    print("blog No,{}: ".format(i + 1))
    if not os.path.exists('./images/{}'.format(i+1)):
        os.mkdir('./images/{}'.format(i+1))
    for img in imgs:
       m = re.match("http://livedoor.blogimg.jp/adways04/imgs/.*/.*/(.*)", img["src"]) # 対象の画像かを確認
       if m:
           n += 1
           src = img['src']
           print("{} -> ./images/{}/{}".format(src, i + 1,m.group(1)))
           downloadImageFromInternet(src, './images/{}/{}'.format(i + 1, m.group(1))) # 記事ごとに分けて画像を保存しておく

新ブログに全画像をアップロード&新画像パスの取得

次は新ブログに画像をアップロードします。 これを行うために、はてなフォトライフのAPIを使用しました。

はてなフォロライフAPI経由で画像をアップロードするには、OAuthを行う必要があります。(WSSEは使えなくなっていました。)
なので、OAuthの勉強も兼ねて、OAuthのクライアントサイドを実装してみました。
せっかくなので、ドキュメントは RFC5849しか見ない、という縛りも加えました。
辛かったですが、これくらいできないとダメだと感じ、頑張りました。

流れとしましては、 http://developer.hatena.ne.jp/ja/documents/auth/apis/oauth/consumer このページの通りですが、

アプリをはてなに登録、consumer_key と consumer_secretを取得  
↓  
consumer_key と consumer_secretを使って、access_tokenを取得  
↓  
access_tokenを使って、https://www.hatena.ne.jp/oauth/authorize?oauth_token={access_token}にアクセスして、verifierを取得  
↓  
verifierを使って、oauth_tokenとoauth_token_secretを取得  
↓  
oauth_tokenとoauth_token_secretを使って、はてなフォロライフAPIにアクセス、画像のアップロード  

と、なります。

少し長めなので、gistをはっておきます。

oauth_tokenの取得

画像のアップロード

一つ目のgistを動かしてoauth_tokenとoauth_token_secretを取得、
二つ目のgistで取得したoauth_tokenとoauth_token_secretを使ってapiアクセス、画像アップロードをしています。

幾つかはまりどころがあったので、お話しします。

はまりどころ

oauth_tokenの取得

苦労した点としては、毎回signatureを作る必要があるのですが、少しでも間違えるとsignatureエラーが出るだけで、何が間違っているのかわからない、という点でした。
例えば、すでにエンコード済みのoauth_tokenなどはエンコードする必要がありません。なので、oauth_tokenを再度エンコードすると、エラーになります。

画像のアップロード

こちらは一つだけハマったのは、僕の実装だと、OauthCli.newした時にタイムスタンプを作ってそれをヘッダーに乗せていたのですが、それだとアップロードの最中にタイムスタンプが有効期限切れになってしまった点です。

そしてアップロードした後は、アップロードした画像と新パスの対応付けのため、redisにデータを保存しています。

後の詳細はコードを見ていただければわかると思います。

1でエクスポートした記事の画像のパスを3でアップロードした画像のパスに置換

次はエクスポートした記事の中の画像パスを新しい画像のパスに置換します。
ここで先ほどredisに保存したデータを使います。
大まかな流れは、

ブログを1つずつ分ける  
↓   
redisのデータから、画像のパスを置換  
↓   
配列に入れておく  
↓  
配列に入れた記事をすべてつなげて、ファイルに書き込む
require 'redis'

redis = Redis.new
f = File.open('./backup.txt')
blogs = f.read().split("-----\n--------")
new_blogs = []

blogs.each.with_index(1) do |blog, i|
  img_srcs = blog.scan(/img.*?src=\"(.*?)\"/)
  img_srcs.flatten.each do |src|
    dst_src = redis.get("#{i}/#{src.match(/.+\/([\w-]+\..+)$/)[1]}")
    if dst_src
      p "#{i}: #{src} => #{dst_src}"
      blog = blog.sub(src, dst_src)
    end
  end
  new_blogs << blog
end


File.open("backup-1.txt", "w") do |f|
  f.puts(new_blogs.join("-----\n--------"))
end

特に難しいところはありません。正規表現が大変だったなーくらいです。
これで新しいブログにインポートする記事のデータが完成しました。

4で画像のパスを置換した記事データを新ブログにインポート

ここまできたら後ははてなブログの管理画面で先ほど作った記事データをインポートします。
これが完了すると、画像のパスも書き換わった状態の記事が新ブログに反映されます! これで新ブログが完成です。

過去のリンクを踏んだユーザーをリダイレクトをさせるjsの用意

僕らのブログは、Adways Engineers Diaryなど、外部に幾つかリンクを持っています。
それらの外部リンクには、過去のブログのパスがはってあります。
しかし新ブログに移行するにあたり、パスが変わってしまったので、それらのリンクがリンク切れを起こしてしまいます。 (http://blog.engineer.adways.net/archive/4444444.html -> http://blog.engineer.adways.net/entry/4444444.html という形になります。)

なので今回は、エラーページにjsを仕込んでおき、エラーページのURLと対応した記事ページがあったら、リダイレクトさせることにしました。

そのために、

- 旧記事と新記事のパスの対応表の作成  
- エラーページで発火するjsの実装  

が必要になります。

旧記事と新記事のパスの対応表の作成

この処理で、対応表をjson形式で吐き出すようにして、jsに渡します。
対応表を作るために、旧記事と新記事の記事データがまた必要になります。
両方の記事データからパスの部分を抜き出す必要があるからです。

なのでまた記事データをブログからエクスポートして用意します。
そして以下のRubyプログラムに食わせます。

require 'json'

key_paths   = File
                .open('./backup-livedoor.txt')
                .read
                .scan(/PATH: (.*)\n/)
                .flatten
                .map do |path|
                  "http://blog.engineer.adways.net/#{path}"
                end

value_paths = File
                .open('./backup-hatena.txt')
                .read
                .scan(/BASENAME: (.*)\n/)
                .flatten
                .reverse
                .map do |path|
                  "/entry/#{path}"
                end

table = key_paths.zip(value_paths).to_h.to_json

p key_paths.size
p value_paths.size
File.open("table.json", "w") do |f|
  f.puts(table)
end

こうするとルーティングを管理する対応表ができます。
後はここでできたjsonをjsにはって、

<script>
  if(document.querySelectorAll('.no-entry').length){
      // route_tableにjsonを貼る   
      route_table = {"http://blog.engineer.adways.net/archives/5094288.html":"/entry/5094288","http://blog.engineer.adways.net/archives/14407148.html":"/entry/14407148","http://blog.engineer.adways.net/archives/5441933.html":"/entry/5441933",

…snip…

,"http://blog.engineer.adways.net/archives/49447083.html":"/entry/2016/09/03/014433"}
      
      url = location.protocol + "//" + location.host + location.pathname;
      if(route_table[url]) {
        location.href = "http://blog.engineer.adways.net" + route_table[url];
      }
  }
</script>

はてなブログの管理画面のデザイン > フッタのところにコピペして保存すればOKです!

これで昔のリンクを踏んできたユーザーが移行した記事にリダイレクトされます。

まとめ

これでDNSの設定を変え、ドメインをはてなブログに設定すれば、移行は完了です。
OAuthの実装がかなり山だったな・・・と感じております。

いろいろとありましたが、無事我らAdwaysEngineersBlogは、はてなブログに移行が完了しました。

今後も更新頑張っていくので、よろしくお願いいたします。