固定ハッシュタグ複数定点観測用追跡器作りました
[Groovy][Twitter] 固定ハッシュタグ複数定点観測用追跡器作りました。
#devsumi とか、#jawsug とか、ずっと追跡し続けたい固定ハッシュタグは、いくつかあるものです。
自分のPDAデバイスそのものでハッシュタグを追跡すると、途中を見落とししたり、電池切れで追えなくなったりします。
このような問題があるため、できれば追跡そのものは自宅のマシンで無人で行い、その結果を随時PDAで閲覧する方式の
方が便利が良いと思います。また、デブサミのような大規模なセミナーだと複数のハッシュタグを追わないと追いつかないケースもあり、
複数のハッシュタグを同時に記録し続けたいこともあります。
そんな目的のために複数固定ハッシュタグ追跡器作りました。
複数のハッシュタグを追跡し続けてログを付けます。
準備:
TAGS.txt という、事前に追跡しておきたい、お好みのハッシュタグテーブルを作ります、1件1行です。
実は、登録する単語は、ハッシュタグである必要はなく、また日本語でもOKです。TwitterはStream APIで検索する場合は、ハッシュタグのみが可能です、逆に通常の検索の場合は、日本語でも何でも探せますが、その代わり、即時性がありません、スピードと多様性はトレードオフですが、ここでは、記録性を優先するので、通常の検索で追っています、従って5分から10分程度の遅延を伴います。
動かし方: TAGS.txt と同じディレクトリーで HashTagChaser.groovyを動かします。
すると#jggug.log といった名前のログファイルが作られます。
この結果をPDAでコンファレンス会場などで閲覧すればOKです。
で、この結果を外からどうやって見るかですが、簡単なことですが単にDropboxのディレクトリー内で動かすだけです。
巨人の肩の上に立つ訳です。
Dropboxの閲覧はPCでもiPhoneでも可能です。
Groovyをインスコするのはイヤという人のために、独立したjar版も作っておきました。
HashTagChaser.jar
こちらは、Javaだけあれば単独で動きます。
また、Dropboxのアカウントをお持ちでない人は、よかったらこちらからアカウント取得してください。
http://db.tt/8BiuAAb
招待ありでアカウントを作成すると、紹介者と招待された人の双方に、初期値の2Gに、プラスで250MB の容量追加が与えられます。招待者の私(nemo10)も250MBメリットがあります。
TAGS.txt ファイルの例 (単なる単語でもOK)
#jggug #javareading #devsumi アイマス
それぞれのタグ名に応じて #jggug.log とか #javareading.log などのログが時々進んでいきます。
実行コード HashTagChaser.groovy >|groovy| import static groovy.util.GroovyCollections.combinations import groovy.util.Eval import groovyx.gpars.GParsExecutorsPool // 検索単語エンコード String.metaClass.encodeIt = { -> return URLEncoder.encode(delegate,"UTF-8").replaceAll(/\.log/,'') } assert "ツイッター.log".encodeIt() == "%E3%83%84%E3%82%A4%E3%83%83%E3%82%BF%E3%83%BC" // 発言番号を取得 String.metaClass.getNum = { -> if (delegate ==~/.*tag.*search.*/) { //println "発言番号取得 " + delegate.replaceAll(/.*:([0-9]+)/, '$1') return delegate.replaceAll(/.*:([0-9]+)/, '$1') } else { return delegate } } assert "tag:search.twitter.com,2005:16060673193".getNum() == "16060673193" // 発言IDを取得 String.metaClass.getID = { -> if (delegate ==~/.*\(.*\)/) { //println "ID取得 " + delegate.replaceAll(/([^ ]+) \(.*/, '$1') return delegate.replaceAll(/([^ ]+) \(.*/, '$1') } else { return delegate } } assert "nemo_kaz (kazuo nemoto)".getID() == "nemo_kaz" // 日本語チェック String.metaClass.isKana = { -> if (delegate ==~/.*[あ-んア-ン].*/) { return "OK" } else { return "NG" } } assert "tag:search.twitter.com,2005:16060673193".getNum() == "16060673193" ////////////////////////////////////// def loggers=[] def file = new File("TAGS.txt") file.eachLine{logname -> loggers.add (new Agent(logname+".log")) } for (;;){ try { loggers.each {clazz -> clazz.logIt() } Thread.sleep(1000*60*8) } catch (Exception e) { println "Exception"+e } } class Agent{ String url File fhandle def counter def logname Agent(String input) { logname=input fhandle = new File(input) //fhandle.write("aaa\n") } def logIt() { def feed = null print " " def atom2 = ("http://search.twitter.com/search.atom?q="+logname.encodeIt()).toURL().text feed = new XmlSlurper().parseText(atom2) feed.entry.collect{it}.reverse().each { if (counter < it.id.toString().getNum().toLong()) { counter = it.id.toString().getNum().toLong() if (it.title =~ /^RT/) {} else { println "■"+it.title +" "+it.author[0].name.toString().getID() fhandle.append("■"+it.title +" "+it.author[0].name.toString().getID()+"\n" ) } //RTは割愛 } else { Thread.sleep(1000*5) } }//each }//logIt() } //class Agent