tweepyでStreaming API

id:nekomusha6:20110121:1295606221 でライブラリがtweepyに決まったので、次はアプリケーションの作成になります。

お題

tweepyを使ってフォローしている人のつぶやきをStreaming APIを使って拾う

はじめに

twitterAPIには、公式*1 *2によると現在以下の3種類、分け方によっては5種類があります。

  1. REST API
  2. REST API(Search API)
  3. Streaming API
    1. Streaming API
    2. User Stream
    3. Site Stream

(これらのWeb ServiceのPythonラッパーである)tweepyで対応しているのは、(1)〜(3-1)までですが、今回使用するのは(1)と(3-1)だけです。

それでは検討

Streaming APIのサンプルを見ると、どうやらユーザー名とパスワードが分かれば(Basic認証で)なんかできそうな気がします。で、やってみると...

動きますが、filterのユーザー指定(folow)ができません。いや正確には例えば私のユーザーを指定するのだったら@の後ろに書くnekomushaでいいのかと思っていたのですが、ユーザーID(230193917)が必要でした。それでは

  1. どうすればユーザー名からユーザーIDを得られるのか?
  2. それにどうすればフォローしている人の一覧を得られるのか?

しかし(3-1)Streaming APIにはどちらの機能も用意されていません。これらは(1)REST APIの1つである以下のAPIでまとめて用意されています。

"GET friends/ids"
http://dev.twitter.com/doc/get/friends/ids

これはREST APIかつ認証不要なAPIなので、例えばブラウザから

http://api.twitter.com/1/friends/ids.json?screen_name=nekomusha

とするだけで私のフォローしている人のID一覧がJSON形式で取れてしまいます。

もちろんtweepyにもこのAPIを使う関数が用意されています。

API.friends_ids(id/screen_name/user_id[, cursor])(http://joshthecoder.github.com/tweepy/docs/api.html#API.friends_ids)

これだけ情報が揃えば十分作れそうです。

実装

ほぼサンプルのままですが...

#!/usr/bin/env python

from textwrap import TextWrapper
import tweepy

username = "ユーザー名"
password = "パスワード"

class StreamWatcherListener(tweepy.StreamListener):

    status_wrapper = TextWrapper(width=60, initial_indent=' ', subsequent_indent=' ')

    def on_status(self, status):
        try:
            print self.status_wrapper.fill(status.text)
            print '\n %s %s via %s\n' % (status.author.screen_name, status.created_at, status.source)
        except:
            # Catch any unicode errors while printing to console
            # and just ignore them to avoid breaking application.
            pass

    def on_error(self, status_code):
        print 'An error has occured! Status code = %s' % status_code
        return True # keep stream alive

    def on_timeout(self):
        print 'Snoozing Zzzzzz'

follow_list = tweepy.api.friends_ids(screen_name=username)
if len(follow_list) > 5000:
    print "too many firends."
else:
    stream = tweepy.Stream(username, password, StreamWatcherListener(200), timeout=None)
    stream.filter(follow_list)

なお、フォローしている人が5000人を超えたらエラーにしているのは、

"Streaming API: Methods"
http://dev.twitter.com/pages/streaming_api_methods#statuses-filter

により、デフォルトだとその辺が上限だからです。これ以上増やしたい場合は、User Streamの出番なのでしょう。試していたらfollowしたりされたりなどのイベントまで拾ってくれるようです(いい忘れましたがStreaming APIにするだけでもいっぺんに拾える情報が増えます)。

おまけ

今回は認証方法はBasic認証になっていますが、HTTPなのでパスワードが平文で送られることになります。パスワードは昨今いろいろなところで同じのが使われていたりしがちなので、(OAuthに比べて)漏れたときのダメージが格段に大きく、あまりよくありません。せっかくStreaming APIに対応しても、OAuthでほぼ同じことができるREST APIの方が安全で良いという結論になってしまいます(実はStreaming APIBasic認証/OAuth両対応してます。tweepyがBasic認証しか対応してないだけです)。そこで、まずは暗号化通信できないか調べてみました。結果...

暗号化通信はできませんでした。twitterSSL(HTTPS)に対応していますが、tweepyのStreaming APIではできないからです。しかしtweepyを1行いじればHTTPSで通信できるようになります(HTTPでは通信できなくなりますが)。

/usr/share/pyshared/tweepy/streaming.py: 196

                conn = httplib.HTTPConnection(self.host)

これを

/usr/share/pyshared/tweepy/streaming.py: 196

                conn = httplib.HTTPSConnection(self.host)

こうするだけ!

ubuntu & pythonでtwitter

gwibberによる収集もいいのですが、BOTによる自動収集で規模を増やしたくなりました。そこで今更ですがpythonからtwitter APIを使うことにします。

本日のお題

ライブラリ(twitter APIpythonラッパーモジュール)何にしましょう?

結論

PPAからpython-tweepy引っ張ってくることにしました。

経緯

まず疑問点が2つありました。

  1. UbuntuってPythonで動いている部分が結構あるんだけどapt使わずにsudo easy_installとかしていいの?
  2. python用のtwitterモジュールっていくつもあるけどどれがいいの?

以下ではこれらの疑問に対する答えを探っています。

1. Ubuntuパッケージとの競合

Ubuntuでは結構Pythonを使っているので心配です。モジュールの追加に関して調べてみたところ

"Why you should not use easy_install carelessly on Debian"
http://workaround.org/easy-install-debian

という2009年12月のブログを見つけました。英語なので概略だけ書いておくと、

  • easy_install(setuptool)は悪くないけどアンインストールもできないし汚い
  • easy_install(setuptool)とaptで二重にインストールが行われるかもしれない(どちらが使われるかは設定次第)
  • debianのaptだとバージョンが古くてもセキュリティパッチが提供され続ける
  • 新しいものが欲しければdebian unstableを使えばいい
  • 日常使用にはオススメしないがvirtualenvで環境を切り替えられるよ

とのこと。競合までするのか分かりませんが、互いに互いを意識してなさそうなので重ならないようにしないといけない気がします。

なおvirtualenv自体の使い方などについては、同じ時期にこちらにも

"2009年版Python開発環境を整えよう"
http://labs.unoh.net/2009/12/2009python.html

書かれていました。これらの情報、玄人的には当たり前なのかもしれませんが、素人的には助かります。

以上の情報から、Ubuntuはunstable相当でしょうし、個人的には

基本はaptで、公式リポジトリになかったり欲しいのより古ければPPAで、それでもなければvirtualenvかeasy_installという感じ

でいいのかな、と考えています。

2. pythontwitterモジュールの選択

twitter公式にあるだけでも現在

モジュール 作者 説明
oauth-python-twitter2 Konpaku Kogasa Combines python-twitter and oauth-python-twitter to create an evolved OAuth Pokemon.
python-twitter DeWitt Clinton This library provides a pure Python interface for the Twitter API.
python-twyt Andrew Price BSD licensed Twitter API interface library and command line client.
twitty-twister Dustin Sallings A Twisted interface to Twitter.
twython Ryan McGrath REST and Search library inspired by python-twitter.
Tweepy Josh Roesslein Supports OAuth, Search API, Streaming API.

これだけあるようです。ググったりするとtweepyがいいようですが、python-twitterも名前がそのものすぎて気になります。python-twitterは10.10付属のバージョンだとOAuthが使えずにダメなのですが、PPAで新しいのを持ってくるとOAuthは使えるようになります。対してtweepyはUbuntu公式リポジトリにはありません。これは迷います。

さて、twitterへのアクセスは

"Rate Limiting"
http://dev.twitter.com/pages/rate-limiting#rest

にあるように、認証つきのものはユーザーごとに350リクエスト/h、認証なしのものはIPごとに150リクエスト/hとなっており、制限を超えてしまうとエラーになってしまいます。例えば頻繁に更新したくて、制限ギリギリまでリクエストを出すように設計すると、同じユーザーで別のアプリからいじったときに350/h制限に引っかかったりしてしまい、面倒だったりします。

しかし、Streaming APIを使うとこの制限を受けません。tweepyはこの紹介文中唯一Streaming API対応をうたっています*1。というわけで今回は

tweepyが良いかな

という感じです。

3. 総合的に

tweepyはUbuntu公式リポジトリには用意されていませんでしたが、PPAにはあるようです。

"python-tweepy"
https://launchpad.net/~chris-lea/+archive/python-tweepy

というわけでtweepyに決定です。11.04では公式リポジトリに入ってほしいな。

おまけ

gwibberはどうしているかというと...OAuthとJSONパーサはモジュール使うものの、後は自力なようです。

*1:ただAPIドキュメントは更新されてないのかStreamなんてどこにも書いていません(githubにはA python library for the Twitter API. OAuth, complete coverage, streaming APIと書いてあって、サンプルもついてましたが

SQLAlchemy+MySQL Unicode文字列の怪

Python2.Xで日本語を扱うときUnicode文字列への/からの変換時に面倒な問題があるらしく*1、コード内ではどちらかに統一したいと思うらしいです(そう思います)。しかしSQLAlchemyにはPlane/Unicodeどちらで渡す/から返される文字列はどちらなのか仕組みがよく分かりません。そこで分かる範囲内で少し(現象的に)調べてみました。

結果

以下のように推測します。

create_engine()のconvert_unicode引数 True False
渡す文字列 Unicode型? DB-APIが受け取れるもの?
返る文字列(String経由) Unicode DB-APIが返したもの
返る文字列(executeなどDB-API直) DB-APIが返したもの DB-APIが返したもの

特に問題になるのはデータベースのドライバが返したものが何になるのかですが、MySQLdbとSQLiteについては以下のように推測します。

DB-API 条件 返る型
SQLite なし unicode
MySQLdb collate *2 がbinary(utf8_binなど) str
MySQLdb collateがbinary以外でuse_unicode=1 *3 unicode
MySQLdb collateがbinary以外でuse_unicode=0 str

collate値によってunicodeにならないのは不自然と思います。調べてみたら案の定

http://sourceforge.net/tracker/index.php?func=detail&aid=2837134&group_id=22307&atid=374932

バグ登録されてました。1年以上前に....(でもopen

つまりDB-APIのMySQLdbのバグでSQLAlchemyは無罪ということのようです。

根拠

以下の2サイトの情報を元に

http://technolize.blogspot.com/2010/11/sqlalchemy-collate-utf8bin.html
http://groups.google.com/group/sqlalchemy/browse_thread/thread/f4ba7ca61206fa7e/cb47da86cb7856f2

以下のように現象ベースで試しみました。

MySQLについては以下の2パターンで
(1)create database practice CHARACTER SET 'utf8';
(2)create database practice CHARACTER SET 'utf8' COLLATE 'utf8_bin';
以下のコードを実行して

#! -*- coding: utf-8 -*-
from itertools import izip
from sqlalchemy import Column, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()
class Sample(Base): # Sampleクラスとsamplesテーブルとそのマッピングの定義
    __tablename__ = 'samples' # テーブル名
    __table_args__ = {'mysql_engine':'InnoDB'} # MySQLではInnoDB

    id = Column(String(10), primary_key=True) # IDカラムの定義
    val = Column(String(10))                  # VALカラムの定義

    def __init__(self, id, val):
        self.id = id
        self.val = val

# SQLiteとMySQLのデータベースの定義
engine_parameters = (
    "sqlite:////home/user1/practice.sqlite",
    "mysql+mysqldb://root@localhost/practice?charset=utf8&use_unicode=0",
    "mysql+mysqldb://root@localhost/practice?charset=utf8&use_unicode=1",
)
# エンジンの作成
engines = []
names = []
for param in engine_parameters:
    for conv in (True, False):
        engines.append(create_engine(param, echo=False, convert_unicode=conv))
        scheme = param[0:5] + param[-1] 
        names.append("%s-%s" % (scheme, conv))

# セッションクラスの作成とセッションオブジェクトの作成(SQLiteとMySQL両方)
Sessions = []
sessions = []
for engine in engines:
    # create table
    Base.metadata.create_all(engine)
    # セッション生成
    Sessions.append(sessionmaker(bind=engine))
    sessions.append(Sessions[-1]())

# SQLの実行
for name, session in izip(names, sessions):
    # insert into samples values(xxx, 'val1')
    session.add(Sample(u'文字列ID', u"文字列VAL"))

    selector = session.query(Sample)
    value = selector.filter(Sample.id == u"文字列ID").one().val
    print name, "query", type(value), value
    (value,), = session.execute("select val from samples where id=:id", {"id": u"文字列ID"})
    print name, "execute", type(value), value

    session.rollback()
    session.close()

以下のようになりました。
(1)collate指定なし

sqlite-True query <type 'unicode'> 文字列VAL
sqlite-True execute <type 'unicode'> 文字列VAL
sqlite-False query <type 'unicode'> 文字列VAL
sqlite-False execute <type 'unicode'> 文字列VAL
mysql0-True query <type 'unicode'> 文字列VAL
mysql0-True execute <type 'str'> 文字列VAL
mysql0-False query <type 'str'> 文字列VAL
mysql0-False execute <type 'str'> 文字列VAL
mysql1-True query <type 'unicode'> 文字列VAL
mysql1-True execute <type 'unicode'> 文字列VAL
mysql1-False query <type 'unicode'> 文字列VAL
mysql1-False execute <type 'unicode'> 文字列VAL

(2)collate指定がbinary

sqlite-True query <type 'unicode'> 文字列VAL
sqlite-True execute <type 'unicode'> 文字列VAL
sqlite-False query <type 'unicode'> 文字列VAL
sqlite-False execute <type 'unicode'> 文字列VAL
mysql0-True query <type 'unicode'> 文字列VAL
mysql0-True execute <type 'str'> 文字列VAL
mysql0-False query <type 'str'> 文字列VAL
mysql0-False execute <type 'str'> 文字列VAL
mysql1-True query <type 'str'> 文字列VAL
mysql1-True execute <type 'str'> 文字列VAL
mysql1-False query <type 'str'> 文字列VAL
mysql1-False execute <type 'str'> 文字列VAL

つまり

ということになり、辻褄が合うように一般化すると最初の推測になる、というわけです。

おまけ

で、自分はどうしたかというと、MySQLSQLiteで同じコードにしたく、また面倒なSQLとcollate指定binaryが必要だったため、完全にどちらかに倒すことができず、結局isinstance()使いまくりました...
(迷ったのはmysqlutf-8SQLiteunicode、と動的型キャスト方式の二択)

*1:id:kaito834:20090921:1253539430

*2:データベースのカラム定義

*3:URIの最後のクエリ文字列

SQLAlchemy+MySQLでUpdate文に失敗

ハマった問題の原因調査結果です。

現象

sqlalchemy.orm.exc.ConcurrentModificationError: Updated rowcount 0 does not match number of objects updated 1

原因不明なこんなメッセージに悩まされてました。

再現条件

  • SQLAlchemy
  • MySQL
  • アダプタがMySQLdb

再現スクリプト

動作条件

Ubuntu 10.10で必要なパッケージは

以下のデータベースを作成済みであること

CREATE DATABASE practice DEFAULT CHARACTER SET utf8 COLLATE utf8_bin
スクリプト

コメントにしてますが、比較用にSQLiteでの動作なども確認できます。

#! -*- coding: utf-8 -*-
from sqlalchemy import Column, String, Numeric, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()
class Sample(Base): # Sampleクラスとsamplesテーブルとそのマッピングの定義
    __tablename__ = 'samples' # テーブル名
    __table_args__ = {'mysql_engine':'InnoDB'} # MySQLではInnoDB

    id = Column(Numeric(18,0), primary_key=True) # IDカラムの定義
    val = Column(String(10))                  # VALカラムの定義

    def __init__(self, id, val):
        self.id = id
        self.val = val

# SQLiteとMySQLのデータベースの定義
# MySQLは事前にCREATE DATABASE practice DEFAULT CHARACTER SET utf8 COLLATE utf8_bin(大文字小文字区別あり)
engines = (
#    create_engine("sqlite:////home/user1/practice.sqlite", echo=True),
    create_engine("mysql+mysqldb://root@localhost/practice?charset=utf8&use_unicode=0", echo=True),
)

# セッションクラスの作成とセッションオブジェクトの作成(SQLiteとMySQL両方)
Sessions = []
sessions = []
for engine in engines:
    # create table
    Base.metadata.create_all(engine)
    # セッション生成
    Sessions.append(sessionmaker(bind=engine))
    sessions.append(Sessions[-1]())

# SQLの実行
for session in sessions:
    # insert into samples values(xxx, 'val1')
    session.add(Sample(19778376323571713, "val2"))
    # commit
    session.commit()
    
    # select * from samples
    for sample in session.query(Sample):
        print sample.id, sample.val
        # update samples set val='削除します' where id=?
        sample.val = u'削除します'
        # commit
        session.commit()
        # delete from samples where id=?
        session.delete(sample)
        # commit
        session.commit()
    
    session.close()

原因

update時に

UPDATE samples SET val='削除します' WHERE samples.id = '19778376323571713'

なクエリが出ていたこと。MySQLのマニュアル*1によると、この文字列は一旦53bitの浮動小数点数に丸められるため、この桁数は表現できず、WHERE句の条件では一致しない。

正しくは

UPDATE samples SET val='削除します' WHERE samples.id = 19778376323571713

でないといけない。

回避策

この例ではNumericの代わりにBigIntegerを使用する


とても助かったページ↓(マニュアルの翻訳)
http://omake.accense.com/static/doc-ja/sqlalchemy/index.html

Python開発環境 on Ubuntu 10.10

普通はemacsvimか、もしくはNetBeansと言ったところ(WindowsだとPyScripterとかも。wineでどうかとかは知りません。)なんでしょうが、eclipseでのPython開発環境構築方法をメモっておきます。先に断っておくと環境にもpythonにも詳しくなくまだ勉強中の身の上です。環境はUbuntu 10.10 64bit版です。
(ちょっと長めですが、細かく書いただけで別に大変な作業ではありません。)

JREのインストール

eclipseはほとんどJavaで書かれており、その動作にJREが必要になるので、まずは入れちゃいます。

$ sudo apt-get install default-jre

Javaの開発もしたい人は

$ sudo apt-get install default-jdk

としてJDKも入れた方がいいかもしれません。

eclipse本体のインストール

Ubuntuにはeclipseパッケージも用意されていますが、最新ではないのでここでは本家から取ってきます(好み)。

http://www.eclipse.org/downloads/

ただし以下のように目的別にいろいろなエディションが用意されています(Pythonはないですが)。

Pythonは後からプラグインで入れるのでどれでもいいのですが、ここではせっかくLinuxなのでEclipse IDE for C/C++ Linux Developersにしておきます(趣味)。

ダウンロードが終わったら、書庫マネージャでホームディレクトリに展開します。展開し終わると以下のようにホームディレクトリにeclipseフォルダができ、この中にeclipseがインストールされています。

↓ホームを開いた画面

このフォルダにあるeclipseをダブルクリックすればeclipseが起動するのですが、このままでは英語版のC/C++開発環境になります。

↓英語版の最初の画面

eclipse日本語化プラグインPleiadesのインストール

次はメニューなどを日本語にします。以下のサイト

http://mergedoc.sourceforge.jp/

に行って赤丸部分をクリックしpleiadesプラグインだけをダウンロードしにいきます(all-in-oneはWindows用なので)。
いつでも

subversionリポジトリなので分かりにくいデスが、赤丸部分からダウンロード出来ます。ダウンロードが終わったら書庫マネージャからeclipseフォルダ(~/eclipse)に展開します。

展開が終わったらeclipse.ini(~/eclipse/eclipse.ini)を編集するので開きます。

開いたら以下の(ユーザー名)をご自分のユーザー名に置き換えて最終行に追加します。

-javaagent:/home/(ユーザー名)/eclipse/plugins/jp.sourceforge.mergedoc.pleiades/pleiades.jar

ここまでできれば日本語用の設定はOKです。
eclipse(~/eclipse/eclipse)を実行して日本語の画面が表示させてください。

この画面が日本語の最初の画面です。eclipseのプロジェクトの置き場所であるワークスペースをどこにするか尋ねています。とりあえずデフォルト(~/workspace)でいいのでOKします。OKすると、開発用の画面が立ち上がります。

このようこそ画面は得るものがないのでさっさと閉じます(ヘルプ>ようこそで見れます)

これでC/C++の日本語開発環境が整いました。次はようやくPythonです。

Python開発用プラグインPyDevのインストール

このプラグインeclipse上のUIからインストールすることができます。

まずはeclipseのメニューからヘルプ>新規ソフトウェアのインストール...を選択すると、

このような画面が開きます。これは何のプラグインをインストールか尋ねている画面ですが、まずはPyDevプラグインを置いているサイト(リポジトリ)を追加するために追加ボタンを押します。

名前は自動判定してくれるので、ロケーションに以下のURL(PyDevプラグインを置いているサイト)を入力してOKします。

http://pydev.org/updates

OKすると元の画面に戻るのですが、先の追加により真ん中のリストボックスにチェックできる項目が二つほど出現します。これがPyDevのプラグインとそのオプションプラグインです。なお出現には数十秒程度時間がかかることがあるので、すぐに出ない場合は少し待ってください。

チェックできるようになったら、両方チェックして次へを押します。

進んだ画面は依存関係などで入るプラグインも含めた実際にインストールされるプラグインの確認です。次へで構いません。

進んだ先の画面は各プラグインのライセンスの使用許諾画面です。ライセンスに問題がなければ、「使用条件の条項に同意します」を選択して完了してください。ちなみにEclipse Common Licenseはオープンソースライセンスの一種で、wikipedia(日本語)の説明がこちらです。

するとダウンロードが始まり、画面をしばらく見てると...

この警告が表示されます。ようは作った人が誰だかを示す電子署名をわざわざ入れてないものを発見したということです。オープンソースではよくあることと割り切れればOKを押してください。ソースレベルで誰がコミットしたものか分からなくなるようなことは滅多にないと思うので、バイナリレベルで電子署名がない=不正なものであることは稀だと思いますが。

そして次は署名はあるけどその署名の証明書が正式な証明機関が発行(?)したものでない、これ信用しますか?という質問です。正式な証明機関は普通無料で証明書を発行しないので、特にオープンソースではそういうケースが多いです。許容できれば全て選択を押してOKしてください。あとは待っているとインストールは終わります。

再始動するか聞かれるので、再始動するを選択してください。

しばらくするとスプラッシュ画像が表示されたのち、またワークスペースを聞かれるのでOKしてください。また待っていると、起動終了します。これでC/C++/Pythonの開発環境として動作可能な状態になります。

もう動作はするのですが、後少しPythonの実行環境を設定しておきます。メニューからウィンドウ>設定を選択してください。

この画面で、左のPydevからインタープリタ-Pythonを選択し、Auto Configを押してください。

するとパスをトラバースして現在の環境から適切な設定をしてくれます。とりあえずOKです。

後はもうOKするだけです。

今後UbuntuのaptからPythonのパッケージを入れた場合も、さっきの画面で一旦除去して再度AutoConfigすることでまた適切な設定になります。

以上でPythonで開発する準備が整いました。

Hello, World!

最後にHello, World!だけしておきます。

メニューからファイル>新規>プロジェクト...を選択して、プロジェクトを作ります。

するとプロジェクトの種類を聞かれるので、PyDevプロジェクトを選択して次へ進みます。

次はプロジェクトの設定を聞かれるので、プロジェクト名をhello、バージョンを2.6(好み)に設定して、完了を押します。

これでPyDev(Python)プロジェクトが作られるのですが、画面がC/C++用の開発画面のままなので、Python用の開発画面に変更するか聞かれます。はい、を選択してください。

この画面(パースペクティブ)がPython開発用の画面です。次はhello.pyファイルを作るため、左のhelloプロジェクトを開いてsrcファルダを右クリックして、新規>Pydevモジュールを選択します。

するとどんなファイルを作るか聞かれるので、パッケージは空欄のまま、名前がhello、テンプレートがモジュール: メインを設定して完了を押します。

すると、選択されたテンプレートを元にhello.pyが作られ、その編集画面が開きます。

後はpassをprint "Hello, World!"に置き換えて保存すれば(Ctrl+Sなど)プログラムは完成します。

そして左からhello.pyを選択した状態から右クリックメニューで実行>Python実行を選択すると...

こんなんでてきます。

最後に

とりあえずこれだけできれば後は慣れるだけなので、説明はしません(知らないので出来ないとも言う)。デバッグやら何やら、可能そうな大抵のことはできると思いますので、慣れてから自力で調べてください。便利なツールがあると生産性が上がる場合があるので、これも自力で見つけてみてください。自分はEGitとsubclipseなどのプラグインも入れています。

その人を表す特徴的な単語

gwibberにあるデータからなんか統計処理できないかと考えてみた。

お題

Twitterで(勝手に)フォローしてる人固有の特徴的な単語上位3つを挙げる

結果

頻度の高い方順に5人抽出しました。

ID 1 2 3
mitukiii 金沢 screen mitukiii
repeatedly gakisp Andrei 学歴
SubaruG const decltype
ponkotuy eneloop mAh ポンコツ
littlestone71 hakone

なんとなくそういうイメージな人からどうしてそうなったのかよく分からない人までいますが、おおよそ違和感のない感じになっています。

方法は

  1. gwibber収集データからつぶやきを取得*1
  2. MeCabで名詞を抽出*2
  3. 人ごとに単語のTF-IDF積*3を計算して並べる

といった感じ。

以前はこの後にきちゃなくて遅いソースを載せていたのですが、gwibberからのデータが大量でも処理できるよう、データベースに格納するようにしたら、すっかりスパゲッティになってしまいました。とりあえずgithubに移動してあります。

https://github.com/nekomusha/public/tree/hatena20110117/twitter_statistics/src

実行にはPython用O/R mapperであるSQLAlchemyが必要です。

*1:「gwibberでtwitterid:nekomusha6:20110112:1294826232

*2:ubuntu & python形態素解析id:nekomusha6:20110112:1294782745

*3:その人がその単語をつぶやいた回数(TF)と全人数をその単語をつぶやいたことのある人数で割ったもの(IDF)の積