ミッションたぶんPossible

どこにでもいるシステムエンジニアのなんでもない日記です。たぶん。

Twitterリスト情報をTwitter4Jで取得する。

 このプログラム、元々Scalaの勉強のつもりで書いたはずがちょっと処理が込み入っていきなりScalaで書くのが難しくなってしまったので、一旦Javaで書いてみました。ライブラリにTwitter4Jを使ってます。やってることは以下。

  • 自分のTwitterアカウントの特定のリストを取得
  • リストがフォローしているユーザー情報を列挙、はてなダイアリー用に成型
  • リストを特定条件に応じて分割、その中でアカウント作成日付でソート
  • 成型したリストをファイル出力

 結果をファイル出力したのはTwitter4J自体がコンソールに山ほどJSONメッセージとおぼしきものを吐くから。うっかり標準出力しちゃうと何がなんだか分からなくなっちゃうんですよね。混ぜるな危険。別エントリ(明日公開予定)で書いたTwitterリストを読み出してるんですが、実は分類処理が以下のソースコードだと不十分です。魔女とかきちんと分類出来ないし「鹿目ほむら」とかイレギュラーな名前には対応出来てません。あとソートもちゃんとアルゴリズム勉強した訳じゃないので、完全に自己流です。オレはこれまであまりString.format()メソッドを使った事が無かったので、結果としてほぼその練習みたいになってしまいました。慣れるとなかなか便利ですね、使えなくなる文字とかあるのが厄介ですがw。


MadokaMagicaList.java

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Vector;

import twitter4j.PagableResponseList;
import twitter4j.Query;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.User;
import twitter4j.UserList;

public class MadokaMagicaList {

	/**
	 * メインメソッド。Twitterからリスト情報を取得する。
	 * @param args
	 */
	public static void main(String[] args) {

		long cursol = -1;
		String screenName = "takigawa401";
		int listId = 0;
		Twitter twitter = new TwitterFactory().getInstance();

		try {
			// 自分のリスト情報を取得
			PagableResponseList<UserList> lists = twitter.getUserLists(screenName, cursol);
			for(UserList list :lists) {
				// リストを取得
				if("@takigawa401/madoka-magica".equals(list.getFullName())) {
					listId = list.getId();
				}
			}
			// リスト「まどか☆マギカ」に登録されたアカウントリストを取得
			PagableResponseList<User> list = twitter.getUserListMembers(listId, cursol);
			// 個別アカウント情報を取得
			boolean homuhomu = true;
			Vector<TwitterUser> vec_madoka	= new Vector<TwitterUser>();	// 鹿目まどか
			Vector<TwitterUser> vec_homura	= new Vector<TwitterUser>();	// 暁美ほむら
			Vector<TwitterUser> vec_sayaka	= new Vector<TwitterUser>();	// 美樹さやか
			Vector<TwitterUser> vec_mami	= new Vector<TwitterUser>();	// 巴マミ
			Vector<TwitterUser> vec_kyoko	= new Vector<TwitterUser>();	// 佐倉杏子
			Vector<TwitterUser> vec_qb		= new Vector<TwitterUser>();	// キュウべえ
			Vector<TwitterUser> vec_witch	= new Vector<TwitterUser>();	// 魔女
			Vector<TwitterUser> vec_other	= new Vector<TwitterUser>();	// サブキャラクター、その他

			while(homuhomu) {
				for(User user:list) {
					TwitterUser account = new TwitterUser();
					account.setDescription((user.getDescription() != null && user.getDescription().trim().length() != 0) ? user.getDescription() : "");	// 説明
					account.setFollowerCount(user.getFollowersCount());		// フォローされている
					account.setFriendsCount(user.getFriendsCount());		// フォローしている
					account.setListedCount(user.getListedCount());			// リスト
					account.setName(user.getName());						// アカウント名
					account.setScreenName(user.getScreenName());			// Twitter ID
					account.setStatusesCount(user.getStatusesCount());		// ツイート
					account.setCreatedAt(user.getCreatedAt());				// アカウント作成日
					account.setProfileImageURL(user.getProfileImageURL());	// アイコンURL

					if(isMadoka(account)) {
						vec_madoka.add(account);
					} else if(isHomura(account)) {
						vec_homura.add(account);
					} else if(isSayaka(account)) {
						vec_sayaka.add(account);
					} else if(isMami(account)) {
						vec_mami.add(account);
					} else if(isKyoko(account)) {
						vec_kyoko.add(account);
					} else if(isQb(account)) {
						vec_qb.add(account);
					} else if(isWitch(account)) {
						vec_witch.add(account);
					} else {
						vec_other.add(account);
					}
				}
				// 一度に20名の情報しか取れないので、次の20名を取得する
				if(list.hasNext()) {
					cursol = list.getNextCursor();
					list = twitter.getUserListMembers(listId, cursol);
				} else {
					// ループ終了
					homuhomu = false;
				}
			}
			StringBuffer hatenaMsg = new StringBuffer();
			for(TwitterUser account : sort(vec_madoka)) {
				hatenaMsg.append(format(account));
			}
			for(TwitterUser account : sort(vec_homura)) {
				hatenaMsg.append(format(account));
			}
			for(TwitterUser account : sort(vec_sayaka)) {
				hatenaMsg.append(format(account));
			}
			for(TwitterUser account : sort(vec_mami)) {
				hatenaMsg.append(format(account));
			}
			for(TwitterUser account : sort(vec_kyoko)) {
				hatenaMsg.append(format(account));
			}
			for(TwitterUser account : sort(vec_qb)) {
				hatenaMsg.append(format(account));
			}
			for(TwitterUser account : sort(vec_witch)) {
				hatenaMsg.append(format(account));
			}
			for(TwitterUser account : sort(vec_other)) {
				hatenaMsg.append(format(account));
			}
			// 出力
			File file = new File("export.txt");
			PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
			pw.println(hatenaMsg.toString());
			pw.close();
		} catch(TwitterException ex) {
			ex.printStackTrace();
		} catch(IOException ex) {
			ex.printStackTrace();
		}
	}

	private static boolean isMadoka(TwitterUser account) {
		return !isPerson(account, "詢子") && isPerson(account, "鹿目") || isPerson(account, "まどか");
	}
	private static boolean isHomura(TwitterUser account) {
		return isPerson(account, "暁美") || isPerson(account, "ほむら");
	}
	private static boolean isSayaka(TwitterUser account) {
		return isPerson(account, "美樹") || isPerson(account, "さやか");
	}
	private static boolean isMami(TwitterUser account) {
		return isPerson(account, "巴") || isPerson(account, "マミ");
	}
	private static boolean isKyoko(TwitterUser account) {
		return isPerson(account, "佐倉") || isPerson(account, "杏子");
	}
	private static boolean isQb(TwitterUser account) {
		return isPerson(account, "キュゥ") || isPerson(account, "キュウ") || isPerson(account, "QB");
	}
	private static boolean isWitch(TwitterUser account) {
		return isPerson(account, "魔女") || isPerson(account, "シャルロッテ");
	}
	private static boolean isPerson(TwitterUser account, String name) {
		return account.getName().indexOf(name) >= 0;
	}

	private static Vector<TwitterUser> sort(Vector<TwitterUser> accounts) {
		// 配列要素が0だったら処理終了
		if(accounts == null || accounts.size() == 0) return new Vector<TwitterUser>();

		boolean homuhomu = true;
		Vector<TwitterUser> vec_sort = null;
		while(homuhomu){
			vec_sort = new Vector<TwitterUser>();
			TwitterUser cAccount = accounts.get(0).copy();
			homuhomu = false;
			for(int i = 1; i < accounts.size(); i++) {
				if(cAccount.getCreatedAt2Date().compareTo(accounts.get(i).getCreatedAt2Date()) < 0) {
					vec_sort.add(cAccount);
					cAccount = accounts.get(i).copy();
				} else {
					homuhomu = true;
					vec_sort.add(accounts.get(i).copy());
				}
			}
			vec_sort.add(cAccount);
			accounts = (Vector<TwitterUser>)vec_sort.clone();
		}
		return accounts;
	}
	private static String format(TwitterUser account) {
		return String.format(">>\r\n<table><tr><td valign='top'><img src='%1$s'></td><td><span class='deco' style='font-weight:bold;font-size:large;'>%2$s : [twitter:@%3$s]</span></td></tr><tr><td colspan='2'><span class='deco' style='font-size:x-small;'>%4$d:ツイート %5$d:フォローしている %6$d:フォローされている %7$d:リスト %8$s〜</span></td></tr></table>\r\n<<\r\n",
				account.getProfileImageURL(),
				account.getName(),
				account.getScreenName(),
				account.getStatusesCount(),
				account.getFriendsCount(),
				account.getFollowerCount(),
				account.getListedCount(),
				account.getCreatedAt(),
				account.getDescription()
			);
	}
}


TwitterUser.java

import java.net.URL;
import java.util.Calendar;
import java.util.Date;

/**
 * Twitterアカウント情報(beanクラス)
 * @author Youichi Takigawa
 */
public class TwitterUser {
	/** アカウント名 */
	private String name;
	/** TwitterID */
	private String screenName;
	/** 説明 */
	private String description;
	/** フォローしている */
	private int friendsCount;
	/** フォローされている */
	private int followerCount;
	/** ツイート */
	private int statusesCount;
	/** リスト */
	private int listedCount;
	/** アカウント作成日 */
	private Date createdAt;
	/** プロフィール画像URL */
	private URL profileImageURL;

	/**
	 * getter:アカウント名
	 * @return name
	 */
	public String getName() {
		return name;
	}
	/**
	 * setter:アカウント名
	 * @param name name
	 */
	public void setName(String name) {
		this.name = name;
	}
	/**
	 * getter:TwitterID
	 * @return screenName
	 */
	public String getScreenName() {
		return screenName;
	}
	/**
	 * setter:TwitterID
	 * @param screenName screenName
	 */
	public void setScreenName(String screenName) {
		this.screenName = screenName;
	}
	/**
	 * getter:説明
	 * @return description
	 */
	public String getDescription() {
		return description;
	}
	/**
	 * setter:説明
	 * @param description description
	 */
	public void setDescription(String description) {
		this.description = description;
	}
	/**
	 * getter:フォローしている
	 * @return friendsCount
	 */
	public int getFriendsCount() {
		return friendsCount;
	}
	/**
	 * setter:フォローしている
	 * @param friendsCount friendsCount
	 */
	public void setFriendsCount(int friendsCount) {
		this.friendsCount = friendsCount;
	}
	/**
	 * getter:フォローされている
	 * @return followerCount
	 */
	public int getFollowerCount() {
		return followerCount;
	}
	/**
	 * setter:フォローされている
	 * @param followerCount followerCount
	 */
	public void setFollowerCount(int followerCount) {
		this.followerCount = followerCount;
	}
	/**
	 * getter:ツイート
	 * @return statusesCount
	 */
	public int getStatusesCount() {
		return statusesCount;
	}
	/**
	 * setter:ツイート
	 * @param statusesCount statusesCount
	 */
	public void setStatusesCount(int statusesCount) {
		this.statusesCount = statusesCount;
	}
	/**
	 * getter:リスト
	 * @return listedCount
	 */
	public int getListedCount() {
		return listedCount;
	}
	/**
	 * setter:リスト
	 * @param listedCount listedCount
	 */
	public void setListedCount(int listedCount) {
		this.listedCount = listedCount;
	}
	/**
	 * getter:アカウント作成日(String型)
	 * @return createdAt
	 */
	public String getCreatedAt() {
		Calendar cal = Calendar.getInstance();
		cal.setTime(createdAt);

		return String.format("%1$d/%2$s/%3$s %4$s:%5$s:%6$s",
									cal.get(Calendar.YEAR),
									formatDate(cal.get(Calendar.MONTH) + 1),
									formatDate(cal.get(Calendar.DATE)),
									formatDate(cal.get(Calendar.HOUR_OF_DAY)),
									formatDate(cal.get(Calendar.MINUTE)),
									formatDate(cal.get(Calendar.SECOND)));
	}
	/**
	 * getter:アカウント作成日(java.util.Date型)
	 * @return createdAt
	 */
	public Date getCreatedAt2Date() {
		return createdAt;
	}
	/**
	 * setter:アカウント作成日
	 * @param createdAt createdAt
	 */
	public void setCreatedAt(Date createdAt) {
		this.createdAt = createdAt;
	}
	/**
	 * getter:プロフィール画像URL
	 * @return profileImageURL
	 */
	public URL getProfileImageURL() {
		return profileImageURL;
	}
	/**
	 * setter:プロフィール画像URL
	 * @param profileImageURL ProfileImageURL
	 */
	public void setProfileImageURL(URL profileImageURL) {
		this.profileImageURL = profileImageURL;
	}
	/**
	 * クラスのコピーを返す
	 * @param account コピー元TwitterUserクラス
	 * @return コピー後のTwitterUserクラス
	 */
	public TwitterUser copy() {
		TwitterUser newAccount = new TwitterUser();
		newAccount.setName(this.name);
		newAccount.setScreenName(this.screenName);
		newAccount.setDescription(this.description);
		newAccount.setFriendsCount(this.friendsCount);
		newAccount.setFollowerCount(this.followerCount);
		newAccount.setStatusesCount(this.statusesCount);
		newAccount.setListedCount(this.listedCount);
		newAccount.setCreatedAt((Date)this.createdAt.clone());
		newAccount.setProfileImageURL(this.profileImageURL);
		return newAccount;
	}
	private String formatDate(int date) {
		if(date < 10) {
			return "0" + date;
		} else {
			return String.valueOf(date);
		}
	}
}


 こちらで書いたソースコードは近日中にScalaへの書き換えにチャレンジしたいと思っています。たぶん相当短くなると思うんですよね。ちなみにこれで作ったデータで何を書いたかは明日のお楽しみ。