Tweepyを触ってみる ~pythonでTwitter APIをいじる~

何となくPythonTwitterを触ってみたいなぁと思って調べてみました。
PythonからTwitterAPIをいじるモジュールとして,

というのが一般的な様です.
Tweepyが簡単そうだったので, とりあえず使ってみましたので簡単にご紹介します.

とりあえず,

"中野"を含むツイートを何件かとってくる

みたいな簡単なものを作ってみましょう.
順を追って説明していきます.
GitHubソースコードはおいてあります.

tweepyのインストール

簡単です. pip使っていれればOKです.

pip install tweepy

これでもうtweepy使えます.

Consumer key等の取得

TwitterAPIをいじるためには登録が必要です.
といっても, Twitterアカウント持ってればすぐに出来ます.
例えばHELLO APIを参考にして,

  • Consumer key
  • Consumer secret
  • Access token
  • Access token secret

を取得します.
この後アプリケーションで使うので, 保存しましょう.
私はkeys.pyというファイルにディクショナリ形式で持っておきました.

KEYS = {
        'consumer_key':'********',
        'consumer_secret':'*********',
        'access_token':'*********',
        'access_secret':'*********',
        }

OAuth認証

tweepyはOAuth認証のインタフェースも整備されているので,
簡単に認証が出来ます.
以下の様にConsumer key等を指定してOAuthHandlerのインスタンスを生成します.

def authorize(consumer_key, consumer_secret, access_token, access_secret):
    """ Authorize using OAuth.
    """
    auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
    auth.set_access_token(access_token, access_secret)
    return auth

REST APIを使ってみる

REST APIを使用する場合は, tweepyのAPIクラスを使用します. TwitterREST APIについてはこちら.
ここでは, 100件の"中野"を含むツイートをとってきて,

をコンソールに表示してみます.

def search_rest(api, keyword, search_count=100):
    search_result = api.search(keyword, count=search_count)
    for i, twt in enumerate(search_result):
        print '---%3d---' % (i + 1)
        print twt.created_at
        print twt.user.name
        print twt.text + '\n'

--- 1---
2014-01-13 04:03:45
AAA
I'm at @Bar_Zingaro (バー・ジンガロ) (中野区, 東京都) http://t.co/Pc1w83hRE8
・・・
---100---
2014-01-13 03:55:59

RT @chicago0812: 成人式となると中野か杉並の成人式に呼ばれた談志家元が開口一番「よーし!今日はテメェらに覚醒剤の打ち方から教えてやる!」とやって翌日の新聞で大問題になったことを思い出す…。

と, こんな感じで表示されます. (過激なのが出てきましたね...)
REST APIの検索だと, 最大数が100と決まっているので, search関数の引数count
大きい数字を与えても, 100より大きくはならないので注意が必要です.
また, search関数の引数によって指定した日付までに投稿されたツイートを
とってくるという様なことも出来ます.

後々, この辺についてもまとめたいと思います.

Streaming APIを使ってみる

TwitterではStreaming APIが用意されています.
ドキュメントはこちら.
Streaming APIを使えば流れているTweetをリアルタイムで取得できます.
Streaming APIでは,

  • User Streams
  • Public Streams
  • Site Streams

という3種類のStreamを取得できます. 今回は"中野"を含むツイートを検索するということなので,
Public Streamsを扱うことになります.
更にいえば, 絞り込むを行うためのfilterを使用することになります.

Tweepyでは, それに対応したfilter関数を使用します.
引数で色々指定できますが, "中野"を含むツイートを探すだけなので,
引数はtrackのみを指定します.

Streamingを使用するときは, インスタンス化のときに指定した
リスナーのコールバック関数が呼ばれるので, 以下のようなコードになります.
この例では, 10件のデータを取得できたらシステムを終了するようにしています.

class MyStreamListener(tweepy.StreamListener):
    def __init__(self):
        self.count = 0
    
    def on_data(self, data):
        self.count += 1
        new_line = unicode(data, 'unicode_escape').encode('utf-8')
        new_line = new_line.replace('\\', '')
        print self.count, new_line
        if self.count == 10:
            sys.exit(0)
        return True
        
def search_stream(auth, keyword):
    stream = tweepy.Stream(auth, MyStreamListener())
    stream.filter(track=[keyword])

ひとつ注意が必要なのは, StreamingはTwitterイマを取得するものなので,
過去ツイートの検索は出来ません.
"中野"をキーワードに指定すると, 実は, なかなか更新されなかったりします...

on_data()で取得できるdataは, JSON形式の文字列となります.
また, 日本語は'\u3042'などのコードでかえってくるので,
UTF-8エンコードしてあげるなどの一手間が必要になります.

おわりに

さらっとでしたが, Tweepyの紹介でした.
Tweepy面白いので, もう少し詳細に調べて遊んでみようと重います.
そして, とってきたJSONデータをpandasに突っ込んで遊んでみる
みたいなこともやってみようと思います.

では!!

おめでとうございます, ブログ初めてみました

新年、あけましておめでとうございます.
本年もよろしくお願いいたします.

昨年末にブログを始めまして,
早速1つ記事を書いてみました.
ctypes ~pythonでCをイジる(Macで)~
少しずつでも更新していける様, がんばります.

なぜブログを書くのか, 的なのは同じようなエントリーがたくさんあるのでいいですね.
僕がブログを始めた理由も皆さんが言ってるようなことなんで割愛です.

何を書いていくかということは簡単に触れておきたいと思います.
おおよそ, 以下のような内容になると思います.

  • プログラミング関係
  • ランニング関係
  • 我がホームタウン, 中野の情報

なんというか, まとまりがない感じですね.
基本, 書きたいこと書くだけになるのですが,
時に読んだ人のためになるようなことが書ければ幸いです.

内容について, 少しだけ補足をしますね.

プログラミング関係

一応, へっぽこながらもIT系のエンジニアとして働いております.
AndroidだったりiPhoneだったり, PythonだったりCだったり
色々と触っていますので, 日々学んだことをアウトプットします.
統計とかも勉強しているので, 書けるようになったらいいな..
時には書評とかもあるんでしょうかね?

ランニング関係

趣味でランニングをしています.
1年くらい前からちょいちょい走り始めて,
今はハーフを1時間40分をちょっと切って走るくらいです.
東京マラソンに当選したので, 年末年始もがんばってます.
誰かの参考になるようなことが書けるかどうか... どちらかというとランニング記録的な内容になりそうですね.

中野の情報

僕は中野出身で, 学校とかバイトとか多くの時間を中野で過ごしてきました.
休日にランチにいくのも, 仕事帰りに飲みにいくのも,
珈琲を飲みにいくのも, たいてい中野です.
そんな中野が再開発のおかげで最近フィーチャーされてるのは嬉しい限りです.
僕も色々と好きなお店があったりするので, 紹介できればいいなと思います.


内容についてはこんなところですね.
書き始めたらどうなることやらわかりませんが,
継続して更新していこうと思います.

それでは今年もすばらしい1年にしましょう!!

ctypes ~pythonでCをイジる(Macで)~

ctypesって??

ctypesとは、pythonc言語のライブラリを操作するためのライブラリです.
FFI(Foreign Function Interface)というんですね, こういうもののこと.
ドキュメントはココにあります.
このスライドも参考になりました.

似たようなものでCythonというのもありますが,
純粋なpythonスクリプトでCの関数を実行できることがctypesのメリットです.
pythonのプログラムを高速化するのだ!!ということであればCythonかもしれませんが,
ちょっとしたデバッグ用に使うようなときはctypesでサクっと書いた方が良さそうです.

なんでctypes??

もともとAndroidのJNIで使用するためのCのプログラムを書いていました.
で, デバッグの過程でそれを可視化したいなと思っていたのですが,
描画なんてpythonでサクっとやってしまいたいと思うのが男心ですよね.
そんな経緯で調べてみたらctypesを知りまして,
使ってみたら思った通りサクっとできて幸せになりました.

簡単にではありますが, ctypesについて紹介させていただきます.

使い方

MacとWindowsでライブラリのロードの方法がかわってきます.
今回は手元のMacbook Pro(Mavericks)で実行してるので, その前提ですすめていきます.
ソースコードGitHubにおいてあります.

動的ライブラリのコンパイル

まずはCのコードをコンパイルします.
Macの場合はso形式が必要となりますので

gcc ctypes_sample.c -shared -o libctypessample.so  

という感じでコンパイルします.

ライブラリのロード

まずは, 先ほどコンパイルしたライブラリを読み込みます.
Macでやっているので,
ctypes.cdll.LoadLibrary(libname)
を使用します.

from ctypes import *

libc = LoadLibrary("libctypessample.so")

という感じになりますね.

引数も戻り値もない関数

ここからはpythonのコードを見ていきます.
ひたすらいろいろな関数を実行していきますので, ざっと見てみてください.

では試しに, コンソール出力するだけの関数を呼び出してみましょう.

libc.print_with_no_arg()  

コンソール出力はこのように.

[native] print_with_no_arg called

たったこれだけですが, 確かにCの関数を呼び出すことができました!!

引数を持つ関数

先ほどの関数より少しだけ進化した, 引数を1つとってコンソール出力する関数です.

libc.print_with_int_arg(c_int(100))  
libc.print_with_float_arg(c_float(12.34))  

[native] print_with_int_arg called, arg:100
[native] print_with_float_arg called, arg:12.340000

はい, 正しく出力できています.
ここでc_int, c_floatが出てきましたが, これらはctypesの基本データ型です.
データ型については公式ドキュメントに任せることにします.

また, 実は, c_int(100)の部分を100としても実行できます.
ただし, 後述するbyref(className)を使用した値渡しを行いたいときなどは,
上記の基本データ型でなければ正しく実行できません.

戻り値もある関数

今度は, 引数でとったint型の値に1を足す関数です.

added = libc.add_one(c_int(100))  
print "orig:%d, added:%d" % (100, added)  

[native] add_one called
orig:100, added:101

関数の戻り値がc_int型ではなくpythonの整数型になっていることがわかりますね.

参照渡し

上の関数と似ているのですが, 引数をポインタとして渡し, そのポインタに1を足します.

num_c = c_int(100)  
libc.add_one_to_ptr(byref(num_c))  
print "orig:%d, added:%d" % (100, num_c.value)  

[native] add_one_to_ptr called
orig:100, added:101

でてきました, byref(className)です.
これを使うことで, 今回であればc_int型のポインタを作ることが出来ます.
もうひとつ, ctypesの基本データ型から値を取り出す場合, valueメンバにアクセスします.
上記のnum_c.valuenum_cとした場合, 当然TypeErrorで怒られます.

配列を扱う

少しずつ楽しくなってきましたね. 今度は配列を扱ってみます.
int型配列の各要素に1を足すというCの関数を実行しています.

IntArray10 = c_int * 10  
i_arr = [i for i in xrange(10)]  
i_arr_c = IntArray10(*i_arr)  
print "before:", [i_arr_c[i] for i in xrange(10)]  
libc.add_one_in_array(i_arr_c, 10)  
print "after:", [i_arr_c[i] for i in xrange(10)]  

before: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[native] add_one_to_array called
after: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

ctypesで配列を扱うときは, 基本データ型の配列の型をつくることからはじまります.
例では, 10個のc_int型からなるIntArray10を定義し, インスタンス化しています.

構造体を扱う

さて, Cでプログラムを書いていたら, 当然構造体をつかいますよね.
ctypesは構造体も簡単に扱うことができます.

Cのプログラムでは, このような構造体を定義しています.

typedef struct {  
    float x;  
    float y;  
    float z;  
} FPOINT_3D;

まずは, 上記の構造体を受け取り, その内容をコンソール出力するプログラムを考えます.
Cの構造体に対応するクラスをpython側で定義します.

class FPoint_3D(Structure):  
    _fields_ = [  
            ("x", c_float),  
            ("y", c_float),  
            ("z", c_float)]  
  
    def __init__(self, x, y, z):  
        self.x = x  
        self.y = y  
        self.z = z  

このように, ctypes.Structureを親に持つクラスを定義します.
そして, _fields_を[("メンバ名", 基本データ型), ...]の形式で記述します.
初期化しやすいように, def __init__も定義しました.
呼び出すプログラムは非常にシンプルになります.

point_3d = FPoint_3D(1.1, 2.2, 3.3)  
libc.print_fpoint3d(point_3d)  

[native] print_fpoint3d x:1.100, y:2.200, z:3.300

確かに自作の構造体を渡すことができました.

構造体のポインタを扱う

今度は同様の構造体のポインタを渡し, 各メンバに1を足す関数を呼び出します.

point_3d = FPoint_3D(1.1, 2.2, 3.3) 
print "before: x:%.3f, y:%.3f, z:%.3f" % (point_3d.x, point_3d.y, point_3d.z) 
libc.add_one_in_fpoint3d(byref(point_3d))  
print "after: x:%.3f, y:%.3f, z:%.3f" % (point_3d.x, point_3d.y, point_3d.z)  

before: x:1.100, y:2.200, z:3.300
[native] add_one_in_fpoint3d
after: x:2.100, y:3.200, z:4.300

おそらく予想通りの呼び出し方でしょう.
任意の構造体であってもbyref()でポインタとして扱うことが出来ます.

構造体の配列を扱う(ついでに描画もしちゃう)

一応, 用意した最後の関数です.
今回は少しかわっていて, まず, set_random_to_array()関数を呼び出し,
CのstaicなIPOINT_2D型配列の各要素の各メンバにランダムな0から9までの値をセットします.
そして, その配列をpython側で用意した配列にコピーするという処理になります. IPOINT_2Dはint型のx,yからなる構造体で, 2次元の方が描画が楽だという理由で用意しました.
python側のIPoint_2Dクラスの実装は想像通りです.

libc.set_random_to_array()  

IPoint_2DArray100 = IPoint_2D*100  
point_2d_arr = IPoint_2DArray100(*(IPoint_2D(0,0) for i in xrange(100)))  
libc.cpy_array(point_2d_arr)  

基本データ型の配列と同様に, 型名 * 長さで配列型を定義し, インスタンス化しています.
ついでに, matplotlibを使って結果をプロットしてみましょう.

f:id:curlnoodle:20131230221136p:plain

このデータ自体に意味がある訳ではないのですが,
Cでの処理結果を取得してビジュアルで確認したりできるんですね.
pythonだとmatplotlibやnumpyやscipyなどデータを処理するのに便利なモジュールが
たくさんそろっているのでサクっといじるのにもってこいですよね.

最後に

長くなりましたがctypesの使い方の参考になれば幸いです.
ブログもちょいちょい更新していける様がんばります.

ありがとうございました.

詳説 Cポインタ

詳説 Cポインタ