ラベル Twitter の投稿を表示しています。 すべての投稿を表示
ラベル Twitter の投稿を表示しています。 すべての投稿を表示

2014年12月11日木曜日

Pythonを使ってTwitterでOAuthのログインをするためのクラスを作る

TwitterのOAuthでログインできるようにしておくと色々便利なのはAppEngineで経験済みなので,そのためのライブラリを作成してみる.

基本的にはこちらのページを参考にこしらえた.
pythonでoauth認証
事前準備は,
さくらインターネットでPython2.7を使ってTwitterAPIを叩くを参考のこと.
なお,Twitterとの接続に使うのは,python-twitter.

まず,
http://syncer.jp/twitter-api-create-application
を参考に,Twitterのアプリを作成しておく.
ただし,アプリ作成の場所が,分かりづらくなったので,直接こちらにアクセスした方がよい.
https://apps.twitter.com/
Twitter社はアプリ作らせる気がないのか?と思うくらい分かりづらい.

このとき,CallBackURLにアプリのCGIのURLを指定しておくことを忘れずに.
後から変えられるけど.

次に,アプリ作成に入る.
まず,sqliteを使うので,oauth.dbというファイルを作っておいて,
$ sqlite3 oauth.db
sqlite> CREATE TABLE oauth (oauth_token text primary key,oauth_token_secret text);
sqlite> CREATE TABLE user (id integer primary key, consumer_key text, consumer_secret text, access_token text, access_token_secret text, screen_name text, image_url text, time datetime);
sqlite> .exit
として,データベースを作成しておく. 次に,TwitterLogin.pyを作成.
ここが今日のハイライト.


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import site
site.addsitedir('/home/username/lib/python')

import os
import cgi
import cgitb
import oauth2 as oauth
import sqlite3
import twitter
import datetime
from Cookie import SimpleCookie
from twitter import TwitterError

request_token_url = 'https://api.twitter.com/oauth/request_token'
access_token_url = 'https://api.twitter.com/oauth/access_token'
authenticate_url = 'https://api.twitter.com/oauth/authorize'

class TwitterLogin:
    def __init__(self, db_name, consumer_key, consumer_secret):
        self.db_name = db_name
        self.consumer_key = consumer_key
        self.consumer_secret = consumer_secret

    def getOAuth(self):
        consumer = oauth.Consumer(key=self.consumer_key, secret=self.consumer_secret)
        client = oauth.Client(consumer)
        # reqest_token    
        resp, content = client.request(request_token_url, 'GET')
        request_token = dict(self.parse_qsl(content))
       
        #call_back="http://torix.sakura.ne.jp/twitter/signin.cgi"
   
        # request_token    
        con = sqlite3.connect(self.db_name)
        con.execute(u'insert into oauth values (?, ?)', (request_token['oauth_token'], request_token['oauth_token_secret']))
        con.commit()
        con.close()

        #
        url = '%s?oauth_token=%s' % (authenticate_url, request_token['oauth_token'])
        print 'Content-type: text/html; charset: utf-8\n\n'
        print
        print '' % url


    def getApi(self):
        access_token, access_token_secret, screen_name, image_url = self.loadAccessToken()
        self.screen_name = screen_name
        self.image_url = image_url
        if not access_token == None:
            # Cookieを使った認証
            api = twitter.Api(consumer_key=self.consumer_key,
                              consumer_secret=self.consumer_secret,
                              access_token_key=access_token,
                              access_token_secret=access_token_secret)
            return api
        elif 'QUERY_STRING' in os.environ:
            # CallBackを使った認証
            query = cgi.parse_qs(os.environ['QUERY_STRING'])
            if len(query) == 0:
                return None
            # oauth_token_secret を取得
            con = sqlite3.connect(self.db_name)
            oauth_token_secret = con.execute(
                u'select oauth_token_secret from oauth where oauth_token = ?'
                , [query['oauth_token'][0]]).fetchone()[0]
            con.close()

            if oauth_token_secret == None:
                return None

            # Access_token と access_token_secret を取得
            consumer = oauth.Consumer(key=self.consumer_key, secret=self.consumer_secret)
            token = oauth.Token(query['oauth_token'][0], query['oauth_verifier'][0])
            client = oauth.Client(consumer, token)
            resp, content = client.request(
                access_token_url, "POST", body="oauth_verifier=%s" % query['oauth_verifier'][0])
            access_token = dict(self.parse_qsl(content))

            # access_tokenの保存
            api = twitter.Api(consumer_key=self.consumer_key,
                        consumer_secret=self.consumer_secret,
                        access_token_key=access_token['oauth_token'],
                        access_token_secret=access_token['oauth_token_secret'])
            try:
                user = api.VerifyCredentials()
                uid = user.GetId()
                self.screen_name = user.screen_name
                self.image_url = user.profile_image_url
                self.saveAccessToken(uid, access_token['oauth_token'], access_token['oauth_token_secret'], self.screen_name, self.image_url)
            except TwitterError:
                return None
            return api
        else:
            return None

    #save access token to db
    def saveAccessToken(self, uid, access_token, access_token_secret, screen_name, image_url):

        con = sqlite3.connect(self.db_name)
        c = con.cursor()
        #print datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        c.execute(u"replace into user (id, consumer_key, consumer_secret, access_token, access_token_secret, screen_name, image_url, time) values (?, ?, ?, ?, ?, ?, ?, datetime(?))", [uid, self.consumer_key, self.consumer_secret, access_token, access_token_secret, screen_name, image_url, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")])
        con.commit()
        c.close()

        ck = SimpleCookie()
        ck.load(os.environ.get("HTTP_COOKIE",""))
        ck["id"] = uid
        ck["token"] = access_token
       
        now_date = datetime.datetime.now()
        expires = now_date
        expires = expires + datetime.timedelta(seconds=+(60*60*24))
        expires = expires.strftime('%a, %d-%b-%Y %H:%M:%S %Z')
           
        ck["id"]["expires"] = expires
        ck["token"]["expires"] = expires
        print ck.output(["id", "expires"])
        print ck.output(["token", "expires"])

    #load data from data base
    def loadAccessToken(self):
        ck = SimpleCookie()
        ck.load(os.environ.get("HTTP_COOKIE",""))
        if ck.has_key("id") and ck.has_key("token"):
            id = int(ck["id"].value)
            token = ck["token"].value
            access_token = None

            con = sqlite3.connect(self.db_name)
            c = con.cursor()
            c.execute(u'select access_token, access_token_secret, screen_name, image_url from user where id=%d'% id)
            for row in c:
                access_token = row[0]
                access_token_secret = row[1]
                self.screen_name = row[2]
                self.image_url = row[3]
            con.close()

            if not access_token == None and access_token == token:
                return access_token, access_token_secret, self.screen_name, self.image_url
            else:
                return None, None, None, None
        else:
            return None, None, None, None



    def parse_qsl(url):
        param = {}
        for i in url.split('&'):
            _p = i.split('=')
            param.update({_p[0]: _p[1]})
        return param

    def parse_qsl(self, url):
        param = {}
        for i in url.split('&'):
            _p = i.split('=')
            param.update({_p[0]: _p[1]})
        return param

getOAuthで,OAuth認証を行うページに飛ばして,callbackで戻ってきたときにgetApiで捕まえる.
それ以降は,UserIDとAccessTokenをCookieに保存することでSession管理をすることにする.
本当は独自のsessionIDを発行した方がいいのかもしれない.

次に,実際にログインするところを作ってみよう. 使い方は以下の通り.
なお,consumer_keyとconsumer_secretはTwitterで登録したアプリのものをつかう.
callback urlがhttp://xxx.xx.xx/twitter/login.cgi
そのURLでアクセスできるcgiを以下のように作成する.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import site
site.addsitedir('/home/username/lib/python')
from TwitterLogin import TwitterLogin

consumer_key = 'consumer_key'
consumer_secret = 'consumer_secret'

if __name__ == '__main__':
    twitter = TwitterLogin('oauth.db', consumer_key, consumer_secret)
    api = twitter.getApi()
    print 'Content-type: text/html; charset: utf-8\n\n'
    if api == None:
        print 'Fail to login'
    else:
        user = api.VerifyCredentials()
        print user.screen_name

これで,認証したアカウントのスクリーンネームが表示されるはず.

APIの使い方は,以下を参照.
https://python-twitter.googlecode.com/hg/doc/twitter.html

色々問題はあるかもしれないけど,とりあえず動いたので,満足.




2014年12月8日月曜日

さくらインターネットでPython2.7を使ってTwitterAPIを叩く

タイトル長いな.
世界の中心で愛を叫ぶみたいになってもうた.

さて,TwitterAPIを使ったツイート収集だけど,最近検索APIで1週間分なら無制限にとれるっぽいことが分かったので,これを使って遊んでみよう計画.

最近Pythonも気になるので,Pythonでさくらサーバ使って遊んでみる.


PythonでTwitterのAPIをたたいてみよう-セットアップ偏
を参考にしてみる.

なお,Twitterとの接続に使うのは,python-twitter.

$ setenv PYTHONPATH /home/username/lib/python

$ wget --no-check-certificate github.com/simplegeo/python-oauth2/archive/master.zip
$ unzip master.zip
$ cd  python-oauth2-master/
$ ./setup.py install --home=~

$ cd ../
$ rm master.zip
$ wget --no-check-certificate https://github.com/bear/python-twitter/archive/master.zip
$ unzip master.zip
$ cd  python-twitter-master/
$ ./setup.py install --home=~

$ cd ../
$ rm master.zip
$ wget --no-check-certificate https://github.com/jcgregorio/httplib2/archive/master.zip
$ unzip master.zip
$ cd  httplib2-master/
$ ./setup.py install --home=~

$ cd ../
$ rm master.zip
$ wget --no-check-certificate https://pypi.python.org/packages/source/s/simplejson/simplejson-3.6.5.tar.gz
$ tar zxvf simplejson-3.6.5.tar.gz
$ cd simplejson-3.6.5/
$ ./setup.py install --home=~

これで必要なファイルはダウンロード完了.
ここで適当なアプリを作成しておく.

ConsumerKeyとConsumerSecret,AccessToken, AccessTokenSecretを獲得して,
以下のようなcgiを作成.

#!/usr/bin/env python
# -*- coding: utf-8 -*-


#!/usr/bin/env python
# -*- coding: utf-8 -*-


import site
site.addsitedir('/home/username/lib/python')

import sys
import twitter

print ('Content-type: text/html; charset=UTF-8\n\n')

CONSUMER_KEY = ''                             # Consumer Key
CONSUMER_SECRET = ''         # Consumer Secret
ACCESS_TOKEN = '' # Access Token
ACCESS_TOKEN_SECRET = ''         # Accesss Token Secert

api = twitter.Api(consumer_key=CONSUMER_KEY,
                      consumer_secret=CONSUMER_SECRET,
                      access_token_key=ACCESS_TOKEN,
                      access_token_secret=ACCESS_TOKEN_SECRET)


tlAry = api.GetSearch(term="keyword",count=100)
tweetlist=[]
for s in tlAry:
    if not s.text.startswith('RT @'):
        tweetlist.append(s.text)
        print s.text.encode('utf-8')


これで,keywordを含むツイートを取得可能.
ポイントの一つが,
import site
site.addsitedir('/home/username/lib/python')
の部分.
これを書いておかないとライブラリを読みに行ってくれないので注意.
さくらインターネットでは,ライブラリはローカルに置くしかないので,この作業をやらないと,twitterライブラリがimportできません.




2013年6月24日月曜日

Twitter4jを使った検索

研究用に,指定した単語を含む全ツイートを取得するプログラムを作成. API1.1対応.

cronを使って一分ごとに走らせたりすると,特定の単語に関するツイートは網羅的に集められそう.

ちなみに, Twitter4jバージョン3.0.3を利用しています.


import java.util.List;

import twitter4j.Query;
import twitter4j.QueryResult;
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.conf.Configuration;
import twitter4j.conf.ConfigurationBuilder;

/**
 * 任意の単語でツイートを検索する.<br>
 * @author tori
 * @date 2013/06/11
 */
public class TwitterSearcher {

 public static void main(String[] args){
  TwitterSearcher ts = new TwitterSearcher(consumerKey, consumerSecret, accessToken, accessTokenSecret);
  try{
   List<Status> searchResultList = ts.search(searchword);
   for(Status status:searchResultList){
    System.out.printf("%s\t%s\n", status.getUser().getScreenName(), status.getText());
   }
  }catch(TwitterException e){
   e.printStackTrace();
  }
 }
 
 Twitter twitter;
 
 /**
  * ConsumerKey,ConsumerSecret,AccessToken,AccessTokenSecretを利用して検索インスタンスを作成
  * @param consumerKey
  * @param consumerSecret
  * @param accessToken
  * @param accessTokenSecret
  */
 public TwitterSearcher(String consumerKey, String consumerSecret, String accessToken, String accessTokenSecret) {
  Configuration configuration = new ConfigurationBuilder().setOAuthConsumerKey(
    consumerKey).setOAuthConsumerSecret(consumerSecret)
    .setOAuthAccessToken(accessToken)
    .setOAuthAccessTokenSecret(accessTokenSecret).build();

  TwitterFactory factory = new TwitterFactory(configuration);

  twitter = factory.getInstance();
 }

 /**
  * 単語を指定して検索を行う.獲得数は最大の100
  * @param word
  * @return
  * @throws TwitterException
  */
 public List<Status> search(String word) throws TwitterException{
  return search(word, 100);
 }

 /**
  * 単語を指定して検索を行う.任意の数を獲得する.(最大は100)
  * @param word
  * @param page
  * @param count
  * @return
  * @throws TwitterException
  */
 public List<Status> search(String word, int count) throws TwitterException{
  Query query = new Query(word);
  query.setResultType(Query.RECENT);
  query.setCount(count);
  QueryResult result = twitter.search(query);
  return result.getTweets();
 }

}


2012年12月10日月曜日

東日本大震災時にデマはリツイートされたのか?

先日J-WAVEのJam the worldという番組に出て,Twitter上のデマについて話をしてきました. その際に,「案外デマはリツイートされなかったんですよ」という話をしたところ,パーソナリティの竹田さんに「そんなことないでしょう」と言われてしまいました.

確かに,これだけTwitter上ではデマが多い,問題だと言われている中でそんなこと言っても信じてもらえないのは無理もない. というわけで,ちゃんとデータで示してみます. 

我々の収集した東日本大震災時のTwitterのデータの中から,3月11日~3月17日に投稿されたツイートのうち,1000件以上リツイートされたものを取り出して,その中身がデマかどうかを確認します. ちなみに,確認の方法は目視.いわゆる「根性マイニング」です.
データは全部で1983ツイートありました. さて,この中で明らかなデマはいくつあったでしょうか.

実は正解は12ツイートです.つまり,全体の1%にも満たない訳ですね.
もちろん,1000リツイート以下のツイートにはもっとデマが含まれていたツイートもあるでしょうが, 大規模に広がったツイートの中にはほとんどデマはなかったということです.

その12ツイートの内容は,
  • 災害対策予備費が削減されていた
  • 自衛隊機は支援物資の空中投下ができない
  • 築地市場では魚が余っている
  • 枝野官房長官が105時間ぶりに寝た
  • 携帯の0か#を長押しすると防犯ブザーが鳴る
  • 政治家等の動向
  • コスモ石油の火災で有毒ガスが発生
  • 千葉製鉄所で爆発
です.最初の二つについては,正確なところは私もよく分かっていないのですが,どうもデマのようです.デマじゃないとしたら,デマの数は計9ツイートになります.
ちなみに,コスモ石油のツイートが1357リツイート,一番リツイートが多かった災害対策予備費の件が2ツイートあり,合計11062リツイートです.
2000近くのツイートを確認しましたが,デマを否定するツイートは多く存在していたが,そのデマ自体はそれほど多くリツイートされなかったという現状が見えてきます.

ちなみに,震災期間中に最も多くリツイートされたのは,こちらのツイートです.
知り合いの福島の方が、「被ばくで怖いのは、健康被害じゃなくて、差別」と仰っています。彼女は現在も福島にいます。どうか「自分は差別しない!」と胸を張って言って下さる方、RTを下さい。偽善でも、その数を見せて差し上げたいんです。
当時で2万リツイート超.2012年12月現在では5万回もリツイートされています. 皆さん覚えていましたか?

ちなみに,これ以外に多かったのが都内での買いだめへの警告です.
買いだめをやめよう,買いだめするこんな恥ずかしい人を見かけた,ウエシマ作戦(どうぞどうぞと譲り合う)などが,当時大量にRTされていたようでした.皆さん覚えていましたか?私は忘れていました.
当時大部分を占めていたはずの内容を忘れて,デマがあったことだけを覚えているというのは,Twitter上でデマが出回ったぞという情報が多く存在したためかもしれません. 人間の記憶なんてその程度の曖昧な者である,ということかもしれませんね.
ちなみに,2012年12月7日に発生した地震で,Twitter上に「家に押しつぶされた」とツイートした高校生がいて「やっぱりツイッターはデマばっかりだ」という印象を持った人も多いかもしれません. でも,それ以外にデマはあったんでしょうか?少なくとも私が見た限りでは発見できませんでした.


ツイッターはデマが多いメディアなのか,それとも,そう思い込んでいるだけなのか.
データを分析すると見えてくる真実は多いです.
ツイッターを「デマ発生器だ」などと揶揄せずに,情報拡散に役立つソーシャルメディアとしての側面をクローズアップして,今後も発生するであろう震災に向けて,よりよい使いかたを模索していきたいものです.


そんな震災時のソーシャルメディアの利用に興味のある研究者はこちらまで.
ソーシャルメディアの基本特性をデータやモデルから明らかにしたい学生の皆様はこちらまで
ご連絡を.

2011年10月5日水曜日

仕様変更に伴うTwitterStreamingReaderの変更

どうやら,Twitterで仕様変更があったらしく,
StreamingAPIを使ったデータの読み込みを変更しなければいけなくなりました.

具体的には,TwitterのDeveloper用ブログにあるとおり,
All our Streaming API products are now supporting SSL and we've just updated the Streaming API Methods, User Streams and Site Streams documentation pages accordingly. As we're planning to sunset HTTP support in about a month, we strongly encourage you to switch to SSL (HTTPS) as soon as possible, especially if you're still authenticating your Streaming API requests with Basic Auth.
ということで,SSLを使ってね,ということらしいです.

そんなわけで,以前作成したTwitterStreamReaderではなく,新しいバージョンを使ってください.

変更点は,SSLに対応させただけだけど.
得られるTweetデータはJSON形式なので,jsonicなどを使ってJSONの処理をちゃんとしないと日本語が文字化けするようです.
また,日本語のTweetだけ取り出したい人は,文字列に日本語が含まれるかどうかの判定をご参照ください.

package twitter;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.Authenticator;
import java.net.HttpURLConnection;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * StreamAPIから送られてくるStreamデータをひたすら取得し続けるクラス
 * @author tori
 *
 */
public class TwitterStreamReader {


 /**
  * 使い方の例
  * @param args
  * @throws UnsupportedEncodingException 
  */
 public static void main(String[] args) throws UnsupportedEncodingException {
  
  TwitterStreamReader tsr = new TwitterStreamReader("Twitter-UserName", "Password");
  tsr.start();
  
  while(true){
   List<String> tweetList = tsr.readStoredList();
   List<String> errList = tsr.readErrLogList();
   
   for(String text:tweetList){
    System.out.println(text);
   }
   
   for(String err:errList){
    System.err.println(err);
   }
  }
 }
 
 /**
  * sampleのタイムライン取得のためのURL
  */
 private static final String DEFAULT_API_URL = "https://stream.twitter.com/1/statuses/sample.json";

 String userName;
 String password;

 /**
  * 強制終了させられたかどうか
  */
 boolean isForceStop;

 /**
  * 取得しているかどうか
  */
 boolean isRunning;
 
 /**
  * Synchlonizedされたリスト
  */
 List<String> dataList;

 /**
  * Synchlonizedされたリスト
  */
 List<String> errLogList;

 /**
  * apiのURL
  */
 String apiUrl = DEFAULT_API_URL;

 /**
  * ストリームを読み続けるためのスレッド
  */
 private Thread streamReadThread;
 
 public TwitterStreamReader(String userName, String password) {
  super();
  this.userName = userName;
  this.password = password;

  dataList = Collections.synchronizedList(new ArrayList<String>());
  errLogList = Collections.synchronizedList(new ArrayList<String>());
  
 }

 /**
  * 読み込みを開始する
  */
 public void start(){
  StreamReader streamReader = new StreamReader();
  
  streamReadThread = new Thread(streamReader);
  streamReadThread.start();
 }

 /**
  * 取得を停止する
*/
 public void stop() {
  this.isForceStop = true;
 }
 
 /**
  * データ取得中かどうかを返す
  * @return
  */
 public boolean isRunning(){
  return streamReadThread.isAlive();
 }
 
 /**
  * 取得済みのTweetデータのリストを取得する
* これまでに読み込んだデータは削除される
  * @return 是までに読み込んだデータ
  */
 public List<String> readStoredList(){
  List<String> storedList;
  synchronized (dataList) {
   storedList = new ArrayList<String>(dataList);
   dataList.clear();
  }

  return storedList;
 }
 
 /**
  * エラーログのリストを取得する
* これまでに読み込んだデータは削除される
  * @return 是までに読み込んだデータ
  */
 public List<String> readErrLogList(){
  List<String> storedList;
  synchronized (errLogList) {
   storedList = new ArrayList<String>(errLogList);
   errLogList.clear();
  }
  return storedList;
 }

 /**
  * 強制停止信号を送ったかどうか
  * @return the isForceStop
  */
 public boolean isForceStop() {
  return isForceStop;
 }
 
 /**
  * データ読み込み用クラス
  * @author tori
  *
  */
 class StreamReader implements Runnable{
  @Override
  public void run() {
   isForceStop = false;
   while(!isForceStop){
    InputStreamReader isr = null;
    BufferedReader br = null;
    try{
     URL connectUrl = new URL(apiUrl);
     HttpURLConnection con = (HttpURLConnection)connectUrl.openConnection();
     
     con.setRequestMethod("GET");
     con.setDoOutput(true);
     con.setInstanceFollowRedirects(true); 

     Authenticator auth = new Authenticator(){
      public PasswordAuthentication getPasswordAuthentication(){
       return new PasswordAuthentication(userName, password.toCharArray());
      }
     };
     Authenticator.setDefault(auth);
     

     isr = new InputStreamReader(con.getInputStream(), "UTF8");
     br = new BufferedReader(isr);
     while(!isForceStop){
      String tweet = br.readLine();
      synchronized (dataList) {
       dataList.add(tweet);
      }
     }
    }catch(Exception e){
     StringBuffer buf = new StringBuffer();
     buf.append(e.getClass().getName()+"\n");
     for(StackTraceElement ste:e.getStackTrace()){
      buf.append(String.format("\tat %s.%s(%s:%d)\n", ste.getClassName(), ste.getMethodName(), ste.getFileName(), ste.getLineNumber()));
     }
     synchronized (errLogList) {
      errLogList.add(buf.toString());
     }
    }finally{
     try{
      br.close();
     }catch(Exception e){
     }
     try{
      isr.close();
     }catch(Exception e){
     }
    }
   }
  }
 }
}

2011年6月11日土曜日

震災時のTweetデータの分析

先日共同研究者を募集したTweetデータについて,簡単な分析を行ってみました.

まず,総Tweet数ですが,3月5日から3月24日までで3億3235万8199Tweetでした.
日ごとのTweet数の内訳は,図1の通りです.
図1 Tweet数の変位
ただし,震災直後(3月12~15日)あたりは計画停電などの影響で十分にデータが取れていない可能性もあります.後々再収集を試みましたが,すべて収集出来ているとは限りません.

まあ,とはいえ3月11日にTweet数が一気に増加したのは間違いなさそうです.

次に,震災時にどの県が注目されていたかを分析しました.
各県名がTweetされた回数について,日常の1時間あたりの平均を県名Tweet数を1としたときに,震災時に何倍Tweetされたかを日本地図上に色分けして乗せました.
色が濃いほど日常よりも多くつぶやかれていることを示します.
autoボタンを押せば,時系列で見ることが出来ます.
画面がチラ付くのはご愛敬.

やはり,地震直後は「宮城」「福島」などがTweetされていますが,
時間とともに「宮城」などは減少し,「福島」が目立つことが分かります.
二日目付近からは,原発に注目が行っていたのでしょう.
また,長野や静岡でも地震が発生すると,大きく注目されていることが分かります.

ここから分かることはまだまだ少ないですが,今後さらに多くの情報をデータから抽出して,
震災時に何が起きたのかを明らかにしていきたいと思います.

2011年6月8日水曜日

ポモドーロテクニック

研究生活を続けていると,集中して論文を書いたりする作業が必要になったりします.
でも,つい他のことが気になって論文書きながら別の事をやってしまったりとかありますよね.
そんなときに集中力を維持するのに有効なテクニックとして,ポモドーロテクニックがあります.

これは,25分間は集中して一つの作業をして,その後5分間休憩し,
また25分作業する,というのを続けるテクニックです.
作業中の25間はメールが来てもFacebookに書き込みがあっても,全部無視して作業に集中します.
Twitterへの書き込みだって控えます.
電話は出ないとダメかもしれないけど.
雑用は5分ですませて,ついでに少し休みます.
それを4~5回繰り返すと作業効率がぐっとアップするというわけです.

本当かよ~とか思いつつタイマーを用意してやってみたのですが,
これが意外といい.
確かに,集中出来るので思ったより作業が進む.
特に,レポートの採点とか論文の査読とかあんまり楽しくない作業にはぴったり.

というわけで,単にタイマーを使うのではなく,
ちゃんと25分を計ってくれるアプリがいいなあと思って作成しました.
ポモドーロタイマー.
作業回数と内容を入力してスタートするだけで25分間+5分の休憩を淡々と記録していってくれます.

しかも,Twitterと連携して作業開始時にTwitter上に宣言をしてくれます.
今作業中だよ,と.
つまり,作業をサボってTwitterで遊んでいると他の人にばれてしまうわけですね.

というわけで,集中力のないそこのあなた.
是非使ってみてください.

外部関連記事:
ポモドーロテクニックについて

2011年4月7日木曜日

東日本大震災時のTwitterデータを用いた共同研究者募集

東日本大震災が発生してから一ヶ月が経過しようとしています.

そんななか,
Twitter検索の@PENGUINANA_さんのご協力の下,
2011年3月5日から24日までのTwitterでつぶやかれた日本語のTweetのうち2億6688万9069Tweetを入手しました.

含まれているデータには,
・TweetID(1ツイート毎に割り当てられる唯一のID)
・ScreenName(ツイート当時の物)
・Contents(本文)
・Source(ツイート元)
・Time(ツイート時間)
・reply_to(Reply機能を使ったときのリプライ先TweetID)
・reply_to_sc(Replyを受けた人のScreenName)
があります.

2011/4/8追記
各Tweetに含まれるハッシュタグと,はてなキーワードを抽出したメタデータもあります.
また,@User関連のデータも作成予定です.
追記ここまで.

近いうちに公開したいと思いますが,
その前に共同で研究してくださる方を募集します.

ネタとしては,
・情報の信頼性分析
・情報伝播の分析
を考えていますが,これにこだわりません.

一緒に研究しようぜ!という方はメール(ご存じの方)か,Twitter(@toritorix)につぶやくか,このエントリーのコメントでご連絡ください.

あ,ちなみに本件に関して(今のところ)予算はありませんw

2011年2月2日水曜日

MongoDB使ってみる

Twitter検索の中の人が日本語ツイッターユーザのFollow関係DBを作っていたので,利用してみようと思う.
ちなみにデータはこちら.
https://github.com/penguinco/yats-socialgraph-dump
約300万ノード,約2.8億エッジという巨大なソーシャルネットワークを扱うことが出来るようになる.
分析のやりがいがあるけど,まずはデータを扱いやすい形にするところから.
今回のデータはMongoDBのDumpとして渡されるので,MongoDBをインストールして使ってみる.

とりあえず,何はともあれインストール.
http://www.mongodb.org/から
Production Release Window32Bitをダウンロード.
zipで来るので適当なディレクトリで解凍すればOk.

MONGODB\bin\mongod.exe
でサーバを立ち上げる.
その後,ダウンロードしたyats-socialgraph-dumpを
MONGODB\bin\以下に解凍して,
>mongorestore.exe -d twitter dump\twitter
とかやればOk.
もしかしたらあらかじめtwitterというデータベースを作っておかないと駄目かも.

現在インストール中.
どうなる事やら.

2010年12月24日金曜日

TwitterStreamingAPIを使って日本語だけを取り出す

以前書いたTwitterStreamingAPIを使う話は,Twitter4jを使っていなかったので,今度はTwitter4jを使ったバージョンを書いてみる.

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import twitter4j.Status;
import twitter4j.StatusDeletionNotice;
import twitter4j.StatusListener;
import twitter4j.TwitterStream;
import twitter4j.TwitterStreamFactory;


public class StreamReader implements StatusListener{

   /**
    * @param args
    */
   public static void main(String[] args) {
      StreamReader streamReader = new StreamReader(User, Password);
      streamReader.start();

   }
   
   
   /**
    * ユーザ名
    */
   String userName;

   /**
    * パスワード
    */
   String password;

   /**
    * stream
    */
   private TwitterStream streamTwitter;
   
   
   
   
   public StreamReader(String userName, String password) {
      super();
      this.userName = userName;
      this.password = password;
   }

   public void start() {
      TwitterStreamFactory factory = new TwitterStreamFactory();
      streamTwitter = factory.getInstance(userName, password);
      streamTwitter.addListener(this);
      streamTwitter.sample();
   }

   @Override
   public void onStatus(Status status) {
      if(isJapanese(status.getText())){
         System.out.printf("%d\t%s\t%s\n", status.getId(), status.getUser().getScreenName(), status.getText());
         //得られたStatusに対する処理をする
      }
      
   }

   /**
    * 日本語かどうかを返す
    * @param text
    * @return 日本語ならtrue
    */
   boolean isJapanese(String text){
      Matcher m = Pattern.compile("([\\p{InHiragana}\\p{InKatakana}])").matcher(text);
      return m.find();
   }
   

   @Override
   public void onDeletionNotice(StatusDeletionNotice arg0) {
      
   }



   @Override
   public void onException(Exception arg0) {
      
   }

   @Override
   public void onTrackLimitationNotice(int arg0) {
      
   }

}
多分,これで動くはず.
おまけで日本語だけを取得するようにしておいた.

2010年9月3日金曜日

bit.lyの短縮URLをワンクリックで作る

TwitterにURLを張るときは,http://bit.ly/などの短縮URLを使うのが普通らしいけど,
いちいちbitlyへ移動して作るのが面倒くさいので,ブックマークレットを作ってみた.
javascript:(function(){
var%20s=location.href;s.charset="UTF-8";
var%20b="http://bit.ly/?u="+encodeURIComponent(s)+"&searchButton=Shorten";
window.open(b,"bitly");})();
使ってみたい人は,以下のURLをブックマークバーにドラッグしてください.
[短縮URL]
たぶん,もっと便利なものを誰かが作っているんだろうなあと,思いつつ.

2010年4月28日水曜日

Twitter ストリーミングAPIをgardenhoseで使う

とりあえず,TwitterストリーミングAPIでデータをもっと欲しいので,
gardenhoseに申し込んでみた.
http://twitter.com/help/request_streaming
にアクセスして,名前,所属,E-Mail,URL,申請理由を書くだけ.

登録したら速攻使えるようになりました.
というか,sampleの性能があがって,gardenhoseになりました.
申請理由とか真面目に書いたのに,あんまり意味がない.

で,どのくらいとれるようになったかを確認.
sampleではIDの下一桁が0-4のTweetがとれていたけど,gardenhoseだと下2桁が0-14のTweetが取得出来ていた.およそ3倍の性能と言ったところ.
それでも,全体の15%しかとれない.これが多いか少ないかは難しいところだ.

sampleでは日本語のTweetが秒間6.5個くらいとれたのに対して,
gardenhoseでは日本語のTweetが秒間20個くらいとれた.
ちなみに,計測時間は19:30くらい.
昼間は秒間10Tweetくらいだった.だいぶ差があるなあ.

2010年4月27日火曜日

日本語が含まれるかどうかの判定

TwitterのストリーミングAPIから日本語が使われているTweetだけ取得したいので,
日本語マッチングをやってみた.
Matcher m = Pattern.compile("([\\p{InHiragana}\\p{InKatakana}])").matcher(text);
boolean isJapanese = m.find();
\p{InCJKUnifiedIdeographs}で漢字もとれるけど,漢字オンリーのつぶやきは中国系の人の物が多いみたいなので,ひらがなかカタカナが含まれるもののみにした.

2010年4月26日月曜日

Twitter Streaming APIをJavaで試してみる


2011年10月05日追記:
Streaming APIがHTTPS対応となったので,若干変更が必要です.
詳しくは,こちらのバージョンをご覧ください.

2010年12月24日追記:
Twitter4jを使ったバージョンを作ったので,そちらを使った方が良いと思われます.


TwitterのStreamingAPIにがぜん興味が出てきたので,Javaで実装してみた.
package twitter;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.Authenticator;
import java.net.HttpURLConnection;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * StreamAPIから送られてくるStreamデータをひたすら取得し続けるクラス
 * @author tori
 *
 */
public class TwitterStreamReader {


 /**
  * 使い方の例
  * @param args
  * @throws UnsupportedEncodingException 
  */
 public static void main(String[] args) throws UnsupportedEncodingException {
  
  TwitterStreamReader tsr = new TwitterStreamReader("Twitter-UserName", "Password");
  tsr.start();
  
  while(true){
   List<String> tweetList = tsr.readStoredList();
   List<String> errList = tsr.readErrLogList();
   
   for(String text:tweetList){
    System.out.println(text);
   }
   
   for(String err:errList){
    System.err.println(err);
   }
  }
 }
 
 /**
  * sampleのタイムライン取得のためのURL
  */
 private static final String DEFAULT_API_URL = "http://stream.twitter.com/1/statuses/sample.json";

 String userName;
 String password;

 /**
  * 強制終了させられたかどうか
  */
 boolean isForceStop;

 /**
  * 取得しているかどうか
  */
 boolean isRunning;
 
 /**
  * Synchlonizedされたリスト
  */
 List<String> dataList;

 /**
  * Synchlonizedされたリスト
  */
 List<String> errLogList;

 /**
  * apiのURL
  */
 String apiUrl = DEFAULT_API_URL;

 /**
  * ストリームを読み続けるためのスレッド
  */
 private Thread streamReadThread;
 
 public TwitterStreamReader(String userName, String password) {
  super();
  this.userName = userName;
  this.password = password;

  dataList = Collections.synchronizedList(new ArrayList<String>());
  errLogList = Collections.synchronizedList(new ArrayList<String>());
  
 }

 /**
  * 読み込みを開始する
  */
 public void start(){
  StreamReader streamReader = new StreamReader();
  
  streamReadThread = new Thread(streamReader);
  streamReadThread.start();
 }

 /**
  * 取得を停止する<br>
  */
 public void stop() {
  this.isForceStop = true;
 }
 
 /**
  * データ取得中かどうかを返す
  * @return
  */
 public boolean isRunning(){
  return streamReadThread.isAlive();
 }
 
 /**
  * 取得済みのTweetデータのリストを取得する<br>
  * これまでに読み込んだデータは削除される
  * @return 是までに読み込んだデータ
  */
 public List<String> readStoredList(){
  List<String> storedList;
  synchronized (dataList) {
   storedList = new ArrayList<String>(dataList);
   dataList.clear();
  }

  return storedList;
 }
 
 /**
  * エラーログのリストを取得する<br>
  * これまでに読み込んだデータは削除される
  * @return 是までに読み込んだデータ
  */
 public List<String> readErrLogList(){
  List<String> storedList;
  synchronized (errLogList) {
   storedList = new ArrayList<String>(errLogList);
   errLogList.clear();
  }
  return storedList;
 }

 /**
  * 強制停止信号を送ったかどうか
  * @return the isForceStop
  */
 public boolean isForceStop() {
  return isForceStop;
 }
 
 /**
  * データ読み込み用クラス
  * @author tori
  *
  */
 class StreamReader implements Runnable{
  @Override
  public void run() {
   isForceStop = false;
   while(!isForceStop){
    InputStreamReader isr = null;
    BufferedReader br = null;
    try{
     URL connectUrl = new URL(apiUrl);
     HttpURLConnection con = (HttpURLConnection)connectUrl.openConnection();
     
     con.setRequestMethod("GET");
     con.setDoOutput(true);
     con.setInstanceFollowRedirects(true); 

     Authenticator auth = new Authenticator(){
      public PasswordAuthentication getPasswordAuthentication(){
       return new PasswordAuthentication(userName, password.toCharArray());
      }
     };
     Authenticator.setDefault(auth);
     

     isr = new InputStreamReader(con.getInputStream(), "UTF8");
     br = new BufferedReader(isr);
     while(!isForceStop){
      String tweet = br.readLine();
      synchronized (dataList) {
       dataList.add(tweet);
      }
     }
    }catch(Exception e){
     StringBuffer buf = new StringBuffer();
     buf.append(e.getClass().getName()+"\n");
     for(StackTraceElement ste:e.getStackTrace()){
      buf.append(String.format("\tat %s.%s(%s:%d)\n", ste.getClassName(), ste.getMethodName(), ste.getFileName(), ste.getLineNumber()));
     }
     synchronized (errLogList) {
      errLogList.add(buf.toString());
     }
    }finally{
     try{
      br.close();
     }catch(Exception e){
     }
     try{
      isr.close();
     }catch(Exception e){
     }
    }
   }
  }
 }
}


参考にしたのは,
この辺.

どんどん取ってくるためにThread化したけど,使いたい人は好きに作り直せばいいと思う.