はてなダイアリーを自動バックアップする
先日書店に行ってみると「Rubyist Magazine 出張版 Ruby on Windows」という書籍を見つけました。タイトルから想像できるように、Windows環境に特化したRubyの解説書でした。その中には、Internet Explorerを操作して、Web自動巡回という内容もありました。特段目新しいモノでもありませんが、何となく気にとまりました。
書籍やネットを参考にして、Rubyで、はてなダイアリーの内容を自動でバックアップするツールを作成してみました。次のように実行すると、Internet Explorerが起動し、自動的にはてなダイアリーのエクスポートページへ移動し、ファイル保存を行います。はてなのログインIDやパスワードなどは、事前に設定ファイルに記述しておきます。このRubyスクリプトでは、Hpricotというライブラリを使用しているので、事前にgemsを利用してインストールしておく必要があります。
実行手順
1.Hpricotインストール
当Rubyスクリプトでは、HTMLの解析にHpricotというライブラリを使用していますので、事前に、gemを利用して、インストールしておきます。
gem install hpricot
2.設定ファイル編集
下記の「exporthatena.yaml.sample」を参考にして、「exporthatena.yaml」を作成します。ユーザ名やパスワードをご自分の設定に編集して下さい。必要あれば、「outbasename」項目に、パスを追加することで、任意のディレクトリにファイル保存できます。
3.Rubyスクリプト実行
次のコマンドを実行することで、Internet Explorerが起動し、はてなダイアリーのエクスポートページへ移動し、ファイル保存を行います。
ruby exporthatenablog.rb
Rubyスクリプト
exporthatenablog.rb
#!ruby -Ks # IEを利用して、はてなからブログデータをエクスポートします。 # # 使用時の注意事項 # ・設定ファイルサンプル(exporthatena.yaml.sample)を参考にして、 # 当ファイルと同じディレクトリに、設定ファイル(exporthatena.yaml) # を作成して下さい。 # ・ブログデータをファイル保存する際、ポップアップブロックにより、 # 情報バーが表示される場合、次のどちらかの方法で、情報バーが表示 # されないようにして下さい。 # (1)ポップアップブロックをオフにしてください # (2)はてな(http://*.hatena.ne.jp)を許可サイトに登録し、セキュ # リティレベル設定で、ゾーン間を移動したときのダイアログボッ # クスを非表示にして下さい。 # ・動作中は、キーボードやマウスの操作をしないで下さい。 # # 2009/02/07 新規作成 require 'iebrowsing' require 'win32ole' class HatenaBlogExport < IEBrowsing def initialize super("exporthatena.yaml") end def run browsing { transaction { @ie.Navigate("http://d.hatena.ne.jp/") } transaction("はてなダイアリー - 無料ブログを簡単にはじめよう、すぐできるブログ(blog)") { get_links().get_by_innertext("ログイン").first.Click() } transaction("ログイン - はてな") { get_element_by_id("login-name").Value = @ini["username"] get_element_by_id("password").Value = @ini["password"] get_element_by_id("auto_login").Value = 0 get_elements_by_tagname("form").first.Submit() } transaction("はてなダイアリー - 無料ブログを簡単にはじめよう、すぐできるブログ(blog)") { get_links.get_by_innertext("管理").first.Click() } transaction("#{@ini['username']}の日記 - 管理ツールトップ") { get_links.get_by_innertext("データの管理").first.Click() } path = @ini["outbasename"] + Time.now.strftime("%Y%m%d-%H%M%S") + @ini["outext"] transaction("#{@ini['username']}の日記 - インポート/エクスポート") { get_links.get_by_innertext("ダウンロード").first.Click() # ダイアログボックスを操作してファイル保存します。 send_keys_download_dialog(path) } @log.info("ダウンロードしました。ファイル名=[#{path}]") transaction("#{@ini['username']}の日記 - インポート/エクスポート") { get_links.get_by_innertext("ログアウト").first.Click() } transaction("はてなダイアリー - 無料ブログを簡単にはじめよう、すぐできるブログ(blog)") { } } end end # 実行します。 begin HatenaBlogExport.new.run rescue puts $!.message puts $!.backtrace end
iebrowsing.rb
# IEブラウジングクラス # IEと、COMオブジェクトのHTMLDocumentクラスを操作します。 # # 前提 # ・Hpricotを使用します。事前に次のようにしてインストールしておいて下さい。 # gem install hpricot # # 2009/02/06 新規作成 require 'logger' require 'nkf' require 'win32ole' require 'yaml' require 'rubygems' require 'hpricot' require 'htmlelementcollection' class IEBrowsing attr_reader :ie # タイムアウト時間(秒) DEFAULT_TIMEOUT = 30 # デフォルトの処理待ち時間(秒) DEFAULT_WAIT = 0.1 # ログ @log = nil # 初期設定ファイル連想配列 @ini = {} # IEのCOMオブジェクト @ie = nil # WScript.ShellのCOMオブジェクト @wsh = nil # 初期化 def initialize(yamlfile=nil) # ログ出力を設定します。 @log = Logger.new(STDOUT) @log.level = Logger::INFO # YAML形式の初期設定ファイルを読み込みます。 if yamlfile open(yamlfile) { |io| @ini = YAML.load(io.read) } @log.info("設定ファイル[#{yamlfile}]を読み込みました。") end # WScript.ShellのCOMオブジェクトを作成します。 @wsh = WIN32OLE.new('WScript.Shell') end # IE操作ブロック # 指定されたブロック処理の前段でIEを起動し、ブロック処理後、IEを # 終了します。 def browsing(&block) browsing_start() yield() browsing_end() end # IE操作開始処理 def browsing_start() # COMオブジェクトを作成します。 @ie = WIN32OLE.new('InternetExplorer.Application') # IEのウインドウを可視化します。 @ie.Visible = true @log.info("IEを起動しました。") end # IE操作終了処理 def browsing_end() @ie.Quit() @ie = nil # IE終了後、続けてIE起動すると失敗するため、少しウエイトを入れます。 sleep 1 @log.info("IEを終了しました。") end # トランザクション処理ブロック # title - 任意引数。操作を行うページのタイトルを指定します。 #alias :page :transaction def transaction(title=nil, &block) wait_ie(title) begin yield() rescue @log.error("エラー発生時のHTML出力開始") @log.error(@ie.document.documentElement.outerHTML) @log.error("エラー発生時のHTML出力終了") raise end wait_ie end # IE処理待ち # title - 任意引数。指定された場合、TITLEタグ要素の内部テキストが # 引数の値になるまで待ちます。未指定の場合、ページ内容に # 関わらず、IEのページ読み込み完了まで待ちます。 def wait_ie(title=nil) wait { # IE処理中の間、待ちます。 wait { @ie.busy } result = false if title @log.debug("タイトルチェック開始") doc = Hpricot(@ie.document.documentElement.outerHTML) real_title = NKF.nkf("-s", (doc/"title").inner_html) result = (title != real_title) if ! result @log.info("ページ[#{real_title}]を表示しました。") end @log.debug("想定=[#{title}]") @log.debug("実際=[#{real_title}]") @log.debug("結果=[#{result}]") @log.debug("タイトルチェック終了") end result } end # 判定処理&タイムアウト付きウエイト def wait(timeout=DEFAULT_TIMEOUT, &block) time = 0 while yield sleep DEFAULT_WAIT time += DEFAULT_WAIT if time >= timeout raise "ERROR: タイムアウトが発生しました。" end end end # HTMLドキュメントから要素の取得 def get_element_by_id(id) return @ie.document.getElementById(id) end # HTMLドキュメントから要素の取得 def get_elements_by_name(name) return HtmlElementCollection.new(@ie.document.getElementsByName(name)) end # HTMLドキュメントから要素の取得 def get_elements_by_tagname(tagname) return HtmlElementCollection.new(@ie.document.getElementsByTagName(tagname)) end # HTMLドキュメントから要素の取得 def get_links() return HtmlElementCollection.new(@ie.document.links) end # 表示HTMLのファイル保存 def save(filepath) open(filepath, "w") { |io| io.binmode io.write(@ie.document.documentElement.outerHTML) } end # IEの「名前を付けて保存」機能を使用して表示ページファイル保存(html形式) def save_html(filepath) save_by_ie(filepath, "{TAB}") end # IEの「名前を付けて保存」機能を使用して表示ページファイル保存(mht形式) def save_mht(filepath) save_by_ie(filepath, "{DOWN}{TAB}") end # IEの「名前を付けて保存」機能を使用して表示ページファイル保存 # type_keys - ファイルの種類を選択するためのキー操作 def save_by_ie(filepath, type_keys) # IEをアクティブにします。 doc = Hpricot(@ie.document.documentElement.outerHTML) title = NKF.nkf("-s", (doc/"title").inner_html) wait { ! @wsh.AppActivate("#{title} - Microsoft Internet Explorer") } # メニューから、ファイル>名前を付けて保存、を選択します。 @wsh.SendKeys("%fa") # 「Web ページの保存」ダイアログボックスをアクティブにします。 wait { ! @wsh.AppActivate("Web ページの保存") } # ファイル名を設定します。 @wsh.SendKeys("%n") @wsh.SendKeys(filepath) # ファイルの種類を設定します。 @wsh.SendKeys("{TAB}") @wsh.SendKeys("{DOWN}") sleep 1 @wsh.SendKeys(type_keys) # 保存を実行します。 @wsh.SendKeys("%s") # ログ出力 @log.info("ファイル[#{filepath}]を保存しました。") end # ファイルのダウンロードダイアログボックスの操作 def send_keys_download_dialog(filepath) # ダイアログボックスをアクティブにします wait { ! @wsh.AppActivate("ファイルのダウンロード") } # ダイアログボックスが表示されても、ボタンなどが描画されていな # いかもしれないので、スリープします。 sleep 1 # 「保存」ボタンを押します @wsh.SendKeys("s") # ダイアログボックスをアクティブにします wait { ! @wsh.AppActivate("名前を付けて保存") } # ダイアログボックスが表示されても、ボタンなどが描画されていな # いかもしれないので、スリープします。 sleep 1 # ファイル名欄にカーソルを移動します @wsh.SendKeys("%n") # ファイル名を入力します @wsh.SendKeys(filepath) # 保存します @wsh.SendKeys("{ENTER}") end end
exporthatena.yaml.sample
# exporthatenablog.rb用設定ファイル # # 2009/02/07 新規作成 # ユーザ設定 # はてなへログインするためのユーザ名とパスワードを設定して下さい。 username: username password: password # 出力ファイル設定 outbasename: hatena_blog_ outext: .xml
動作環境
- OS:Microsoft Windows XP Home Edition SP3
- ブラウザ:Internet Explorer 6.0
- Ruby:ActiveScript Ruby 1.8.7