twitter4jとScalaでネトストしたい!!
好きな人のツイートは全部見たいって思うことありますよね。
ツイ消しも画像も含めて全部見たいって思いますよね。
ということで、ガチネトストプログラムを作りました。
ディレクトリ構成
sbtを使っていくので、ディレクトリ構成は以下のようになります
/ |- src/ |- main/ |- scala/ |- main.scala |- connect.scala |- stream.scala |- files/ |- build.sbt |- twitter4j.properties
build.sbt
build.sbtに必要なライブラリの依存関係を書いていきます。
とはいえ、twitter4j以外使わないので以下のようになります。
lazy val root = (project in file(".")). settings( inThisBuild(List( scalaVersion := "2.11.8", version := "0.1.0-SNAPSHOT" )), name := "stalker", libraryDependencies ++= Seq( "org.twitter4j" % "twitter4j-core" % "4.0.4" , "org.twitter4j" % "twitter4j-stream" % "4.0.4" ) )
twitter4j.properties
次に、twitter4jの設定を書いていきます。
debug=true oauth.consumerKey=YOUR_CONSUMERKEY oauth.consumerSecret=YOUR_CONSUMERSECRET oauth.accessToken=YOUR_ACCESSTOKEN oauth.accessTokenSecret=YOUR_ACCESSTOKENSECRET twitter4j.loggerFactory=twitter4j.NullLoggerFactory
自分のconsumerKeyその他は頑張って探してください。
普通にググれば取り方が出て来るはずなので。
本体
コードそのものを書いていきます。
connect.scala
まずは、screen name(@以下の部分)からID(アカウントに固有の数字)を求めましょう。
そうすることで、@以下を変えられたとしても継続してストーキングすることができるようになります。
package connect import twitter4j._ object TwitterConnector{ val factory = new TwitterFactory() val twitter = factory.getInstance() def getId(names : List[String]) :List[Long] ={ return names.map(x => twitter.showUser(x).getId()) } }
これで終わりです。ライブラリって便利ですね。
StringのListの形で引数を与えると、Id(Long)のListの形で返って来るようになっています。
本当はError処理とかするべきなんですが、面倒なので割愛します。
streaming.scala
次はstreamingでツイートを取得しましょう。
好きな人が呟いた瞬間にツイートを取って来ることができるようになります。
package stream import java.io._ import java.net.URL import twitter4j._ import scala.sys.process._ import scala.language.postfixOps class StalkerListener extends StatusListener{ override def onDeletionNotice(statusDeletionNotice :StatusDeletionNotice) ={ // ツイートが削除された時に発動します // 今回は無視 } override def onScrubGeo(userId :Long, upToStatusId :Long) ={ // 今回は無視 } override def onStatus(status :Status) ={ // ツイートされた時に発動します val user = status.getUser() val file = new File(new File(".").getCanonicalPath, s"files/${user.getId}.txt") s"echo ${status.getText()}" #>> file ! val medias = status.getMediaEntities().map(x => x.getMediaURL()).toList medias.zipWithIndex.foreach{case(x:String, i:Int) => val stream = new URL(x).openStream val buf = Stream.continually(stream.read).takeWhile( -1 != ).map(_.byteValue).toArray val nameOnly = x.drop(x.lastIndexOf('/')) val fileName = nameOnly.split('.').mkString(i.toString ++ ".") val dir = (new File(".").getCanonicalPath).toString ++ s"/files/${user.getId}/" s"mkdir -p ${dir}"! val imageFile = new File(dir, s"${fileName}") val bw = new BufferedOutputStream(new FileOutputStream(imageFile)) stream.close() bw.write(buf) bw.close } } override def onTrackLimitationNotice(numberOfLimitedStatuses :Int) ={ // 今回は無視します } override def onException(e :Exception) ={ // 例外が起こった場合に通知されます // 今回はスタックトレースでも出しておきます e.printStackTrace(); } override def onStallWarning(e: StallWarning) = { // 変わったらしい } }
streaming APIを使うときにはListenerというものが必要になるので、それの定義を行なっています。
監視しているstreamにツイートがされたときに、そのツイート引数としてonStatus関数が呼ばれます。
そのツイート主のIDを取ったtxtファイルを作成し、その中にツイートの内容をリダイレクトすることで書き込んでいます。
JavaのFileとかを使って追記しても良かったんですがめんどくさかったのでscala.sys.processを使って、リダイレクトして書き込んでいます。
後半部は画像ファイルを落として来る処理ですね。
動画が上がったときに関してはテストしていないので、どうなるのかわかりませんが、画像はいい感じに保存されるようになっています。
main.scala
さて、あとはstreamを監視するだけですね。
import java.io._ import twitter4j._ import scala.io._ import scala.sys.process._ import scala.language.postfixOps import stream._ import connect._ object Main{ def main(args: Array[String]) :Unit = { // val newName = ["hoge","fuga"] // val firstIds = TwitterConnector.getId(newName) val idFile = new File(new File(".").getCanonicalPath,"files/ids.txt") println(idFile) // firstIds.foreach{firstId => // s"echo ${firstId}" #>> idFile ! // } val idSource = Source.fromFile(idFile.getPath()) val ids = idSource.getLines.map{x => x.toLong}.toArray idSource.close val twitterStream : TwitterStream = new TwitterStreamFactory().getInstance(); val listener = new StalkerListener() twitterStream.addListener(listener) val fq = new FilterQuery(ids: _*) println(ids.mkString(",")) println("start Streaming") twitterStream.filter(fq) } }
コメントアウトを全て外せば、newNameに含まれているscreenNameからIDを取得して、検索対象に含むようになります。
twitterStreamにはuser,filterなど複数あるのですが、今回はIDを指定してツイートを取得したいので、filterを用いています。
詳しくは公式のドキュメントを。
使い方
これで完成です。
あとはrootディレクトリにて
sbt run
しておくだけで、files/(id).txtにツイートが収集できます。
画像はfiles/images/(id)/以下に収集されます。
ただし、仕様上filterだと非鍵垢のツイートしか取得できないので、鍵垢に対してはuser streamを用いた上でonStatusではじくようにしたら良いのではないでしょうか。
最後に
これを用いた際に生じる不都合については一切責任は追いませんので、ご容赦ください。
それでは、楽しいネトストライフを!