はてなブックマーク件数取得API
はてなでは、はてなブックマークされている数を取得するためのAPIが公開されています。
はてなブックマーク件数取得APIとは - はてなキーワード
よく利用されるであろうAPIは以下の二つ
- 特定のページの被ブックマーク数を取得する(GETリクエスト、XML-RPC)
- 例 http://www.ruby-lang.org/ja/の被ブックマーク数を取得(997件)
- 特定のサイトの被ブックマーク数を取得する(XML-RPC)
- 例 http://www.ruby-lang.org/ja/以下の被ブックマーク数を取得(3744件)
「特定のページ」のほうはGETリクエストでのJSONPに対応してるので、scriptタグとjavascriptから簡単に使えます。が、「特定のサイト」のほうはXML-RCPだけ。これではjavascriptからは使えません。(javascriptのXML-RPCライブラリはあるのですが、XMLHttpRequestを使うので同じドメインのみ)
うーん、困った。それじゃあ自分のサーバ上にGETリクエスト(JSONP)で「特定のサイト」を取得するAPIを作ろう。
XML-RPCって何?
Google先生に聞けば色々詳しく教えてもらえる。僕がGoogle先生から教えてもらったことを要約するとこんな感じ。
- RPCってのはリモート・プロシージャ・コールの略。
- ほかのコンピュータのAPIを呼び出す仕組み。
- その呼び出し方や応答のやりとりをHTTPとXMLを使って行う
詳しくは以下参照。
XML-RPC - Wikipedia
404 - エラー: 404
RubyでXML-RPCを使うには?
RubyでXML-RPCを使うのはすごく簡単。標準添付されているxmlrpcライブラリを使用すればおk。
require "xmlrpc/client" client = XMLRPC::Client.new2("http://example.com/api/sample.rb") result = client.call("sample.sumAndDifference", 5, 3) p result
Content-Typeがtext/xml以外はエラー
じゃあこれで被ブックマーク数のAPIを使えると思ってコード書いて実行するとエラー
require "xmlrpc/client" url = "http://www.ruby-lang.org/ja/" client = XMLRPC::Client.new2("http://b.hatena.ne.jp/xmlrpc") result = client.call("bookmark.getTotalCount" , url) puts "getTotalCount:#{result}"
/usr/local/lib/ruby/1.8/xmlrpc/client.rb:557:in `do_rpc': Wrong content-type (received 'application/xml' but expected 'text/xml') (RuntimeError) from /usr/local/lib/ruby/1.8/xmlrpc/client.rb:420:in `call2' from /usr/local/lib/ruby/1.8/xmlrpc/client.rb:410:in `call' from ./hoge.rb:8
どうやらRubyのxmlrpc/clientでは受け取ったデータのcontent-typeはtext/xmlのみしか許さない様子。うーん、また困った。
仕方ない、ソース読んで何か解決策探すか。
(Ruby1.9では起きないのかなー?)
かなり場当たり的だけど一応解決
ソース読むとxmlrpc/client.rbでエラー(例外箇所)を発見。
ct = parse_content_type(resp["Content-Type"]).first if ct != "text/xml" if ct == "text/html" raise "Wrong content-type (received '#{ct}' but expected 'text/xml'): \n#{data}" else raise "Wrong content-type (received '#{ct}' but expected 'text/xml')" end end
これはparse_content_typeをちょっといじれば何とかなるかも。parse_content_typeはxmlrpc/utils.rbにあります。
module XMLRPC ... module ParseContentType def parse_content_type(str) a, *b = str.split(";") return a.strip.downcase, *b end end end
parse_content_typeの戻り値を無理やり変更して以下のように対応。(ライブラリを直接修正するのはちょっと心配なのと、レンタルサーバ上だからそもそもライブラリを編集できません)
require "xmlrpc/client" module XMLRPC::ParseContentType def parse_content_type(str) a, *b = str.split(";") a = "text/xml" if a == "application/xml" return a.strip.downcase, *b end end url = "http://www.ruby-lang.org/ja/" client = XMLRPC::Client.new2("http://b.hatena.ne.jp/xmlrpc") result = client.call('bookmark.getTotalCount', url) puts "getTotalCount:#{result}"
というわけでかなり場当たり的な方法で解決。良い子は真似しちゃダメな気がする><