歩いたら休め

If the implementation is easy to explain, it may be a good idea.

【Python】音楽ブログをFC2からはてなに移転しました

音楽ブログをFC2ブログからはてなブログに移転しました。

sakana38.hatenablog.com

最近のFC2アカウントの凍結騒ぎ等、FC2がいろいろとキナ臭そうなので。Amazonリンクが使えなくなったりと、年々サービスとして使いづらくなってましたし。

news.yahoo.co.jp

基本的にやったことは以下の3ステップです。

  1. FC2からデータをエクスポート
  2. 画像や他の記事へのサイト内リンクを新サイトに置き換え
  3. はてなにインポート
  4. (旧記事から新記事へのリンク設置)

1. FC2からデータをエクスポート

基本的に困ることは無いと思います。「データバックアップ」から「全ての記事」のデータをエクスポートしましょう。

http://admin.blog.fc2.com/control.php?mode=control&process=backup

こんな感じのテキストファイル(自分の場合「sciandeng.txt」)がダウンロードできます。

AUTHOR: 黒めだか
TITLE: はじめまして!!
STATUS: Draft
ALLOW COMMENTS: 1
CONVERT BREAKS: default
ALLOW PINGS: 1
PRIMARY CATEGORY: 雑記
CATEGORY: 雑記

DATE: 04/12/2009 15:46:50
-----
BODY:
はじめまして<img src="http://blog-imgs-1.fc2.com/image/i/179.gif"  class="emoji" style="border:none;" />
大学に入り自分で生活するようになり、その記録を残しておきたいと思い、ブログをはじめました<img src="http://blog-imgs-1.fc2.com/image/i/239.gif"  class="emoji" style="border:none;" />
今は大学の寮に入り、友達とルームシェアしながら暮らしています<img src="http://blog-imgs-1.fc2.com/image/i/38.gif"  class="emoji" style="border:none;" />

2. 画像や他の記事へのサイト内リンクを新サイトに置き換え

面倒な作業です。(もし同じような作業をする人がいるなら)画像リンクがFC2のままで気にならないならこの作業は必要ないでしょう。

こういう作業の自動化にはプログラミング言語が便利です。私はお手軽スクリプト言語Python(Python3.4.3)を利用しました。作業はブラウザ上のコンソールでPythonが動かせるjupyter(旧ipython notebook)で、インタラクティブに作業をしました。

画像のリンクの修正

FC2にアップされた画像リンクを、はてなフォトライフにアップロードし直し、リンクを書きなおしてあげましょう。

ひどいコードですが、一応載せておきます。

まずは、先ほど落としてきたテキストファイル(sciandeng.txt)の中から、画像URLを抽出します。

import re
from urllib import request
from functools import reduce

# 正規表現でurlを抽出する
r = re.compile("https?://[\w/:%#\$&\?\(\)~\.=\+\-]+")
new_base_url = 'http://sakanafish.hatenablog.com/'

def search_url(txt):
    return r.findall(txt)

def get_picture(img_url, num): # 画像の名前が被ったときのために、ファイル名に数字も入れておく(1_example.pngみたいに)
    img = request.urlopen(img_url)
    img_path = 'img/%i_%s'%(num, img_url.split('/')[-1]) # 作業フォルダに用意したimg/ディレクトリに画像を吐き出す
    with open(img_path, 'wb') as i:
        i.write(img.read())
    return img_path

def get_pictures():
    with open('./sciandeng.txt') as f:
        urllist = reduce(bind_urllist, map(search_url, f))
        pictures = list(filter(lambda x: re.match(r".*(gif|jpg|png|bmp|jpeg)", x) and 'fc2' in x, urllist))
        pictures_unique = sorted(set(pictures), key=pictures.index)
    for i, p in enumerate(pictures_unique):
        try:
            get_picture(p, i)
            # 実際はここで1秒程度sleepさせるべき
        except:
            print(p) # 画像の取得に失敗した場合、画像のURLを出力する

get_pictures() # 実行!

ブラウザ上からはてなフォトライフに一括アップロードします。

一括アップロードした後は、FC2とはてなフォトライフのURLを紐付けるcsvファイルを手作業で用意してあげます。画像ファイル自体は200枚程度だったのでなんとかなりました。

f:id:takeshi0406:20151213100954p:plain

↑のファイルに従って、sciandeng.txtの中身をひたすら文字列置換して新しいファイル(new_sciandeng.txt)に保存しなおしてあげます。

with open('data2.csv', 'r') as f:
    urllist = ([x.replace('\n', '').split(',') for x in f])
    
with open('./sciandeng.txt', 'r') as inputfile:
    with open('./new_sciandeng.txt', 'a') as outputfile:
        for line in inputfile:
            if search_url(line) != [] and 'fc2' in line:
                for u in urllist:
                    line = line.replace(*u)
            outputfile.write(line)

こんなことをしなくても、はてなフォトライフAPIを使えばアップロード時に、新しい画像のURLを取得できそうです。ただ、APIの使い方がすぐに学べそうになかったので、少々面倒な手段でやりました。

はてなフォトライフドキュメント一覧 - Hatena Developer Center

サイト内リンクの修正

タイトルをキーにして、画像リンクと同様、新記事と旧記事のURLを結合してあげます。

from datetime import datetime
from bs4 import BeautifulSoup

# はてなブログは投稿時間でURLが決まるので、sciandeng.txtを解析してタイトルと新URLを紐付ける
# 実際はリストではなくジェネレーターです
def get_post_date():
    with open('./sciandeng.txt') as f:
        title = None
        is_comment = False
        for line in f:
            if re.match(r'TITLE:', line.strip()):
                title = line.replace('TITLE: ', '').replace('\n', '')
            elif re.match(r'COMMENT:', line.strip()):
                is_comment = True
            # コメント欄の投稿日時も取得してしまうので、AUTHORが自分のとき以外は読み込まないようにフラグを立てる
            elif "AUTHOR: 黒めだか" in line: 
                is_comment = False
            elif re.match(r'DATE: ', line) and is_comment == False:
                timestamp = datetime.strptime(line.replace('DATE: ', '').strip(), '%m/%d/%Y %H:%M:%S')
                yield (title, timestamp.strftime( '%Y/%m/%d/%H%M%S'))

# FC2のアーカイブ( http://sciandeng.blog38.fc2.com/archives.html )を解析し、タイトルと旧URLを紐付ける
def get_post_url():
    for i in range(1, 8): #7ページまで存在
        html = request.urlopen('http://sciandeng.blog38.fc2.com/archives.html?p=%i'%i)
        soup = BeautifulSoup(html)
        for s in soup.find(class_="list_body").findAll('li'):
            yield (s.find('a').text, s.find('a').get('href'))

# リスト内包表記を使って結合
def to_new_url():
    post_dates = list(get_post_date())
    post_urls = list(get_post_url())
    return [(u[1], new_base_url + 'entry/' + d[1]) for d in post_dates for u in post_urls if d[0] == u[0]]

# 画像リンクの場合と同じようにリンクの置き換え
urllist = to_new_url()
with open('./new_sciandeng.txt') as inputfile:
    with open('./new2_sciandeng.txt', 'a') as outputfile:
        for line in inputfile:
            if search_url(line) != [] and 'fc2' in line:
                for u in urllist:
                    line = line.replace(*u)
            outputfile.write(line)

置き換え残したリンク

これでほぼ全てのリンクを置き換えることができたのですが、カテゴリページへのリンク等はやり残してしまっています。これはそれほど数が多くないので手作業で直します。

雑記 - 今私は小さな魚だけれど

3. はてなにインポート

置き換えたファイル(./new2_sciandeng.txt)をはてなブログにインポートしましょう。

4. (旧記事から新記事へのリンク設置)

FC2のテンプレートを利用し、各記事のトップに新記事へのリンクを設置してみました。本当はリダイレクトさせるべきでしょうが、無料サービスでは難しそうなので断念。

f:id:takeshi0406:20151213102849p:plain

↑こんな感じ。

<p id="for_canonical"><BIG><a href="http://sakana38.hatenablog.com/entry/<%topentry_year>/<%topentry_month>/<%topentry_day>/<%topentry_hour><%topentry_minute><%topentry_second>">※当ブログははてなブログに移転しました。<br/>ブックマークの移動等よろしくおねがいします。</a></BIG></p><br />

SEO的な部分も対策したかったのですが、トップページ宛にcanonicalタグを埋め込むくらいしかできませんでした。Google様の裁きを待ちます。