[ヅラド] JavaとTwitter4JでTwitterのツイート検索結果をHTML出力する の焼き直し。
ちがうのは、『検索結果』ではなく『指定したユーザー』のツイートを出力するところ。

ついったー(Twitter)発言まとめツール がすばらしかったので、HTML構造や CSS の id や class はこれを踏襲している。

動作確認環境

* Windows XP SP3
* JDK 1.6
* Twitter4J version 2.1.1

ソースコード


import java.io.*;
import java.text.*;
import java.util.*;
 
import twitter4j.*;
 
public class UserTimelinePrinter {
 
  public static void main(String[] args) throws Exception {
    
    String user = "nilab"; %lt;- ここに取得したいユーザー名を指定
    Integer limit = 1000;
    String outfile = "outfile.html";
    PrintStream out = new PrintStream(new File(outfile), "UTF-8");
    // PrintStream out = System.out;
    print(user, limit, out);
  }
  
  private static String KAIGYO = "\r\n";
  
  /**
   * 検索クエリにマッチしたツイートを出力します。
   * @param searchQuery 検索クエリ
   * @param limit 出力するツイートの最大数
   * @param out 出力先
   * @throws 何かエラー
   */
  private static void print(String user, Integer limit, PrintStream out) throws Exception {
    
    // APIからツイートを取得
    List<Status> tweets = getTweets(user, limit);
    
    // APIで取得すると新しいほうから古いほうへの並びになっているので、ツイートが古い順になるようソート。
    Collections.reverse(tweets);
    
    // ツイートを出力
    print(tweets, limit, out);
  }
  
  /**
   * 検索クエリにマッチしたツイートを返します。
   * @param searchQuery 検索クエリ
   * @param limit 取得する最大数
   * @return 検索クエリにマッチしたツイート
   * @throws 何かエラー
   */
  private static List<Status> getTweets(String user, Integer limit) throws Exception {
    
    List<Status> tweets = new ArrayList<Status>(); 
    try{
      // APIにてツイートを取得
      TwitterFactory factory = new TwitterFactory();
      Twitter twitter = factory.getInstance();
        final int count = 200;
        Paging paging = new Paging(1, count);
      long max_id = 0;
      while(true){
        // ツイートを取得
        ResponseList<Status> statusList =
          twitter.getUserTimeline(user, paging);
        System.out.print("*"); // APIを1回コールしたという表示
        // ツイートが取得できなくなったら終了
        if(statusList.size() == 0){
          break;
        }
        tweets.addAll(statusList);
        // ツイートが欲しい最大数以上になったら終了
        if(limit != null && limit <= tweets.size()){
          break;
        }
        // 次にAPIをたたくときは、いま取得したツイートより昔のを取得するように
        max_id = statusList.get(statusList.size() - 1).getId() - 1;
        paging.setMaxId(max_id);
        // 負荷高いと怒られるかもしれないから
        try{Thread.sleep(1000);}catch(Exception e){}
      }
      System.out.println("");
      
    }catch(Exception e){
      // エラーが発生してもスタックトレースを出力するだけで
      // ここまでに取得したツイートは返すことにする
      e.printStackTrace();
    }
    
    return tweets;
  }
  
  /**
   * ツイートを出力します。
   * @param tweets 出力するツイートの最大数
   * @param out 出力先
   */
  private static void print(List<Status> tweets, Integer limit, PrintStream out){
    out.print("<table class=\"twitter_matome\">");
    out.print(KAIGYO);
    int num = limit != null ? Math.min(limit, tweets.size()) : tweets.size();
    for(int i=0; i<num; i++){
      Status tweet = tweets.get(i);
      out.print(toString(tweet));
    }
    out.print("</table>");
    out.print(KAIGYO);
  }
  
  /**
   * 出力するツイートを文字列化します。
   * @param t 出力するツイート
   * @return 文字列化されたツイート
   */
  private static String toString(Status t){
    StringBuffer buf = new StringBuffer();
    buf.append("<tr class=\"twitter_matome_status\" id=\"" + t.getId() + "\">");
    buf.append(KAIGYO);
    buf.append("<td class=\"twitter_matome_image\">");
    buf.append("<a href=\"http://twitter.com/" + escapeHtml(t.getUser().getScreenName()) + "\">");
    // プロフィールアイコンにTwitter標準のURLを使うときはこっち↓
    buf.append("<img src=\"" + escapeHtml(t.getUser().getProfileImageURL().toString()) + "\" width=\"48\" />");
    // プロフィールアイコンに http://tweetimag.es/ を使うときはこっち↓
    //buf.append("<img src=\"http://img.tweetimag.es/i/" + escapeHtml(t.getUser().getScreenName()) + "_n\" width=\"48\" />");
    buf.append("</a>");
    buf.append("</td>");
    buf.append(KAIGYO);
    buf.append("<td class=\"twitter_matome_name\">");
    buf.append("<a href=\"http://twitter.com/" + escapeHtml(t.getUser().getScreenName()) + "\">");
    buf.append(escapeHtml(t.getUser().getScreenName()));
    buf.append("</a>");
    buf.append("</td>");
    buf.append(KAIGYO);
    buf.append("<td class=\"twitter_matome_text\">");
    buf.append(normalizeText(t.getText()));
    buf.append("</td>");
    buf.append(KAIGYO);
    buf.append("<td class=\"twitter_matome_date\">");
    buf.append("<a href=\"http://twitter.com/" + escapeHtml(t.getUser().getScreenName()) + "/statuses/" + t.getId() + "\">");
    buf.append(normalizeDate(t.getCreatedAt()));
    buf.append("</a>");
    buf.append("</td>");
    buf.append(KAIGYO);
    buf.append("</tr>");
    buf.append(KAIGYO);
    return buf.toString();
  }
  
  private static DateFormat createDateFormat(){
    // 日本人ですからJST、そしてLocaleはJAPANで。
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.JAPAN);
    df.setTimeZone(TimeZone.getTimeZone("JST"));
    return df;
  }
  
  public static final DateFormat yyyyMMddHHmmss = createDateFormat();
  
  /**
   * 出力するツイートの日時情報を文字列化します。
   * @param d 日時情報
   * @return 文字列化された日時情報
   */
  private static String normalizeDate(Date d){
    return yyyyMMddHHmmss.format(d);
  }
  
  /**
   * HTML用えすけーぷ。
   * @param s 文字列
   * @return 適切に変換された文字列
   */
  private static String escapeHtml(String s){
    return
      s.replace("&", "&amp;")
       .replace("<", "&lt;")
       .replace(">", "&gt;")
       .replace("'", "&#39;")
       .replace("\"", "&quot;");
  }
  
  /**
   * 出力するツイート内容を適切な文字列に変換します。
   * @param s ツイート内容
   * @return 適切に変換された文字列
   */
  private static String normalizeText(String s){
    s = escapeHtml(s);
    // ツイートに改行が含まれることがあるので何とかする
    s = s.replaceAll("\r\n", "\n");
    s = s.replaceAll("\r", "\n");
    s = s.replaceAll("\n", "<br />");
    return s;
  }
 
}

出力結果の例

出力結果の例(HTMLの中身)


<table class="twitter_matome">
<tr class="twitter_matome_status" id="18099257860">
<td class="twitter_matome_image"><a href="http://twitter.com/nilab"><img src="http://a1.twimg.com/profile_images/25307672/zlashdot_profile_normal.jpg" width="48" /></a></td>
<td class="twitter_matome_name"><a href="http://twitter.com/nilab">nilab</a></td>
<td class="twitter_matome_text">あいたくて あいたくて ふるえる</td>
<td class="twitter_matome_date"><a href="http://twitter.com/nilab/statuses/18099257860">2010-07-09 16:02:08</a></td>
</tr>
<tr class="twitter_matome_status" id="18099592025">
<td class="twitter_matome_image"><a href="http://twitter.com/nilab"><img src="http://a1.twimg.com/profile_images/25307672/zlashdot_profile_normal.jpg" width="48" /></a></td>
<td class="twitter_matome_name"><a href="http://twitter.com/nilab">nilab</a></td>
<td class="twitter_matome_text">姫家具の話。このまえTVでも白馬の王子様とお姫様なりたい妄想と家具の話してた。</td>
<td class="twitter_matome_date"><a href="http://twitter.com/nilab/statuses/18099592025">2010-07-09 16:09:30</a></td>
</tr>
<tr class="twitter_matome_status" id="18102844267">
<td class="twitter_matome_image"><a href="http://twitter.com/nilab"><img src="http://a1.twimg.com/profile_images/25307672/zlashdot_profile_normal.jpg" width="48" /></a></td>
<td class="twitter_matome_name"><a href="http://twitter.com/nilab">nilab</a></td>
<td class="twitter_matome_text">list of foursquare badges « presence: a journal by tony felice http://tonyfelice.wordpress.com/foursquare/</td>
<td class="twitter_matome_date"><a href="http://twitter.com/nilab/statuses/18102844267">2010-07-09 17:25:29</a></td>
</tr>
<tr class="twitter_matome_status" id="18111904801">
<td class="twitter_matome_image"><a href="http://twitter.com/nilab"><img src="http://a1.twimg.com/profile_images/25307672/zlashdot_profile_normal.jpg" width="48" /></a></td>
<td class="twitter_matome_name"><a href="http://twitter.com/nilab">nilab</a></td>
<td class="twitter_matome_text">foursquareでワールドカップの試合中に”Go+国名”とshoutすると『Bing Home Turf』バッジがもらえる? http://www.nilab.info/zurazure2/001129.html</td>
<td class="twitter_matome_date"><a href="http://twitter.com/nilab/statuses/18111904801">2010-07-09 20:51:47</a></td>
</tr>
<tr class="twitter_matome_status" id="18114751452">
<td class="twitter_matome_image"><a href="http://twitter.com/nilab"><img src="http://a1.twimg.com/profile_images/25307672/zlashdot_profile_normal.jpg" width="48" /></a></td>
<td class="twitter_matome_name"><a href="http://twitter.com/nilab">nilab</a></td>
<td class="twitter_matome_text">Twitter Search は最近のツイートしか検索できないからつらいな。。。</td>
<td class="twitter_matome_date"><a href="http://twitter.com/nilab/statuses/18114751452">2010-07-09 21:41:39</a></td>
</tr>
</table>

# いまさらだけど、もしかしてTwitterユーザーがアイコンをアップロードしなおしたら画像参照できなくなるんじゃ・・・

参考

* Twitter4J - A Java library for the Twitter API

tags: zlashdot Java Java Twitter Twitter4J

Posted by NI-Lab. (@nilab)