具体例で学ぶ!情報可視化のテクニック

第5回はてなブックマークの可視化(前編)

はじめに

今回からは、これまで学んできた「階層的クラスタリング」「ツリーマップ」の手法を活用して、はてなブックマークの人気エントリーの可視化に挑戦します。まずデータの準備段階として、はてなブックマークが提供するWeb APIにアクセスし、人気エントリーの情報を取得するプログラムを作成します。

ソースコードのダウンロード

今回作成するプログラムのソースコードは、こちらから一括してダウンロードすることができます。ZIPファイルを展開して生成されるフォルダを、プロジェクトとしてNetBeansに読み込むことも可能です。

はてなブックマークのAPI

はてなブックマークには、数種類の開発者向けAPIが用意されています。これらのAPIを利用すると、はてなブックマークから様々な情報を取り出すことができます。本連載では、人気エントリーのRSSフィードとエントリー情報取得APIを組み合わせて使用します。

人気エントリーのRSSフィード

はてなブックマークには人気エントリーのページがありますが、このページの内容はRSSフィード形式でも提供されています。RSSフィードをプログラムから読み込むと、最新の人気エントリーの一覧を取得することができます。

エントリー情報取得API

エントリー情報取得APIを使用すると、特定のブックマークエントリーに対して、ユーザーが付与したタグやコメントなどの詳細情報を、JSON形式で取得することができます。APIの詳細な仕様は、はてなのドキュメントを参照してください。

今回のプログラムではまず、人気エントリーのRSSフィードを解析します。次に、RSSフィードから得られた個々のURLを引数として、エントリー情報取得APIを呼び出し、エントリーのタグ情報を取得します。

ライブラリのダウンロード

RSSフィードやJSON形式のデータをJavaから扱う場合は、外部ライブラリが必要になります。今回は、RSSフィードの解析にROMEとそのサブプロジェクトであるROME FetcherJSONの読み込みにJSON-libを利用することにします。また今回は、URLからのデータの読み込みを容易にするため、Commons IOを使用します。

ROME FetcherとJSON-libは以下のライブラリに依存しており、プログラムを実行する際にはこれらも合わせて必要になります。

ROME Fetcherの依存ライブラリ
JSON-libの依存ライブラリ

各プロジェクトサイトからバイナリファイルをダウンロードして展開し、JARファイルをクラスパスに追加します。最終的に、今回のプログラムのコンパイルと実行に必要なJARファイルは以下となります。

  • commons-beanutils-1.8.0.jar
  • commons-collections-3.2.1.jar
  • commons-io-1.4.jar
  • commons-lang-2.4.jar
  • commons-logging-1.1.1.jar
  • ezmorph-1.0.5.jar
  • jdom.jar
  • json-lib-2.2.2-jdk15.jar
  • rome-1.0RC1.jar
  • rome-fetcher-0.9.jar

人気エントリーの取得

それではまず、はてなブックマークの人気エントリーのRSSフィードを読み込むプログラムを作成しましょう。先に、エントリー情報を格納する以下のBookmarkクラスを用意します。

リスト1 Bookmark.java
public class Bookmark {
   public String url; // ブックマーク対象のURL
   public String title; // ブックマークタイトル
}

次に、HatenaBookmarkAPIクラスを作成します。このクラスには、人気エントリーの一覧をBookmarkオブジェクトのリストとして返すgetHotEntries()メソッドを持たせます。

リスト2 HatenaBookmarkAPI.java(部分)
public class HatenaBookmarkAPI {
   public List<Bookmark> getHotEntries() {
      List<Bookmark> bookmarks = new ArrayList<Bookmark>();
      FeedFetcher fetcher = new HttpURLFeedFetcher();
      SyndFeed feed;
      try {
         // 人気エントリーのRSSフィードを読み込む
         feed = fetcher.retrieveFeed(new URL(
               "http://b.hatena.ne.jp/hotentry?mode=rss"));
      } catch (Exception e) {
         return bookmarks;
      }
      // 各エントリーの情報を取得
      for (Object e : feed.getEntries()) {
         SyndEntry entry = (SyndEntry) e;
         Bookmark bookmark = new Bookmark();
         bookmark.url = entry.getLink();
         bookmark.title = entry.getTitle();
         bookmarks.add(bookmark);
      }
      return bookmarks;
   }
}

コードを簡単にするため、URLへのアクセス時にエラーが発生した場合は、特にエラー処理を行わずに空のリストを返しています。

エントリーの詳細情報の取得

人気エントリーのRSSフィードは、各エントリーのURLやタイトルといった見出しレベルの情報しか含んでいません。ブックマーク数やタグなどの詳細情報を得るためには、エントリー情報取得APIを個別に呼び出す必要があります。この詳細情報を格納する目的で、BookmarkDetailクラスを定義します。

リスト3 BookmarkDetail.java
public class BookmarkDetail {
   public int bookmarkCount; // ブックマーク数
   public List<String> tags; // ブックマークに付与されたタグの一覧
}

そして、HatenaBookmarkAPIクラスに、特定のURLからブックマークエントリーの詳細情報を取得するgetDetail()メソッドを追加します。こちらもエラー処理を省略し、URLの読み込みに失敗した場合には単にnullを返すこととします。

リスト4 HatenaBookmarkAPI.java(部分)
public BookmarkDetail getDetail(String url) {
   String encodedUrl;
      try {
        // URLをUTF-8でエンコードする
        encodedUrl = URLEncoder.encode(url, "UTF-8");
      } catch (UnsupportedEncodingException e) {
          throw new InternalError();
      }
        // エントリー情報取得APIのURL
        String apiUrl = "http://b.hatena.ne.jp/entry/json/?url=" +  encodedUrl;
      InputStream in = null;
      String data;
      try {
         // URLを開き、データを読み込む
         in = new URL(apiUrl).openStream();
         data = IOUtils.toString(in);
      } catch (Exception e) {
         return null;
      } finally {
         IOUtils.closeQuietly(in);
      }
      // 戻り値が()で囲まれている場合、JSON-libで読み込めないので取り除く
      if (data.startsWith("(") && data.endsWith(")")) {
         data = data.substring(1, data.length() - 1);
      }
      // データをJSONとして読み込む
      JSONObject json = JSONObject.fromObject(data);

      // 詳細情報を作成
      BookmarkDetail detail = new BookmarkDetail();
      detail.bookmarkCount = (int) json.getLong("count");
      detail.tags = new ArrayList<String>();

      // bookmarks配列を読み込む
      JSONArray bookmarks = json.getJSONArray("bookmarks");
      for (int i = 0; i < bookmarks.size(); i++) {
         JSONObject item = bookmarks.getJSONObject(i);
         // tags配列を読み込む
         JSONArray tags = item.getJSONArray("tags");
         for (int j = 0; j < tags.size(); j++) {
            detail.tags.add(tags.getString(j));
         }
      }

      return detail;
   }

はてなブックマークAPIの動作確認

ではここで、テストプログラムを作成し、HatenaBookmarkAPIクラスの動作を確認してみましょう。以下が、はてなブックマークの人気エントリーの詳細情報をコンソールに表示するDemoクラスになります。

リスト5 Demo.java
public class Demo {
   public static void main(String[] args) {
      HatenaBookmarkAPI api = new HatenaBookmarkAPI();
      List<Bookmark> bookmarks = api.getHotEntries();
      System.out.println(bookmarks.size() + " entries.");

      for (Bookmark bookmark : bookmarks) {
         System.out.println(bookmark.title);
         System.out.println("  [url] " + bookmark.url);
         try {
            // サーバの負荷を抑えるため呼び出し間隔を空ける
            Thread.sleep(1000);
         } catch (InterruptedException e) {
         }
         BookmarkDetail detail = api.getDetail(bookmark.url);
         if (detail != null) {
            System.out.println("  [bookmarkCount] " + detail.bookmarkCount);
            System.out.println("  [tags] " + detail.tags);
         }
      }
   }
}

Demoクラスを実行すると、実行時点での最新の人気エントリーの情報が、例えば次のように出力されます。

50 entries.
任天堂DS、年内に新型機 カメラ・音楽再生機能追加 - NIKKEI NET(日経ネット)
  [url] http://www.nikkei.co.jp/news/main/20080928AT1D2701J27092008.html
  [bookmarkCount] 192
  [tags] [ニンテンドーDS, 日本経済新聞, ゲーム, ゲーム, game, ds, news, nintendo, ゲーム, ...
[内輪]「あたし彼女」現代語訳 - 藤棚の上
  [url] http://d.hatena.ne.jp/konaken/20080927/1222520200
  [bookmarkCount] 252
  [tags] [ケータイ小説, これはすごい, これはすごい, まとめ, 小説, あたし彼女, あとで読む, ケータイ小説, ...
ウォールストリート日記 : ウォールストリートの歴史的1ヶ月
  [url] http://wallstny.exblog.jp/8669144/
  [bookmarkCount] 124
  [tags] [金融, あとで読む, business, サブプライム, あとで読む, まとめ, 米国, 経済, 金融, 経済, ...
...

[tags]の内容に注目してください。⁠ゲーム, ゲーム, ...」のように、同じタグが1つのエントリーに複数回出現するケースがあります。これは、1つのエントリーに対し、複数のユーザーが同一のタグを付けたことを示しています。この「ゲーム」のように、多くのユーザーが共通して付与したタグほど、エントリーの特徴を強く示すキーワードであると考えられます。

タグと出現回数のセットをベクトル形式で表現できれば、そのベクトルを入力として、階層的クラスタリングを実行することができそうです。また、クラスタリングの後で構築するツリーマップのノード領域面積には、[bookmarkCount]で出力されたブックマーク数をそのまま利用できるでしょう。

次回に続く

今回は、はてなブックマークのRSSフィードとエントリー情報取得APIを使用して、人気エントリーの情報を取得するプログラムを作成し、その動作確認を行いました。これまでの連載で、可視化のために必要なロジックと現実のデータの両方が、ほぼ揃ったことになります。次回は、収集されたブックマークタグをベクトルに変換し、いよいよ可視化の本処理へと進んでいきます。

おすすめ記事

記事・ニュース一覧