( ꒪⌓꒪) ゆるよろ日記

( ゚∀゚)o彡°オパーイ!オパーイ! ( ;゚皿゚)ノシΣ フィンギィィーーッ!!!

Twitterのつぶやきをバックアップするツールをscalaで書いたの

Scalaスケーラブルプログラミングが書店にはもう並んでいるのにいまだにamazonからの発送メールが来ていないゆろよろデスおつかれザマァm9(^Д^)です。
Scalaスケーラブルプログラミング[コンセプト&コーディング] (Programming in Scala)


自分がついったをはじめて早1年半が経ちます。Postが3000を超えたんで、いままでの自分のつぶやきをバックアップしておこうと思いました。


ついったのバックアップをとるための便利なサービスが世の中にはいっぱいあるんですが、scalaの勉強がてらに書いてみました。
ここに置いてあります。


yuroyoro/TwitBackupper · GitHub

使い方

jdkmavenとgitが必要です。入れて。


まずgitからソース一式落とします。つぎにmavenでコマンドうちます。終わり。

$ git clone git://github.com/yuroyoro/TwitBackupper.git
$ cd TwitBackupper
$ mvn scala:run -DaddArgs="-i|君のIDだ"


実行するとカレントディレクトリにxmlで過去のstatusが保存されます。
csvにしたい場合は、こんな感じで。

$ mvn scala:run -DaddArgs="-i|君のIDだ|-t|csv"

中身のはなし

オプションでいろいろ出来るようにしたらコードがえらい汚くなったのが反省です。ってか半分近くがコマンドラインオプションの解析だよあーメンドクセェ。


シンプルに実装するなら、こんな感じになると思います。都合30行です。

package com.yuroyoro.twitbackupper
 
import scala.xml._
import scala.io.Source
 
object Sample {
 
  def main( args:Array[String] ):Unit= {
    val twitterId = args.first
    lazy val userTimelineUrl  = "http://twitter.com/statuses/user_timeline/%s.xml?count=200".format( twitterId )
    def getUserTimeline( maxId:Long , cnt:Int):Unit = {
      // UserTimelineを取得する
      val url = userTimelineUrl + { if( maxId > 0 ) "&max_id=" + ( maxId - 1 ) else ""}
      println( url )
      val source = Source.fromURL( url )
 
      // XML取得
      val xml = XML.loadString( source.getLines.mkString )
      xml \\ "status" size match {
        // statusが取れなくなったら終了
        case 0  => None
        // XMLと、取得した中で最小のstatus idをTupleにして返す
        case _ =>
          XML.saveFull( "%s_user_timeline_%d.xml".format( twitterId, cnt ), xml, "UTF-8", false, null)
          getUserTimeline( (xml \\ "status" \ "id" last).text.toLong , cnt + 1)
      }
    }
    getUserTimeline( -1 , 0)
  }
}


ちょっとコードの解説します。

val source = Source.fromURL( url )

TwitterのAPIのURLを引数に、scala.io.SourceオブジェクトのfromURLを利用してSourceオブジェクトを取得しています。
scala.io.SourceクラスはjavaのInputStreamをちょっと使いやすくしたようなもんです。(そうでもないかも?)

val xml = XML.loadString( source.getLines.mkString )

SourceオブジェクトからXMLの文字列を取得して、scala.xml.XMLオブジェクトを利用してscala.xml.Elemオブジェクトに変換しています。
scalaでXMLリテラルを書くと、実態はこのscala.xml.Elemオブジェクトになるのですよ。

xml \\ "status" size match {
  // statusが取れなくなったら終了
  case 0  => None
  // XMLと、取得した中で最小のstatus idをTupleにして返す
  case _ =>
    XML.saveFull( "%s_user_timeline_%d.xml".format( twitterId, cnt ), xml, "UTF-8", false, null)
    getUserTimeline( (xml \\ "status" \ "id" last).text.toLong , cnt + 1)
}

xmlの中から、xml \\ "status" sizeでstatusタグの数を数えてます。結果をパターンマッチングさせて0だったら終了。
statusが取れてたら、XMLオブジェクトのsaveFullメソッドでxmlそのものをファイルとして保存してます。
保存した後、再帰でもう一度getUserTimelineメソッドを呼び出します。


getUserTimelineメソッドの引数に、次のAPIの呼び出しで指定するmax_idを渡しています。

(xml \\ "status" \ "id" last).text.toLong 

()の中は、xml中のstatusタグをscala.xml.NodeSeqで取得して最後のstatusタグの子要素のidタグを選択してます。idタグに対して textでタグの中身を取ってlongに変換です。


簡単ですね。もっと短く書けるでしょう。