MyWeb から Yahoo! ブックマークに移行しました
先日の記事でお伝えしたとおり、これまで今週の話題でご紹介したネタを整理するのに使っていたソーシャルブックマーク・サービス Yahoo! MyWeb が 3 月で終了することになってしまいました。また、 MyWeb の前に使っていた Google Notebook も開発が終了していつ閉じられてもおかしくない状態です(トホホ)。
このままではせっかく 3 年かけて積み上げてきたデータが、水泡ならぬデータセンターの廃熱に帰してしまうので、日曜日に丸一日かけて Yahoo! Japan の Yahoo! ブックマークに全データを移しました。結果的にレスポンスが向上し、 UI も日本語になったので良かったなと思っています。やはり米 Yahoo! と違って、日本の Yahoo! は安心感がありますね(笑)。ついでに今週の話題のインデックスページに主なカテゴリのリンクも加えました。使える時間も限られているので見た目はイマイチですが、少しは使いやすくなったのではないでしょうか。
本日は、前述のデータ移行の方法と、それに使った Ruby スクリプトなどをご紹介します。あまりいないかもしれませんが、 MyWeb や Google Notebook から Yahoo! ブックマークに移行しようとしている方の参考になればと思います。
MyWeb からの以降
まずは MyWeb からの移行です。 MyWeb のエクスポート機能はダウンロードしても空の HTML が出てくるだけなので(ォィ)、 MyWeb と共通のバックエンドを利用している米 Yahoo! の Yahoo! Bookmarks からエクスポートしました。 XML 形式ならタグの情報も正しくエクスポートできます。
そして、エクスポートした XML を以下のスクリプトで Delicious API と同じ形式に変換し、 Yahoo! ブックマークのインポート機能でインポートすれば完了です。
#! /usr/bin/ruby # -*- coding: utf-8 -*- KCODE='UTF8' require 'rexml/document' require 'time' exported_data = IO.read('myweb.xml') exported_data.gsub!(/<URL>([^<]*)<\/URL>/u) do url = $1.gsub(/&(\w+)(?!;)/u, '&\1').gsub(/&\z/u,'') "<URL>#{url}</URL>" end entities = { "alpha" => 'α', "beta" => 'β', "Sigma" => 'Σ', "omega" => 'ω', "acute" => '´', "rarr" => '→', "phi" => 'φ', "times" => '×', "euml" => 'ë', "hellip" => '…', "hArr" => '⇔', "forall" => '∀' } exported_data.gsub!(/&(\w+);/u) do |match| entity = $1 entities[entity] || match end bookmarks = [] doc = REXML::Document.new(exported_data) doc.elements.each('MYWEBBOOKMARKS/FOLDER/ITEMS/BOOKMARK') do |entry| bookmarks << { :title => entry.elements['TITLE'].text.strip, :url => entry.elements['URL'].text.strip, :date => Time.at(entry.elements['ADD_DATE'].text.strip.to_i).getutc, :comment => entry.elements['NOTE'].text, :tags => entry.elements.to_a('TAGS/TAG').map{|e| e.text.strip } } end bookmarks.sort!{|a, b| (a[:date] <=> b[:date]) * -1 } block = 0 BLOCK_SIZE = 500 while bookmarks.size > block * BLOCK_SIZE doc = REXML::Document.new('<posts />') bookmarks[block*BLOCK_SIZE, BLOCK_SIZE].each do |bookmark| e = REXML::Element.new('post') e.attributes['description'] = bookmark[:title] e.attributes['href'] = bookmark[:url] e.attributes['time'] = bookmark[:date].xmlschema e.attributes['extended'] = bookmark[:comment] e.attributes['tag'] = bookmark[:tags].join(' ') doc.root.elements << e end File.open('myweb_output%02d.xml' % block, 'w') do |file| doc.write(file, 2) end block += 1 end
カレントディレクトリの "myweb.xml" からエクスポートデータを読み込み、変換結果を 500 件ごとに "myweb_output??.xml" というファイル名で出力します。手抜きですいません。
XML の形式を変換しているだけにしてはスクリプトが長いですが、以下の処理をしています。
- URL の <, >, &, " が実体参照になっていなくて REXML がエラーを吐くので、実体参照に変換。
- コメント中の一部の全角記号が HTML4 の実体参照に変換されていたので、それを通常の文字に戻している。でも、インポートしたらけっきょく実体参照化されてしまったので、あまり意味ないかも。
- 件数が多すぎると Yahoo! ブックマークへのインポートがエラーで止まってしまうので、 500 件ごとに分割。
2, 3 はまだしも、 1 みたいなバグが残っているのはちょっとカッコわるいですよね。エクスポートした XML を一回でも検証すればわかるはずなんですが・・・。ちなみに、日本の Yahoo! ブックマークではきちんと修正(CDATA になっている)されてました。さすがです。
Google Notebook からの移行
次は Google Notebook からの移行です。最初は API を使ってデータをエクスポートしようとしたのですが、どうも 100 件までしかデータが取得できないみたいです。仕方ないので、次善の策として Google Notebook のページの下のほうにある「エクスポート」のリンクでノートブックごとにエクスポートしました。こちらは Atom フィードの形式で全件を取得できます。
あとは、それらの XML をカレントディレクトリの "notebooks" の下に保存し、 MyWeb と同様にスクリプトで Delicious API 形式に変換しました。
#! /usr/bin/ruby # -*- coding: utf-8 -*- KCODE='UTF8' require 'rexml/document' require 'time' bookmarks = [] Dir.glob('notebooks/*.xml') do |fname| xml = IO.read(fname).gsub(/&(\w+);/u) do |match| entity = $1 entities[entity] || match end doc = REXML::Document.new(xml) notebook_title = doc.elements['feed/title'].text notes = [] doc.elements.each('feed/entry') do |entry| if entry.elements['link'] bookmark = { :title => entry.elements['title'].text.strip, :date => Time.xmlschema(entry.elements['updated'].text.strip), :comment => entry.elements['content'].text.strip, :tags => ['gn', notebook_title] } bookmark[:comment] = bookmark[:comment].gsub(/<br>/u, "\n").gsub(/<[^>]*>/u, '').strip entry.elements.each('link') do |link| bookmark[:url] = link.attributes['href'] if link.attributes['rel'] == 'related' end entry.elements.each('category') do |category| if category.attributes['scheme'] == 'http://schemas.google.com/notebook/gdata/2007/section' bookmark[:tags] << category.attributes['label'] end end bookmark[:tags].map!{|t| t.gsub(/\s/u, '_') } notes << bookmark if bookmark[:url] && !bookmark[:url].empty? end end bookmarks += notes $stdout << "#{notebook_title} : #{notes.size}\n" end bookmarks.sort!{|a, b| (a[:date] <=> b[:date]) * -1 } doc = REXML::Document.new('<posts />') bookmarks.each do |bookmark| e = REXML::Element.new('post') e.attributes['description'] = bookmark[:title] e.attributes['href'] = bookmark[:url] e.attributes['time'] = bookmark[:date].xmlschema e.attributes['extended'] = bookmark[:comment] e.attributes['tag'] = bookmark[:tags].join(' ') doc.root.elements << e end File.open('notebook_output.xml', 'w') do |file| doc.write(file, 2) end
変換結果は "notebook_output.xml" という名前に出力されます。こちらは件数が少なかったので、分割はしていません。また、ノートブック名とセクション名をタグに変換しています。
Web サービスを使い始める際は、エクスポート機能をチェック!
というわけで、なんとか全データを Yahoo! ブックマークに移行できました。やれやれです。
今回のことで痛感したのが、エクスポート機能の重要性です。 Web 上のサービスは運営側の都合でいつシャットダウンされるかわかりませんし、サービスが終了したら最後、自分が数年かけて蓄えてきたデータがすべて失われてしまいます。そんなハメに陥らないよう、 Web サービスを使い始める際には、必要なデータがきちんとエクスポートできることを確認しておくことが大事ですね。とくに Atom などの標準的なフォーマットでエクスポートできれば、他のサービスに移行するのも簡単です。
逆に言えば、自分でサービスを作る際にもエクスポート機能は必須ということですね。作る側の心理として他のサービスへの移行に障害を設けてしまいがちですが、それではユーザーに安心してサービスを利用してもらうことができないということ。できるだけ利便性の高いエクスポート機能を用意しておきたいものです。
詳しくはこちらの記事をどうぞ!
この記事にコメントする