はてなスターの星の数を数えるRubyスクリプトを書いてみた
ちょっとやりたいことがあって、エントリに付けられたはてなスターの数を数えるRubyスクリプトを書いてみようと思った。はてなスターカウントAPIというのもあるけど、これはブログに付けられた☆の総数を数えるので、エントリごとの☆の数を数えるのには使えない。
そこで、まずはてなスターの処理を行う、下記のスクリプトを読んでみることにした。
http://s.hatena.ne.jp/js/HatenaStar.js
HatenaStar.jsの はてなスターの情報をJSONPで取得する処理
HatenaStar.jsは、大きく2つの部分から構成されている。
- Ten
- クラス、XmlHttpRequest、イベント処理、DOMなど、基礎となる機能を提供するクラス(Ten.*)の定義
- Hatena
- はてなスターの処理を行なうクラス(Hatena.Star.*)の定義
このうち、はてなスターの情報をはてなのサーバから取ってくる処理は、Hatena.Star.EntryLoaderが行っている。だいたい、こんな処理をしている。
はてなスターの情報をJSONPで取得するURIの入出力
次に、実際にリクエストを投げてみたりして、はてなスターの情報をJSONPで取得できる http://s.hatena.ne.jp/entries.json の入出力を調べてみた。このURIの入出力については既に調べられた方がいます(下記のエントリ)が、HatenaStar.js のコードを読むと、追記できる部分もあった*1ので、あらためて書いてみます。
http://d.hatena.ne.jp/Yuichirou/20070802/1186070862
出力(HTTPレスポンスのコンテンツ)
{ /* 閲覧者を識別するためのトークン。閲覧者ごとに異なり、閲覧者が同じなら別のページでも同じ値が返る */ "rks":"****************************************", "can_comment" : 0, /* そのサイトでコメントが書けるなら 1 */ "entries" : [ /* 各エントリについての情報が入ったオブジェクトの配列 */ { /* ☆の個数が15個未満の場合 */ "stars" : [ /* ☆の情報 */ { "name" : "***", /* ☆をつけたユーザー名 */ "quote" : "***" /* ☆をつけたユーザーが引用した箇所 */ }, { "name" : "***", "quote" : "***" }, /* ☆の個数だけ繰り返し */ ], "can_comment" : 0, /* そのエントリーにコメントが書けるなら 1 */ "uri" : "http://***" /* エントリーのURI */ }, { /* ☆の個数が15個以上の場合 */ "stars" : [ { "name" : "***", "quote" : "***" }, /* 最初につけられた☆の情報 */ 25, /* 間に含まれる☆の数(全体 - 2) */ { "name" : "***", "quote" : "***" }, /* 最後につけられた☆の情報 */ ], "can_comment" : 0, /* そのエントリーにコメントが書けるなら 1 */ "uri" : "http://***" /* エントリーのURI */ }, /* 以下、エントリの数だけ繰り返し */ ] }
入力パラメーターcallbackを指定した場合は、上記を引数にしてコールバック関数を呼ぶJavaScriptコードが返ってきます。
はてなスターの☆の数を数えるRubyスクリプト
はてなスターの情報をJSONPで取得するURIの入出力形式が分かったので、これを利用して、はてなスターの☆の数を数えるRubyスクリプトを書いてみました。URIを標準入力から入力すると、そのURIにつけられた☆の数を標準出力にします。
require 'uri' require 'net/http' require 'simple-json' Net::HTTP.version_1_2 class HatenaStar ADDRESS = 's.hatena.ne.jp' ENTRIES_PATH = '/entries.json' def self.getNumberOfStars(uri) entries = getEntriesJson(uri)['entries'] raise 'no entries' if entries.size == 0 raise 'unexpected number of entries' if entries.size > 1 number = 0 entries[0]['stars'].each do |e| if e.is_a? Integer number += e else number += 1 end end number end def self.getEntriesJson(uri) res = Net::HTTP.get(ADDRESS, ENTRIES_PATH + '?uri=' + URI.encode(uri)) JsonParser.new.parse(res) end end uri = gets.chomp puts HatenaStar.getNumberOfStars(uri)
JSONを解析する処理は、下記で公開されているRuby用JSONパーサーを利用させて頂きました。ソースコードをファイル"simple-json.rb"に保存して利用しています。
Ruby 用 JSON パーサーを更新、 JSON への変換も追加 - WebOS Goodies
*1:具体的には入力パラメーターcallbackと、出力のquoteプロパティ。以前は無かったのかな?