JTwitterとTwitter4jを触ってみた

前々からTwitterでのつぶやきを日記に転載しておこうと思っていて、なんとなくやってしまった。APIのドキュメントを読んでいたら、各種言語のライブラリの一覧が記載されていたJavaは4種類あったので、とりあえずJTwitterを使ってみた。それを使って作ったのがいままでのつぶやき。とても簡単に作れた。ついでにTwitter4jも触ってみた。こちらの方が機能は充実していた。インタフェースはJTwitterとほぼ同じで、JTwitterで書いたソースをTwitter4jに書き直すのも一瞬だった。折角なので簡単に使い方を解説する。

JTwitter

インストール

JTwitterのサイトからjtwitter.jarをダウンロードして、ライブラリに追加するだけ。ソースも含まれているので不明点があれば中を見ればよい。

つぶやきの取得と投稿

JTwitterのサイトに載っているコードを実行するとつぶやきの取得と投稿が出来る。

import winterwell.jtwitter.Twitter;

public class JTwitterSample1 {
    public static void main(String[] args) {
        // Make a Twitter object
        Twitter twitter = new Twitter("my-name", "my-password");
        // Print Daniel Winterstein's status
        System.out.println(twitter.getStatus("winterstein"));
        // Set my status
        twitter.setStatus("Messing about in Java");
    }
}
  • Twitterクラスのコンストラクタの引数に自分の名前とパスワードを指定する。
  • getStatusメソッドの引数にユーザー名を指定することで、指定したユーザーの最新のつぶやきを取得できる。
  • setStatusメソッドによってつぶやきを投稿できる。
タイムラインの取得

タイムラインはgetFriendsTimelineメソッドで取得できる。デフォルトは20件だが、事前にsetMaxResultsメソッドを利用することで指定できる。

import java.util.List;

import winterwell.jtwitter.Twitter;
import winterwell.jtwitter.Twitter.Status;

public class JTwitterSample2 {
    public static void main(String[] args) {
        // 第1引数はユーザー、第2引数はパスワード
        Twitter twitter = new Twitter("n3104", "*********");
        // 取得件数を指定。指定しない場合20件になる。
        twitter.setMaxResults(100);
        // タイムラインを取得
        List<Status> timeline = twitter.getFriendsTimeline();
        for (Status status : timeline) {
            System.out.println(status.user + " : " + status.text + " at" + status.createdAt);
        }
    }
}
  • タイムライン系のメソッドはいくつかあって、Twitter全体だとgetPublicTimelineメソッド、特定のユーザーのつぶやきだとgetUserTimelineメソッドになる。
  • つぶやきはwinterwell.jtwitter.Twitter.Statusクラスとして表現されている。ちなみにTwitterのインナークラス。このクラスのインタフェースはTwitter.ITweetで、実装クラスとしてTwitter.Status以外にTwitter.Messageがある。Twitter.MessageにはSenderとRecipientの情報が追加されている。
  • setSinceDateメソッドを指定することで取得する期間の開始日時を指定できる。日毎に取得したい場合などに便利。終了日時は指定できないので、そちらは自分で処理する必要がある。
いままでの自分のつぶやきを取得

自分のタイムラインはgetUserTimelineメソッドで取得できる。後は日毎にまとめて適当に加工して出力すればいままでのつぶやきになる。

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TreeMap;

import winterwell.jtwitter.Twitter;
import winterwell.jtwitter.Twitter.Status;

public class JTwitterSample3 {
    public static void main(String[] args) {
        // 第1引数はユーザー、第2引数はパスワード
        Twitter twitter = new Twitter("n3104", "**********");
        // 取得件数を指定。指定しない場合20件になる。
        twitter.setMaxResults(1000);
        // 自分のつぶやきを取得
        List<Status> userTimeline = twitter.getUserTimeline();

        // 出力しやすいようにつぶやきを日毎にリスト単位でまとめる
        TreeMap<Date, List<Status>> treeMap = new TreeMap<Date, List<Status>>();
        for (Status status : userTimeline) {
            Date date = truncTime(status.createdAt);
            List<Status> list = treeMap.get(date);
            if (list == null) {
                list = new ArrayList<Status>();
                treeMap.put(date, list);
            }
            list.add(status);
        }

        // 出力(はてなダイアリー用)
        DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd (E)");
        DateFormat statusFormat = new SimpleDateFormat("HH:mm:ss");
        for (Date date : treeMap.keySet()) {
            System.out.println("**" + dateFormat.format(date));
            List<Status> list = treeMap.get(date);
            for (Status status : list) {
                System.out.println("-" + status.text + " at "
                        + statusFormat.format(status.createdAt));
            }
        }
    }

    private static Date truncTime(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }
}

最初にこのプログラムを作った際は、つぶやき毎にDateの時刻を削るのが無駄だと考え、以下のように書いていた。でも、やりたいことに対してコードが複雑になりすぎる気がして止めた(そもそも、最初はコレクションに入れないで直接出力していた)。

        // 出力しやすいようにつぶやきを日毎にリスト単位でまとめる
        // 日毎のつぶやきのリストを格納するコレクションを用意
        TreeMap<Date, List<Status>> treeMap = new TreeMap<Date, List<Status>>();
        List<Status> list = new ArrayList<Status>();
        // 新しい順から入っているためリバースする
        Collections.reverse(userTimeline);
        // 比較用に最も古いつぶやきの日付を取得し、時刻以下を切り捨て1日増やす
        Date oldestDate = userTimeline.get(0).createdAt;
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(oldestDate);
        truncTime(calendar);
        calendar.add(Calendar.DATE, 1);
        for (Status status : userTimeline) {
            if (calendar.getTimeInMillis() <= status.createdAt.getTime()) {
                // 日付が切り替わったため、いままでのつぶやきを格納しておく
                treeMap.put(list.get(0).createdAt, list);
                list = new ArrayList<Status>();
                // 次の比較用の日付を作成する
                calendar.setTime(status.createdAt);
                truncTime(calendar);
                calendar.add(Calendar.DATE, 1);
            }
            list.add(status);
        }
        // 最終日のつぶやきを格納する
        treeMap.put(list.get(0).createdAt, list);

    private static void truncTime(Calendar calendar) {
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
    }

この作業を通じて以下のことを思った。

  • コードはやりたいことが分かるようにシンプルに書く。そもそも書き捨てに等しいコードで効率を考えるのは時間の無駄。日付の比較方法についてあれこれ何通りも考えてみたけれど、どの書き方でも1分もかからないのに悩んでどうする。これが1時間かかるなら悩む価値もあるだろうが。
  • DateのgetXXXがDeprecatedになっているのも悩んだ原因。たかだか比較のためだけにCalendarのインスタンスを使うのがあまりに無駄な気がして。DateFormatも使えるけれど、比較したいだけで文字列欲しいわけではないし。はてはjava.sql.Date#toStringを使おうかとさえ悩んでしまった。
  • ふと思ったのがスクリプト言語だと、スクリプトだからと言う感覚で迷わずに書けた気がする。書き捨てのコードでスクリプト言語に人気があることに妙に納得した。

Twitter4j

インストール

Twitter4jのサイトからtwitter4j-2.0.10.zipをダウンロードして、中にあるtwitter4j-2.0.10.jarをライブラリに追加するだけ。ソースも含まれており、コンパイルする場合はlibフォルダ内のライブラリを全て追加すればOK。

いままでの自分のつぶやきを取得

JTwitterで書いたコードをほとんど変更することなく移植できる。

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TreeMap;

import twitter4j.Paging;
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;

public class Twitter4jSample1 {
    public static void main(String[] args) throws TwitterException {
        // 第1引数はユーザー、第2引数はパスワード
        Twitter twitter = new Twitter("n3104", "*********");
        // 取得件数を指定。指定しない場合20件になる。
        Paging paging = new Paging();
        paging.setCount(1000);
        // 自分のつぶやきを取得
        List<Status> userTimeline = twitter.getUserTimeline(paging);

        // 出力しやすいようにつぶやきを日毎にリスト単位でまとめる
        TreeMap<Date, List<Status>> treeMap = new TreeMap<Date, List<Status>>();
        for (Status status : userTimeline) {
            Date date = truncTime(status.getCreatedAt());
            List<Status> list = treeMap.get(date);
            if (list == null) {
                list = new ArrayList<Status>();
                treeMap.put(date, list);
            }
            list.add(status);
        }

        // 出力(はてなダイアリー用)
        DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd (E)");
        DateFormat statusFormat = new SimpleDateFormat("HH:mm:ss");
        for (Date date : treeMap.keySet()) {
            System.out.println("**" + dateFormat.format(date));
            List<Status> list = treeMap.get(date);
            for (Status status : list) {
                System.out.println("-" + status.getText() + " at "
                        + statusFormat.format(status.getCreatedAt()));
            }
        }
    }

    private static Date truncTime(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTime();
    }
}

変更箇所は以下の通り。

  • import文
  • 取得件数の指定方法。Twitter4jはPagingクラスを利用する。JTwitterはsetMaxResultsメソッドで指定する。
  • Statusの値の参照。JTwitterはStatusがインナークラスのためpublicなため、直接フィールドを参照できたが、Twitter4jは別クラスなので普通にgetterを利用する必要がある。JTwitterでもgetterがあるのだけど、面倒なので直接フィールドを参照してしまった。

まとめ

  • TwitterAPIはライブラリを利用することで簡単にアクセスできる。自分用のクライアントを作ろうかと思ってしまうぐらい簡単だった。時間に余裕が出来たら作ろう!
  • Twitter4jの方が高機能でOAuthにも対応している。しかも日本人が作っている。ただ、個人的にはJTwitterの方がシンプルでよいかな。ちょっとだけソースを見た感じ、JTwitterの方が見やすい感じがした。
  • 時間がないない言いながら、なにやってんだ自分。でも、とても楽しい一時を過ごせたのも事実。やっぱり好きでやる方が集中力が違うので、今後も好きなものを優先するようなカリキュラムで学ぶようにしてみよう。