Twitterでフォローされているがしていない人を調べるプログラムをScalaで作成した:100人乗っても大丈夫バージョン

前回 のプログラムはフォロワー、フレンドともに100人まで対応の手抜きバージョンだった。その後、なぜかフォロワーが100人を超えたため、より多くの人数に対応できるようにした。名付けて「100人乗っても大丈夫」バージョン。

TwitterFollowers.scala

import scala.io.Source
import scala.xml.XML

object TwitterFollowers {
  def main(argv:Array[String]) {
    if (argv.length == 0) return
    
    val id = TwitterId(argv.first)
    val user = new TwitterUser(id)
    
    printNames("フォローしているが、されていない", user.friendsButNotFollowers)
    printNames("フォローされているが、していない", user.followersButNotFriends)
  }
  
  def printNames(title:String, names:TwitterNames) = {
    println(title)
    println(names.asString)
    println
  }
}

class TwitterUser(id:TwitterId) {
  val friends:TwitterNames = getFriends
  val followers:TwitterNames = getFollowers
  
  def friendsButNotFollowers = (friends - followers)
  
  def followersButNotFriends = (followers - friends)
  
  def getFriends = {
    val url = "http://api.twitter.com/1/statuses/friends/" +
      id.screenName + ".xml"
    TwitterNames(getNames(url))
  }
  
  def getFollowers = {
    val url = "http://api.twitter.com/1/statuses/followers/" +
      id.screenName + ".xml"
    TwitterNames(getNames(url))
  }
  
  def getNames(url:String) = {
    val names = getNamesIter(url, "-1")
    names.sort((s1, s2) => (s1 compareTo s2) < 0)
  }
  
  def getNamesIter(url:String, cursor:String):List[String] = cursor match {
    case "0" => List()
    case _   => {
      val pagingUrl = url + "?cursor=" + cursor
      val xml = getXml(pagingUrl)
      val names = (xml \\ "screen_name").map(_.text).toList
      val nextCursor = (xml \ "next_cursor").text
      names ::: getNamesIter(url, nextCursor)
    }
  }
  
  def getXml(url:String) = {
    val src = Source.fromURL(url)
    XML.loadString(src.getLines.mkString)
  }
}

case class TwitterId(screenName:String)

case class TwitterNames(names:List[String]) {
  def -(other:TwitterNames) = TwitterNames(names.filter(!other.contains(_)))
  
  def asString = names match {
    case Nil => "";
    case _   => names.reduceLeft{_ + "\n" + _};
  }
  
  def contains(name:String) = names.contains(name)
}

100人以上を取得するにはページング機能というものを有効にする。これはTwitter APIのURLの末尾に「?cursor=-1」を付ければよい。すると/users_list/next_cursorのテキスト部に次のページを取得するための値が返ってくるため、次回からはそれを「?cursor=値」として指定する。もし次ページが存在しないときは、next_cursorとして“0”が返ってくる。この動作はTwitterUserクラスのgetNamesIterメソッドが担当している。

よし、これでフォローされているがしていないユーザーを調べてフォローできるぞ! 将来的にはこれをWeb上で動かせるようにして、みんなで使えるようにしよう!と出来もしない野望に燃えていたところ、Refollow というサイトでフォロー関係を調べられることを発見。やっぱりなー。あると思ったんだ、Webサービス(^^;) というわけで、皆さんは Refollow を使ってくださいね。