はてなダイアリーのインポート、まだ終わりません。インポートの終わったエントリは順次公開されているようですが、昨夜から投稿数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も含まれます。処理したエントリの件数などを把握するためにそうしておいたのですが、今後の処理では邪魔になるので以下のスクリプトで不要な分を削除します。
#!/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 を入れてあります。