はてなダイアリーからのトラックバックの移行(その4)(完)

昨日の昼頃にはてなダイアリーのインポートが完了し、その後ブックマークの移行設定とリダイレクト設定をしてインポートが完了しました。その後の作業のメモです。

ダイアリーのURLとブログのURLの対応表を作る(続き)

リダイレクトが有効になったので前回作成した make-redirection-table.rb で対応表を作りました。が、2点問題が。まず、この件なんですが、

でも匿名セクションに対応したエントリって対応する元のURLがないんですけどどうしますかね? おそらく日記のURLに対応するURLに何か付け足したURLになると思うのですが。これはリダイレクト設定後要確認ですね。

日記ページのURLのリダイレクト先を確認したところ、匿名セクションに対応するエントリのURLではなく、その日一日分のエントリをまとめたページに飛んでいました。一方日記ページの URL とその日記の匿名セクション由来のエントリの URL との対応関係は以下のようになっていました。

http://d.hatena.ne.jp/はてなID/YYYYMMDD → http://ブログドメイン/entry/YYYYMMDD

make-redirection-table.rb で作った対応表をこのルールに合わせて修正するスクリプトを以下に。

fix-redirection-table.rb:

#!/usr/bin/env ruby
# coding: utf-8

require 'yaml'

redirection_table = YAML.load_file(ARGV[0])

new_table = {}
redirection_table.each { |diary_url, blog_url|
  if /^http:\/\/d.hatena.ne.jp\/[^\/]+\/([0-9]+)$/.match(diary_url) then
    date = $1
    /^(https?:\/\/[^\/]+)\//.match(blog_url)
    blog_url = "#{$1}/entry/#{date}"
  end
  new_table[diary_url] = blog_url
}
YAML.dump(new_table, STDOUT)

さらにリダイレクトの不具合(制限?)で一部のエントリのリダイレクト先が間違っているという問題がありました。

これは機械的に置き換えることができませんし、3件だけなので手動で対応表を修正することで対応しました。

インポートしたエントリを hbw 形式でダウンロード

インポートしたエントリはサーバ上にしか存在しないため、そのままでは HatenaBlogWriter (hbw) で修正できません。

そこで hbw の hbw-downloader ブランチで hbwdl.rb というスクリプトを作りました。これを使います。

hbw のワーキングディレクトリで以下を実行します。ただし、ローカルで修正したエントリファイルが残っている場合は先に更新しておきます。

hbwdl.rb 0

引数は最新から何件までダウンロードするかの指定ですが、0 は全件ダウンロードの指定になります。

ここで2つ問題が出ました。hbwdl.rb は Atom のフィードを取得してエントリを切り出して保存するのですが、フィードに全件含まれていないようなのです。具体的には 1789 年の1件(1件中)、0001 年の13件(15件中)が含まれていませんでした。

また、0001 年の2件は ruby の atomutil での解析に失敗しました。

title:  ping 送信しないテスト(hw.pl 1.0.1)
エントリの解析に失敗しました。: invalid date: "0001-01-01T00:00:00+09:18:59"
---
title:  ping 送信しないテスト(hw.pl 1.0.1+patch)
エントリの解析に失敗しました。: invalid date: "0001-01-02T00:00:00+09:18:59"

ATOM エントリの編集日時を解析するところでエラーになっています。てっきり1970年より前の日付だからダメとか?と思ったら、そうではなくて、"0001-01-02T00:00:00+09:18" だと解析できるようです。単純に iso8601 に準拠していない日付書式で書かれているせいでエラーになるようです。これはスクリプト側での対応は困難ですね…

これらのエントリは1789年の1件を除いて「はてなダイアリーライター」のテスト用の記事ですし、重要度は低いので今回は諦めることにしました。

エントリのURLとエントリファイルの対応表の作成

hbwdl.rb はローカルにないエントリのエントリファイルを新規作成する際、およびリモート側で修正されたエントリのエントリファイルを(ユーザが応答して)更新する際に、data ファイルにエントリのURLを保存します。

これを利用して以下のスクリプトでエントリのURLとエントリファイルの対応表を作成します。これは hbw のワーキングディレクトリで実行します。

make-url-to-entryfile-table.rb

#!/usr/bin/env ruby
# coding: utf-8
# HatenaBlogWriter のワーキングディレクトリで実行します。

require 'yaml'

DATA_DIR = "data"

table = {}
Dir.glob("*.dat", base: DATA_DIR).map() { |data_filename|
  entry_filename = nil
  if /^(.+)\.dat$/.match(data_filename)
    entry_filename = $1
  else
    next
  end
  data = YAML.load_file("#{DATA_DIR}/#{data_filename}")
  if data['url'] then
    table[data['url']] = entry_filename
  end
}
YAML.dump(table, STDOUT)

エントリファイルにトラックバックを追加する

いよいよエントリファイルを修正します。不測の事態に備えてあらかじめ hbw のワーキングディレクトリを丸ごとバックアップしておきます。

トラックバックをどう表示するか考えものですが、とりあえずエントリの末尾に普通に表示します。末尾と言っても注釈記法を使うと注釈の方が後ろになるという問題があるのですが、今回はそこは目をつぶります。JavaScript で DOM いじって表示するとかしないと対処できないので…

以下のスクリプトを使いますが、引数には三つのファイルを渡します。

  1. 抽出したトラックバックデータ
    • get-tb.rb の出力と get-tb-for-anon-section.rb の出力を reject-empty-trackback.rb に入力して得た出力
  2. ダイアリーのURLとブログのURLの対応表
    • make-redirection-table.rb の出力を fix-redirection-table.rb に入力して得た出力(を必要に応じて手動で修正したもの)
  3. エントリのURLとエントリファイルの対応表
    • make-url-to-entryfile-table.rb の出力

append-tb-section.rb:

#!/usr/bin/env ruby
# coding: utf-8
# HatenaBlogWriter のワーキングディレクトリで実行します。

require 'yaml'

TB_SECTION_SEPARATOR = '<!-- trackback -->'

def load_entry_file_body(filename)
  entry_body = []
  File.open(filename) { |file|
    file.each_line(chomp: true) { |line|
      break if (line == TB_SECTION_SEPARATOR)
      entry_body.push(line)
    }
  }
  return entry_body
end

def make_backup_file(filename)
  File.rename(filename, "#{filename}.bak")
end

def print_tb_list(f, tb_list)
  tb_list.each { |tb|
    one_line_tb = tb.gsub(/[\r\n\t ]+/, " ")
    f.puts "- #{one_line_tb}"
  }
end

def print_tb_lists(f, tb_entry)
  if tb_entry['tb'] then
    f.puts '[]トラックバック[]:'
    print_tb_list(f, tb_entry['tb'])
  end
  if tb_entry['idtb'] then
    f.puts '[]idトラックバック[]:'
    print_tb_list(f, tb_entry['idtb'])
  end
end

def print_tb_section(f, tb_entry)
  f.puts '<hr><div class="referer" style="font-size:75%;background-color:#eee;">'
  print_tb_lists(f, tb_entry)
  f.puts '</div>'
end

def print_entry (f, lines, tb_entry)
  lines.each { |line|
    f.puts(line)
  }
  f.puts TB_SECTION_SEPARATOR
  print_tb_section(f, tb_entry)
end

def print_entry_file(filename, tb_entry)
  lines = load_entry_file_body(filename)
  print_entry(STDOUT, lines, tb_entry)
end

def save_entry_file(filename, tb_entry)
  lines = load_entry_file_body(filename)
  make_backup_file(filename)
  File.open(filename, "w") { |f|
    print_entry(f, lines, tb_entry)
  }
end

tb_filename = ARGV[0]
redirection_table_filename = ARGV[1]
entry_file_table_filename = ARGV[2]

blog_urls = YAML.load_file(redirection_table_filename)
entry_files = YAML.load_file(entry_file_table_filename)

File.open(tb_filename) { |tb_file|
  YAML.load_stream(tb_file) { |entries|
    entries.each { |entry|
      diary_url = entry['url']
      blog_url = blog_urls[diary_url]
      unless blog_url
        puts diary_url + ": blog entry not found."
        next
      end
      entry_filename = entry_files[blog_url]
      unless entry_filename
        puts diary_url + ": entry file not found."
        next
      end
      puts diary_url + ": " + entry_filename
      #print_entry_file(entry_filename, entry)
      save_entry_file(entry_filename, entry)
    }
  }
}

修正したエントリのアップロード

ここまできたらいよいよエントリを更新します。hbw-downloader ブランチの hbw.rb でアップロードしますが、まず一度 hbw.rb check で修正されたエントリファイルの件数を確認します。問題なければ引数なしの hbw.rb を実行。

hbw.rb
新規エントリファイルはありません。
修正されたエントリファイルが 1244 件あります。
OK: 2004-06-22_01.txt: エントリを更新しました。
OK: 2014-02-06_01.txt: エントリを更新しました。
OK: 2004-10-21_06.txt: エントリを更新しました。
...

hbw-downloader ブランチの hbw.rb は速度規制(1件毎に1〜1.5秒のウェイト)が入っているのでややゆっくり目ですが黙々とアップロードが進んでいき、30分ほどで無事更新が完了しました。*1

修正されたエントリはこんな感じになりました。

ということで、これでトラックバック移行作業は完了です。

わかりにくい部分も多々ありますが、スクリプトの処理内容が理解できる人だけがわかるような書き方にしています。他人の環境での動作保証はできないのでよく理解した上で自己責任で実行してください。

あと、これは終わってから気付いたんですが、この作業やると、idコールむちゃくちゃ飛びますね…

改めまして、申し訳ありませんでした。

あと、はてなさん、訴えないでくださーい…

librahack 事件みたいなことになったらやだなー…

idコールは回避方法がなさそうなので参考にする人はそのあたりはご覚悟を。

*1:実はエントリファイルの修正が一部しくじっていてスクリプトを直して再実行して、150件更新しなおすことになったので「無事」ではありませんでしたが…