スキップしてメイン コンテンツに移動

結局Pythonを使ってコマンドラインで動作するTwitterクライアントを作ってしまった

Twitterのアカウントを取ってから既に1年半になるが、活発に使っているとは言い難い。その原因の一つとしてTwitterのクライアントがある。どうにも自分が利用するのにピッタリだと思うクライアントが見つからなかったのだ。そこで結局、自分の好みに合わせてコマンドライン上で動作するシンプルなTwitterクライアントをPythonで作ってしまった。しかも、ワンライナー(1行プログラム)。

最初の頃はいくつかのクライアントを使ってみたのだが、PCでの作業はシェルで行うことが多いので別のウィンドウを開きたくなかったり、Windows、Unix、MacなどのOSが変わっても同じクライアントを使いたかったり、GUIじゃなくてCUIで操作したかったり、それほど使い込むつもりがないので極力シンプルでコンパクトになっていて欲しかったり、そもそもクライアントをインストールしたくなかったりと、かなり条件を厳しく求めていたら使えるクライアントがなくなってしまい、結局、公式サイトもしくは自分で作成した掲示板からたまにつぶやくだけになってしまった(ただし、Twitterfeedは利用している)。

しかし、最近になってタイムラインをよく眺めるようになり、これが結構面白いと気がついた。自分と違う考え方に触れるのは楽しい。そして、自分ももう少しつぶやいてみようかと思ったのだが、公式サイトや掲示板からのポストはいちいちブラウザを開かなくてはならず手軽だとは言い難かった。

そこで、先日、PythonのワンライナーでTwitterを使えるようにしたこともあり、もう少し改良してそれをクライアントにすることにした。Pythonスクリプトなら、Pythonが入っていればどのOSでも動作するし、使い方はどこでも一緒で、コマンドラインでそのまま使え、タイムラインをgrepなどで簡単に選択表示できる。それに、たった1行のソースコードなので簡単に中身を確認でき、パスワード漏洩やキーロガーなどを心配しなくてもいい。

tw.py

でタイムラインを取得することができ、

tw.py つぶやき tw.py つぶやき http://handasse.blogspot.com/ ブログのURLです。 tw.py "つぶやき&つぶやき" tw.py "つぶやき つぶやき"

などでTwitterにつぶやくことができる。つぶやきに&などの特殊文字や連続でスペースが入る場合などはダブルクォーテーション(")で括ること。つぶやきに入っているURLは自動的にbit.lyによる短縮URLとなる。

また、Windows PowerShellで使用する場合は、プロファイル($profileで表示されるファイルで、Microsoft.PowerShell_profile.ps1などを指し、PowerShell起動時に読み込まれる)に以下の関数を定義しておくと便利だ。

function tw { python 実行パス\tw.py "$args" }

Unixなどで日本語コードが異なっていても、ソースを変更せずにnkfなどを使ったエイリアスで簡単に対処できる。ただ、特殊文字などを使ったときに問題が出るかもしれない。以下の例は、Unix側の表示がUTF-8でシェルがtcshの場合。

alias tw 'tw.py `echo -n \!* | nkf -s` | nkf -w'

これならば tw とするだけで実行できる。また、PowerShellでは tw | select-string フレンド名、Unixでは tw | grep フレンド名で、指定したフレンドの発言をタイムラインから簡単に抜き出すこともできる。

今回のPythonによるTwitterクライアントのソースを以下に示す。simplejsonを使っている。bit.lyのアカウント名とAPI Keyはbit.lyアカウントのページですぐに取得できる。また、Base64でエンコードされたユーザ名とパスワードの作成は、Pythonのbase64モジュールを使ってもいいし、以前にGoogle App Engineで作ったバイナリ/アスキー変換を利用してもらっても構わない。まあ、エンコードされていてもセキュリティが強固になるわけではないのだが、ぱっと見でばれてしまう平文よりはいいんじゃないかな。ワンライナーにしたのもぱっと見で解りづらくするためだし。

ところで、TwitterのAPIでデータを取得したり送信したりすると、たまに失敗することがあるので、エラーが出たときは再度実行すること。

tw.py

#!/usr/bin/env python import sys,re,urllib,urllib2,xml.sax.saxutils,base64,simplejson;pm=urllib2.HTTPPasswordMgrWithDefaultRealm();pm.add_password(None,'twitter.com',base64.b64decode('エンコードされたユーザ名'),base64.b64decode('エンコードされたパスワード'));urllib2.install_opener(urllib2.build_opener(urllib2.HTTPBasicAuthHandler(pm)));tw=None;len(sys.argv)>1and[globals().__setitem__('tw',' '.join(sys.argv[1:]))];F=lambda x:str(simplejson.loads(urllib2.urlopen('http://api.bit.ly/shorten?version=2.0.1&longUrl=%s&login=bit.lyのアカウント名&apiKey=bit.lyのAPI Key'%urllib.quote(x)).read())['results'][x]['shortUrl']);G=lambda x:re.sub(re.escape(x),F(x),tw);tw and[globals().__setitem__('tw',G(link))for link in sorted(re.findall(r'(http(?:s?)\:\/\/[^\/\ ]+\/.*?)(?:[\ <>\"\{\}\|\\\^\[\]\`]|$)',tw),reverse=True)];tw and[urllib2.urlopen('http://twitter.com/statuses/update.xml',urllib.urlencode({'status':tw.decode('cp932').encode('utf-8')}))]or[sys.stdout.write(''.join(['%s: %s\n'%(d['user']['screen_name'],re.sub(r'\r\n|\n|\r',' ',xml.sax.saxutils.unescape(d['text'])))for d in simplejson.loads(urllib2.urlopen('http://twitter.com/statuses/friends_timeline.json').read())]).encode('cp932','replace'))]

追記(2009/9/9):

今までTwitterのお気に入り機能を使っていなかったけど、ふぁぼったーを見て使いたくなったので、今回のコマンドライン型Twitterクライアントでも使えるように改良した。

まず、tw.pyについてはつぶやきの最後に[つぶやきのID]を表示するようにした。また、fav.pyでつぶやきをお気に入りに加えることができる。使い方は以下の通り。

fav.py [つぶやきのID]

tw.pyで表示されたIDを入力する。コピー&ペーストだと簡単。IDの括弧([])は入れても入れなくても構わない。また、IDを付けずに

fav.py

とすれば、自分のお気に入りを表示することができる。

ただ、お気に入りを使わないのであればtw.pyによるIDの表示は目障りになるかもしれないので、そのときは以前のtw.pyを使った方がいいと思う。

以下、修正したソース。

tw.py

#!/usr/bin/env python import sys,re,urllib,urllib2,xml.sax.saxutils,base64,simplejson;pm=urllib2.HTTPPasswordMgrWithDefaultRealm();pm.add_password(None,'twitter.com',base64.b64decode('エンコードされたユーザ名'),base64.b64decode('エンコードされたパスワード'));urllib2.install_opener(urllib2.build_opener(urllib2.HTTPBasicAuthHandler(pm)));tw=None;len(sys.argv)>1and[globals().__setitem__('tw',' '.join(sys.argv[1:]))];F=lambda x:str(simplejson.loads(urllib2.urlopen('http://api.bit.ly/shorten?version=2.0.1&longUrl=%s&login=bit.lyのアカウント名&apiKey=bit.lyのAPI Key'%urllib.quote(x)).read())['results'][x]['shortUrl']);G=lambda x:re.sub(re.escape(x),F(x),tw);tw and[globals().__setitem__('tw',G(link))for link in sorted(re.findall(r'(http(?:s?)\:\/\/[^\/\ ]+\/.*?)(?:[\ <>\"\{\}\|\\\^\[\]\`]|$)',tw),reverse=True)];tw and[urllib2.urlopen('http://twitter.com/statuses/update.xml',urllib.urlencode({'status':tw.decode('cp932').encode('utf-8')}))]or[sys.stdout.write(''.join(['%s: %s [%d]\n'%(d['user']['screen_name'],re.sub(r'\r\n|\n|\r',' ',xml.sax.saxutils.unescape(d['text'])),d['id'])for d in simplejson.loads(urllib2.urlopen('http://twitter.com/statuses/friends_timeline.json').read())]).encode('cp932','replace'))]

fav.py

#!/usr/bin/env python import sys,re,urllib,urllib2,xml.sax.saxutils,base64,simplejson;pm=urllib2.HTTPPasswordMgrWithDefaultRealm();pm.add_password(None,'twitter.com',base64.b64decode('エンコードされたユーザ名'),base64.b64decode('エンコードされたパスワード'));urllib2.install_opener(urllib2.build_opener(urllib2.HTTPBasicAuthHandler(pm)));len(sys.argv)>1and[urllib2.urlopen('http://twitter.com/favorites/create/%d.xml'%int(sys.argv[1].strip('[]')),{})]or[sys.stdout.write(''.join(['%s: %s [%d]\n'%(d['user']['screen_name'],re.sub(r'\r\n|\n|\r',' ',xml.sax.saxutils.unescape(d['text'])),d['id'])for d in simplejson.loads(urllib2.urlopen('http://twitter.com/favorites.json').read())]).encode('cp932','replace'))]

コメント