はてなブログライター v0.9

はてなブログライター v0.9 をリリースしました。

変更点

  • 新しいコマンド hbwdl.rb を追加しました。
    • Web側で追加・更新したエントリをローカルにダウンロードするツールです。
    • 詳細は README-hbwdl.md を参照してください。
  • hbw.rb に fix_data_url サブコマンドを追加しました。
    • 0.9 より前のバージョンの hbw.rb で投稿したエントリの投稿データファィルにエントリのURLの情報を追加するものです。
    • 通常は使用する必要はありません。詳細は README.md を参照してください。

注意事項

  • hbwdl.rb はまだバグがあるかもしれません。エントリのバックアップを取るなどして自衛しつつ使用してください。
  • 一部のエントリ(おそらく特殊な日付のエントリ)は hbwdl.rb ではダウンロードできません。

id記法のリンクの修正 & お詫び

はてなダイアリーのインポートで移行した記事ですが、id記法で記述したはてなダイアリーへのリンクが全滅しています。これははてなダイアリーはてなブログではid記法の仕様が異なるせいです。なんでうまいこと変換してくれないかなーと思いつつ一括で修正してみました。

例によって HatenaBlogWriter (hbw) を使うのが前提です。

はてなダイアリーはてなブログでのid記法の違い

詳しくは今村さんのブログエントリを参照してください。

ここには書かれてなかったと思うのですが、もう一つ違いがあります。

はてなダイアリーでは a タグの href 属性値にid記法を書くとURLに展開してくれるのですが、はてなブログでは展開してくれません。はてなブログで有効な d:id:rna:20190128:p1 のようなid記法で書いてもダメです。

idリンクの置換

以下のようなスクリプトを書きました。要は正規表現置換なのですが、正規表現は苦手科目です… 何度も試行錯誤できるように元のエントリファイルをサブディレクトリにコピーして、それを入力として変換するようにしました。

convert-id-links.rb:

#!/usr/bin/env ruby
# coding: utf-8
# HatenaBlogWriter のワーキングディレクトリで実行します。
# orig ディレクトリに変換前のエントリファイルをコピーしてから実行します。

ORIG_DIR = "orig"
TB_SECTION_SEPARATOR = '<!-- trackback -->'

def load_entry_file(filename)
  header = []
  body = []
  tb = []
  File.open(filename) { |file|
    in_header = true
    in_tb = false
    file.each_line(chomp: true) { |line|
      if (line == TB_SECTION_SEPARATOR) then
        in_tb = true
        next
      elsif in_header && /^$/.match(line) then
        in_header = false
        next
      end
      if in_header then
        header.push(line)
      elsif in_tb then
        tb.push(line)
      else
        body.push(line)
      end
    }
  }
  return { :header => header, :body => body, :tb => tb }
end

def dump_entry_file(filename, entry)
  File.open(filename, "w") { |f|
    f.puts entry[:header]
    f.puts ""
    f.puts entry[:body]
    unless entry[:tb].empty? then
      f.puts TB_SECTION_SEPARATOR
      f.puts entry[:tb]
    end
  }
end

def convert_id_links(lines)
  in_super_pre = false
  new_lines = []
  lines.each { |line|
    if in_super_pre then
      new_lines.push(line)
      in_super_pre = false if line.match(/^||<$/)
      next
    end
    if !in_super_pre && line.match(/^>\|\w*\|$/) then
      new_lines.push(line)
      in_super_pre = true
      next
    end
    # id link
    new_line = line.gsub(/((\[\])|(href=['"]\[?)|[^:]|^)(id:([\w-]+):(\d{8})(:[\w]+|#[\w]+)?)(\[\])?/i) { |matched|
      pre, esc_begin, href, link, id, date, sect, esc_end = $~.captures
      unless (esc_begin && esc_end) || href
        url = "http://d.hatena.ne.jp/#{id}/#{date}"
        if sect then
          sect.gsub!(/^:/, "/")
          url += sect
        end
        "#{pre}<a href=\"#{url}\">#{link}</a>"
      else
        matched
      end
    }
    # href: diary
    new_line = new_line.gsub(/href=['"]\[?(d:)?id:([\w-]+)(:[^\]'"]+)?\]?['"]/i) { |matched|
      service, id ,path = $~.captures
      url = "http://d.hatena.ne.jp/#{id}"
      if path then
        path.scan(/^:((\d{8})$|(\d{8})(:[\w]+|#[\w]+)$)/) { |s,date1,date2,sect|
          if date1 then
            url += "/#{date1}"
          elsif date2 then
            sect.gsub!(/^:/, "/")
            url += "/#{date2}#{sect}"
          end
        }
      end
      "href=\"#{url}\""
    }
    # href: group
    new_line = new_line.gsub(/href=['"]\[?g:([\w-]+)(:id:([\w-]+)(:[^\]'"]+)?)?\]?['"]/i) { |matched|
      group, id_path, id, path = $~.captures
      url = "http://#{group}.g.hatena.ne.jp"
      if id then
        url += "/#{id}"
        if path then
          path.scan(/^:((\d{8})$|(\d{8})(:[\w]+|#[\w]+)$)/) { |s,date1,date2,sect|
            if date1 then
              url += "/#{date1}"
            elsif date2 then
              sect.gsub!(/^:/, "/")
              url += "/#{date2}#{sect}"
            end
          }
        end
      end
      "href=\"#{url}\""
    }
    # href: group keyword
    new_line = new_line.gsub(/href=['"]\[?g:([\w-]+):keyword:([^\]'']+)\]?['"]/i) { |matched|
      group, keyword = $~.captures
      url = "http://#{group}.g.hatena.ne.jp/keyword/#{keyword}"
      "href=\"#{url}\""
    }
    new_lines.push(new_line)
  }
  return new_lines
end

def print_diff(src_lines, dst_lines)
  src_lines.each_index { |i|
    if src_lines[i] != dst_lines[i] then
      puts "- #{src_lines[i]}"
      puts "+ #{dst_lines[i]}"
    end
  }
end

check = (ARGV[0] == "check")
Dir.glob("#{ORIG_DIR}/????-??-??_*.txt").sort.each { |src|
  dst = File.basename(src)
  entry = load_entry_file(src)
  body = entry[:body]
  new_body = convert_id_links(entry[:body])
  if (body != new_body) then
    puts "#{src}:"
    print_diff(body, new_body)
    unless check then
      entry[:body] = new_body
      dump_entry_file(dst, entry)
      puts "saved: #{dst}"
    end
  end
}

引数に check を指定するとエントリファイルを置換せずに置換する部分の差分表示だけして終わります。

hbw のエントリファイルのヘッダ部分を読み飛ばすようにしています。また、トラックバック以降作業後の作業ということで、以前の作業で追加したトラックバック部分も読み飛ばすようにしています。

基本的に自分の書き方でヒットするケースのみに対処しているので、人によってはこのままでは足りないかもしれません。またはてなダイアリーはてなグループ用のリンクにしか対応していません。

結果の確認

正規表現置換は思わぬ置換ミスを引き起こすことがあるので、上のスクリプトを実行後、diff を取って差分を目で一通り確認しました。350件分。2時間半くらいかかりました。

修正の実行

hbw を実行しておしまい。

やり残し

確認中に気付いたのですが、a タグの href 属性値に書いたisbn記法やasin記法もはてなブログでは展開してくれないんですね… これは別途対処しようと思います。

もう一つ、フラグメント識別子で日記内のセクションへリンクしている場合(http://d.hatena.ne.jp/rna/20190128#p1 等)、はてなブログに移行したダイアリーでリンク先のセクションに対応するエントリには飛ばないという問題があります。

これははてなの方に要望を投げているとことろです。対応してもらえるとありがたいのですが、ダメだった場合どうするか… 大抵の場合はURLを http://d.hatena.ne.jp/rna/20190128/p1 に置き換えてしまえばエントリに飛ぶんですが、なぜかそうならないブログもあるのです。

例えば上の記事で挙げた飯田さんのブログがそうです。おそらくダイアリーを日記モードで使っているとそうなるのかなとにらんでいますが、元がどっちモードだったかなんてわからないのでどうしたものか…

お詫び

最後に、今回の作業でまたidコールが飛びまくったかもしれません。もしそうでしたらお詫びします。

一応エントリの再編集では追加分のidにしかコールが飛ばないはずなのですが…

でも AtomPub からの編集でもそうなのか不明ですし、差分の取り方によっては追加分と認識されそうなので、やっぱり飛んでしまったかも…

人によっては10年以上も前のブログからコールが飛んできて不快かと思いますが、リンクはWebの命だと思っているのでリンク切れは極力なくしたいのです。どうかお許しください。

はてなダイアリーからのトラックバックの移行(その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件更新しなおすことになったので「無事」ではありませんでしたが…

はてなダイアリーインポート後のリダイレクトがうまくいかない記事がある

トラックバック移行作業時に気付いたのですが、はてなダイアリーのインポート後にリダイレクト設定をしても、一部の記事で間違ったURLにリダイレクトされる現象が発生しています。

具体的には2005年2月29日の記事です。

1つ目のリンクは "Entry is not found" と書かれたページ http://rna.hatenadiary.jp/entry/20050229/p1 に飛び、2つ目のリンクは invalid date とだけ表示されるページ http://rna.hatenadiary.jp/entries/2005/02/29 に飛びます。

…はい、2005年はうるう年ではないので、2月29日なんて日付は本来ありません。しかしはてなダイアリーはそのような日付を許容していました。「はてなダイアリーライター」を使って誤って日付を書き間違えた日記を投稿したところ、エラーにならず2月29日の日記ができてしまったのです。

そのような日付の記事はインポートの際にどうなるかというと、ちゃんと漏れなく取り込まれています。が、前日の2月28日のエントリとして取り込まれていました。その結果1つ目のリンクの記事は http://rna.hatenadiary.jp/entry/20050228/p1 になりました。

ところが、2月28日にはセクション名 p1 の記事が既にあり、通常はこれが http://rna.hatenadiary.jp/entry/20050228/p1 になるはずなのですが… エントリが上書きされてしまうのでは?と思ったのですがそうはならず28日の記事は http://rna.hatenadiary.jp/entry/20050228/p1_1 に取り込まれました。

ここでまた問題が発生します。28日の p1 の記事は以下の URL です。

このリンクを辿ると29日の記事に対応する http://rna.hatenadiary.jp/entry/20050228/p1 に飛ばされてしまいます。困りました。

というわけで、本当はこうなって欲しいわけです。

なんとかなりませんかね?と言ってもテーブルベースでリダイレクトしているならともかく、ルールベースで計算したURLにリダイレクトする仕組みだとお手上げですよね…

一応はてなに報告はしておきますが、どうにもならないかもしれません。

お詫び

このトラックバック移行の件ですが、

ひょっとしてこの作業のせいで大量のidコールが飛んだりしてます…?エントリ更新時は差分にidやURLが含まれる場合のみコールが発生するのかなと思ったけどそうでもないのかな…

いや、そうだとしても、エントリ本文内にトラックバックのデータを追記しているので、はてなダイアリーからトラックバックした人にはもれなくidコールが飛んでいるような…

もしそうでしたらお騒がせしてすみませんでした。idコールのことはすっかり忘れていました。。。

トラックバック移行完了しました(はずです…)

はてなダイアリーのインポートは昨日の昼までに完了しました。昼にリダイレクト設定までやって通常の移行作業は完了。

トラックバックの移行は昨夜帰宅後徹夜で作業して先ほど完了しました。完了したはずです… たとえばこのエントリ http://rna.hatenadiary.jp/entry/20120310/p1 みたいに表示されます。

いくつか想定外があって若干手戻りがあったり、一部エントリについては移行を諦めたり、一部手作業も入りましたが、なんとか移行できました。更新したエントリは1244件。作業メモは明日までにアップする予定です。

しかしトラックバック以前に id 記法のリンクがほぼ全滅してるのなんとかしないと…

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

はてなダイアリーのインポート、まだ終わりません。インポートの終わったエントリは順次公開されているようですが、昨夜から投稿数1751件で止まっています。インポート前からあるエントリを差し引くと1700エントリくらいで、400件くらい残っているようです。徐々に進んでいるのならわかるんですが途中でピタッと止まった状態なのが気になりますが…

トラックバックデータの抽出のやり直し

まずこのシリーズの最初のエントリスクリプトにバグがありました。入力が HTML、出力が YAML のはずだったのですが、出力を YAML パーサで正しく読み込めない場合がありました。

YAML の出力を文字列で出していたせいで YAMLマークアップエスケープ処理が抜けていたせいです。具体的にはトラックバックのデータ(a 要素)の中に ": " が混じっているとハッシュとしてパースされてしまって、場合によってはエラーになるというもの。

出力をメモリに溜め込むのは趣味じゃないんですが、オブジェクトにデータを溜めてから YAML.dump() で出力するように修正しました。

まず各エントリ(日記のセクション)の HTML ファイルからトラックバックを抽出するスクリプトがこちら。

get-tb.rb:

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

require 'yaml'
require 'nokogiri'

URL_BASE = "http://d.hatena.ne.jp"

def parse_entry_html_file (file)
  entry = {}
  f = File.open(file, "r:euc-jp")
  html = f.read().encode("utf-8", { :invalid => :replace, :undef => :replace })
  f.close
  doc = Nokogiri::HTML::Document.parse(html)
  path = doc.css("div.body > div.section > h3 > a").attribute("href").value
  entry['url'] = URL_BASE + path
  doc.css("div.refererlist").each { |div|
    type = div.css("div.caption > a").attribute("name").value
    tbs = []
    div.css("ul > li").each { |li|
      tbs.push(li.css("a").to_s)
    }
    entry[type] = tbs if tbs.length > 0
  }
  return entry
end

entries = []
ARGV.each { |file|
  begin
    entries.push(parse_entry_html_file(file))
  rescue => e
    STDERR.puts "#{file}: #{e}"
  end
}
YAML.dump(entries, STDOUT)

その2で書いた、匿名セクション(日記ページの最初のセクションの前ある文章)がある場合にその日記に付いたidトラックバックを抽出するスクリプトも同様に修正しました。

get-tb-for-anon-section.rb:

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

require 'yaml'
require 'nokogiri'

def parse_diary_html_file (file)
  f = File.open(file, "r:euc-jp")
  html = f.read().encode("utf-8", { :invalid => :replace, :undef => :replace })
  f.close
  doc = Nokogiri::HTML::Document.parse(html)
  first_section = doc.css("div.body > div.section")[0]
  if first_section && first_section.css("h3").length == 0 then
    entry = {}
    entry['url'] = doc.css("div.day > h2 > a").attribute("href").value
    doc.css("div.refererlist").each { |div|
      type = div.css("div.caption > a").attribute("name")
      tbs = []
      div.css("ul > li").each { |li|
        tbs.push(li.css("a").to_s)
      }
      entry['idtb'] = tbs if tbs.length > 0
    }
    return entry
  else
    return nil
  end
end

entries = []
ARGV.each { |file|
  begin
    entry = parse_diary_html_file(file)
    entries.push(entry) if entry
  rescue => e
    STDERR.puts "#{file}: #{e}"
  end
}
YAML.dump(entries, STDOUT)

トラックバックの付いているエントリのデータだけを抽出する

get-tb.rb と get-tb-for-anon-section.rb で抽出したデータにはトラックバックの付いていないエントリのURLも含まれます。処理したエントリの件数などを把握するためにそうしておいたのですが、今後の処理では邪魔になるので以下のスクリプトで不要な分を削除します。

reject-empty-trackback.rb:

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

require 'yaml'

YAML.load_stream(ARGF) { |entries|
  entries.reject! { |entry|
    tbs = entry['tb']
    idtbs = entry['idtb']
    (tbs == nil || tbs.empty?) && (idtbs == nil || idtbs.empty?)
  }
  YAML.dump(entries, STDOUT)
}

エントリのURL一覧を生成する

reject-empty-trackback.rb で処理したデータから URL のリストを生成します。こういうのは普通のテキストファイルでもいいんですが、YAML で出力しています。

2019-01-25: 入力に複数のYAMLドキュメントが含まれると出力が重複するバグがあったので修正しました。

get-urls.rb:

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

require 'yaml'

urls = []
YAML.load_stream(ARGF) { |entries|
  entries.each { |entry|
    urls.push(entry['url'])
  }
}
YAML.dump(urls, STDOUT)

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

ダイアリーのインポート後にリダイレクト設定をすると、ダイアリーのURLでアクセスした際に対応するブログのエントリにリダイレクトするようになります。これを利用してダイアリーのエントリのURLと移行後のブログのエントリの対応表を作ります。

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

make-redirection-table.rb:

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

require 'yaml'
require 'net/http'

def get_redirect_location(http, url)
  res = http.head(url)
  raise "#{res.code}: #{url}" if (/^[45]../.match(res.code))
  return (res.code == "301") ? res["location"] : nil
end

rnd = Random.new
Net::HTTP.start("d.hatena.ne.jp", 80) { |http|
  table = {}
  YAML.load_stream(ARGF) { |urls|
    urls.each { |url|
      location = get_redirect_location(http, url)
      table[url] = location if location
      sleep(0.5 + rnd.rand(0.5))
    }
  }
  YAML.dump(table, STDOUT)
}

はてなのサーバに連続してアクセスするのでちょっと wait を入れてあります。

今後の作業予定

  • インポート完了を待つ
  • リダイレクト設定
    • 匿名セクションに対応するエントリのURLの確認
  • URL対応表の作成
  • はてなブログライター形式でブログエントリを全件ダウンロード
  • ダウンロードしたエントリファイルの末尾にトラックバックを追加
  • はてなブログライターでエントリを更新