久しぶりに真面目に Ruby.以前にはてなブックマーク API の Ruby ラッパを書いていたのですが,はてなブックマーク API 側もちょこちょこと変更があったようでうまく動かなくなっていたので修正しました.以前に書いたときは,よく分からずあれもこれも書いた結果,ファイル構成やら何やら分かりづらくなっていたので,いくつかの機能に絞って 1 ファイルに纏めました.依存している (gem)ライブラリは json と nokogiri です.
hatena/bookmark.rb - crown - clown's github
module Crown module Hatena module Bookmark # --------------------------------------------------------------- # # # structures # # get() で返される構造体 # # --------------------------------------------------------------- # User = Struct.new(:tags, :date, :comment) Related = Struct.new(:eid, :uri, :title, :count) Response = Struct.new(:eid, :uri, :image, :title, :count, :users, :related) # --------------------------------------------------------------- # # functions # --------------------------------------------------------------- # def count(uri, session= Net::HTTP.new("api.b.st-hatena.com", 80)) # -> int def get(uri, (uri, session = Net::HTTP.new("b.hatena.ne.jp", 80)) # -> Response or nil def each(query, session = Net::HTTP.new("b.hatena.ne.jp", 80)) # { |block| } end # Bookmark end # Hatena end # Crown
count(), get() はそれぞれ,指定した URL のはてなブックマーク件数,はてなブックマーク情報を取得します.each() は少し変わり種で,例えば,http://b.hatena.ne.jp/hotentry などに列挙されてある(ブックマークされている)記事の URL に対応するブックマーク情報を順に取得し,その情報をブロックに渡します.この際,例えば,http://b.hatena.ne.jp/entrylist?url=d.hatena.ne.jp/tt_clown/ のような URL だと html の head に next 属性で次のページの URL が示されてあることがありますが,each() はその next 属性を利用してできるだけ多くのブックマーク情報を取得しようと試みます.
each() は,2010 年人気エントリー,2009 年人気エントリー,2008 年人気エントリー と毎年,その年のブクマされた記事 TOP10 を挙げていますが,このデータを取得する際に使用しています.
#!/usr/bin/ruby -Ku # annual.rb require 'crown/hatena/bookmark' PREFIX = "entrylist?sort=count&url=" # --------------------------------------------------------------------------- # # # 人気のエントリーから引数に指定した年の記事の URL を抽出する. # # Parameters # ARGV[0]: 対象とするサイトの URL # ARGV[1]: 対象とする年 # ARGV[2]: 取得件数(オプション) # # --------------------------------------------------------------------------- # if (ARGV.length < 2) puts("usage annual.rb URL year [num_of_url]") return end year = ARGV[1].to_i limit = (ARGV.length > 2) ? ARGV[2].to_i : 10 i = 0 Crown::Hatena::Bookmark.each(PREFIX + ARGV[0]) { |entry| # その記事がいつのものかは,最初にブックマークした人のブックマーク日時で判断する. v = entry.users.to_a.sort { |x, y| x[1].date <=> y[1].date } if (v != nil && !v.empty? && v[0][1].date.year == year) printf("+ %s (%d users)\n", entry.uri, entry.count) i += 1 break if (i >= limit) end }
実行例
$ ruby annual.rb d.hatena.ne.jp/tt_clown/ 2010 + http://d.hatena.ne.jp/tt_clown/20100202/1265096776 (1123 users) + http://d.hatena.ne.jp/tt_clown/20101213/1292232085 (111 users) + http://d.hatena.ne.jp/tt_clown/20100205/1265350752 (90 users) + http://d.hatena.ne.jp/tt_clown/20100318/1268894932 (83 users) + http://d.hatena.ne.jp/tt_clown/20100702/1278048637 (67 users) + http://d.hatena.ne.jp/tt_clown/20100904/1283587136 (64 users) + http://d.hatena.ne.jp/tt_clown/20100806/1281088936 (57 users) + http://d.hatena.ne.jp/tt_clown/20100216/1266340239 (51 users) + http://d.hatena.ne.jp/tt_clown/20100116/1263622099 (51 users) + http://d.hatena.ne.jp/tt_clown/20100818/1282110140 (49 users)
その他
昔作ったコードが動かなくなっていた原因をここに書いておきます.
html のエスケープの仕方が変わっていた
以前は,はてなブックマーク API に URL を渡す際にエスケープしなければならない文字がちょっと特殊で自力で処理しないといけない部分があったのですが,いつの間にか CGI.escape() 相当のエスケープになっていました.逆に,以前のエスケープ処理だとエラーになってしまうようです.
Ruby の XMLRPC だとエラーになる
はてなブックマーク件数取得 API には XMLRPC 形式のものもあるのですが,はてなブックマーク件数取得APIのXML-RPCをRubyで使う - maru source に書かれたるように Ruby の XMLRPC を使用するとエラーが発生しました.Ruby の XMLRPC は text/xml しか許していないのですが,はてなブックマーク API は application/xml で結果を返してくるせいのようです.
これは今のところどうしようもない(?)ので,はてなブックマーク件数の取得は単純に GET コマンドでブックマーク件数をテキストで取得する方法を使用し,「特定のサイト」の合計ブックマーク数の取得,ASIN からブックマーク数を取得,の機能は実装を見送っています.
Ruby はまだまだどう書くのが(Ruby として)良いのかよく分かってないので変な書き方の部分もあるかもしれませんが,まぁボチボチ直していこうかと思います.