twitter で 60 日以上発言が無い人を remove する - Djangoへの片思い日記
僕自身はFollowはまだ200人弱で、全然制限に引っかからないので困らないのだけど、面白そうなので自分でも書いてみる。
まずはAPIの選定。
http://watcher.moe-nifty.com/memo/docs/twitterAPI.txt
自分がフォローしているユーザーを拾うにはこれしかなさそう。
friends
自分の friend の一覧を(各 friend の最新ステータス付きで)取得する
引数 id を指定すれば、その id のユーザの friend の一覧を取得できる
ただし、この API で取得できるデータは最大100件(100人分)である
URL: http://twitter.com/statuses/friends.format
(format は xml, json のうちのいずれかを指定)100件以上の場合はpage指定して分けるしかない。
page=ページ番号 (オプション)
(1ページを100件とみなしたときの)ページ番号を指定することで、指定ユーザの friend の一覧を100件単位で取得する
例:
http://twitter.com/statuses/friends.xml?page=2
API実行時点で101件目から200件目に相当する(自分の)friend の一覧を XML 形式で取得する
とりあえず試しに取ってみる。
$ perl -MLWP::Simple -le 'getprint "http://twitter.com/statuses/friends.json?id=sugyan"'
[{"following":false,"verified":false,"description":"...たくさん出てきた。BASIC認証は必要ないらしい。これを使ってワンライナーで色々やってみよう。
まず、何度も色々ためしているとあっという間にAPI制限に引っかかってしまうので、一度データをローカルに保存してそれを使う。
$ wget "http://twitter.com/statuses/friends.json?id=sugyan" $ mv friends.json\?id=sugyan friends.json $ ls -l friends.json -rw-r--r-- 1 sugyan staff 134252 6 29 22:11 friends.json
というわけで、まずはJSONの解析。JSONモジュールを使う。
$ perl -MJSON -le 'print from_json <>' < friends.json
ARRAY(0x8104c8)
$ perl -MJSON -le 'print scalar @{from_json <>}' < friends.json
100ちゃんと100件のデータが詰まっているらしい。
$ perl -MJSON -le 'print for @{from_json <>}' < friends.json
HASH(0x8192d8)
HASH(0x810738)
HASH(0x84a920)
...中身はハッシュのリファレンス。とりあえず1件だけ取り出して中身を見てみよう。Data::Dumperで覗いてみる。
$ perl -MJSON -MData::Dumper -le 'print Dumper shift @{from_json <>}' < friends.json
$VAR1 = {
'friends_count' => 91,
'profile_background_tile' => bless( do{\(my $o = 0)}, 'JSON::XS::Boolean' ),
'status' => {
'source' => '<a href="http://d.hatena.ne.jp/Kiri_Feather/20071121">Tween</a>',
'favorited' => $VAR1->{'profile_background_tile'},
'truncated' => $VAR1->{'profile_background_tile'},
'created_at' => 'Mon Jun 29 10:04:43 +0000 2009',
...情報大杉。。。ハッシュのキーだけに絞ろう。
perl -MJSON -le 'print for sort keys %{shift @{from_json <>}}' < friends.json
created_at
description
favourites_count
followers_count
following
friends_count
id
location
name
notifications
profile_background_color
profile_background_image_url
profile_background_tile
profile_image_url
profile_link_color
profile_sidebar_border_color
profile_sidebar_fill_color
profile_text_color
protected
screen_name
status
statuses_count
time_zone
url
utc_offset
verified全部のrefを取ってみるとわかるけど、"status"に対応するvalueはさらにハッシュになっている。
$ perl -MJSON -le 'print for sort keys %{shift(@{from_json <>})->{status}}' < friends.json
created_at
favorited
id
in_reply_to_screen_name
in_reply_to_status_id
in_reply_to_user_id
source
text
truncated
色々調べてみるとわかるけど、
$ perl -MJSON -le 'print shift(@{from_json <>})->{screen_name}' < friends.json
daiki_kameya
$ perl -MJSON -le 'print shift(@{from_json <>})->{status}->{created_at}' < friends.json
Mon Jun 29 10:04:43 +0000 2009というカンジで、自分がフォローしているヒトのidと最終POST日時を知ることができる。
取得した100件全員の最終POST日時を一覧表示したければ、
$ perl -MJSON -le 'print "$_->{status}->{created_at}: $_->{screen_name}" for @{from_json <>}' < friends.json
Mon Jun 29 10:04:43 +0000 2009: daiki_kameya
Mon Jun 29 00:06:52 +0000 2009: sakaik
Mon Jun 29 13:06:54 +0000 2009: tsuka
Mon Jun 29 12:13:29 +0000 2009: shohu33
Mon Jun 29 10:39:43 +0000 2009: haru860
Mon Jun 29 13:03:32 +0000 2009: tenja
Mon Jun 29 11:45:47 +0000 2009: dameninngenn
Mon Jun 29 13:07:59 +0000 2009: ksorano
Mon Jun 29 01:50:56 +0000 2009: omega2
Sun Jun 28 21:26:31 +0000 2009: gunjisatoshi
...とやることができる。
さて、しかしこの"created_at"、この形式の文字列だとどうにも困る。
Date::Parseモジュールを使うとepoch秒に置き換えることができる。
$ perl -MJSON -MDate::Parse -le 'print str2time($_->{status}->{created_at}) for @{from_json <>}' < friends.json
1246269883
1246234012
1246280814
1246277609
1246271983
...ということは、取得した100件のfriendsを最新POST順に並び替えることもできるわけで。
$ perl -MJSON -MDate::Parse -le 'print join ": ", @$_ for sort { $b->[0] <=> $a->[0] } map [str2time($_->{status}->{created_at}), $_->{screen_name}], @{from_json <>}' < friends.json
1246281071: furuhouse
1246280896: Yoshikazu
1246280879: ksorano
1246280872: voluntas
1246280836: tsuyoshikawa
1246280815: mdaisuke
1246280814: tsuka
1246280752: jugyo
1246280727: otenki_bot
1246280717: kekoyana
...ちょっと長いけどシュワルツ変換を使って最新POST日時順に並べ替えて表示してみた。
http://perl-mongers.org/2008/05/schwartzian_transform_and_orcish_maneuver.html
…というわけで、「60日以上発言が無い人」というのは上記のようなsortをする必要も無く、ただgrepでフィルタリングすれば良いだけ。
例えば最近24時間でPOSTしていないヒトを列挙するのであれば
$ perl -MJSON -MDate::Parse -le 'print $_->{screen_name} for grep { str2time($_->{status}->{created_at}) < time - 60*60*24 } @{from_json <>}' < friends.json
yapcasia
KAZUTO1967
katsumic
willpon
kazukichop
...というカンジでできる。60*60*24*60と数字を変えれば「60日以上発言が無い人」というフィルタリングが可能。
さて、ここまで出来たところで、ページングにチャレンジ。
実際にAPIからデータを取得して上記までのことをやろうとすると、LWP::Simpleモジュールを使用して以下のようになる。
$ perl -MJSON -MDate::Parse -MLWP::Simple -le 'print $_->{screen_name} for grep { str2time($_->{status}->{created_at}) < time - 60*60*24 } @{from_json get "http://twitter.com/statuses/friends.json?id=sugyan"}' ここに、終了条件を加えてwhileループを回してやりたい。
終了する条件は、取得されたデータが0件になること。イメージとしてはこんなカンジ。
$ perl -MJSON -MLWP::Simple -le 'sleep(60), print $n while @{from_json get "http://twitter.com/statuses/friends.json?id=sugyan&page=".$n++}'
ということでwhile文を回しつつ60日以上発言が無い人を列挙していく。
while文を使っているとprint部分でforを使えないのでmapで代用する。
$ perl -MJSON -MDate::Parse -MLWP::Simple -le 'map { sleep(60); print $_->{screen_name} } grep { str2time($_->{status}->{created_at}) < time - 60*60*24*60 } @a while (@a=@{from_json get "http://twitter.com/statuses/friends.json?id=sugyan&page=".++$n})'完成!!
…って、ここまで書いてようやく気付いたけど、これだとBASIC認証してないから非公開のユーザーについては判定できないや。
もしかすると60日以上前まで公開していてそこから非公開にしたユーザーが含まれてしまうかも。
ちゃんとBASIC認証してタイムラインを取得するには…
$ perl -MJSON -MDate::Parse -MLWP::UserAgent -le 'map { sleep(60); print $_->{screen_name} } grep { str2time($_->{status}->{created_at}) < time - 60*60*24*60 } @a while (@a=@{($r=HTTP::Request->new(GET=>"http://twitter.com/statuses/friends.json?id=sugyan&page=".++$n))->authorization_basic(@ARGV), from_json(LWP::UserAgent->new->request($r)->content)})' sugyan ********こうか!?
あとは出力を使ってNet::Twitterとかでremoveすればいいと思います。もう疲れたw