Perlで一日分のTwitterの発言を取る方法

Posted at 2010-03-17T15:36:23+09:00 in Coding

Perlを使ってTwitter上の一日の発言を引っこ抜く!

僕がTwitterでつぶやかなくなった理由という記事を書いてからなぜかモリモリTwitterでつぶやくようになったnyarlaです。 みんなつぶやいてますか?

まあそんな前置きはどうでもいいんですが、今日ふと無貌断片の方に、 一日分のTwitterの発言を投稿したらよさげなんじゃないかなーと思い投稿スクリプトを書いてたんですが、なんか途中で面倒になってきたので、 Twitterのタイムラインから一日の発言を引っこ抜く部分だけ抽出してBlogの記事にしてみることにしました。

ということでTwitterの発言から一日分を抜き出すコートは下記の通り。

#!/usr/bin/perl
# this code is under public domain.

use strict;
use warnings;

use XML::Feed;
use URI;

my $username    = 'nyarla';
my $userid      = '3402381';
my $timezone    = 'Asia/Tokyo';

my $now         = time;
my $yesterday   = $now - ( 60 * 60 * 24 );

my $page        = 1;
my $uri         = URI->new("http://twitter.com/statuses/user_timeline/${userid}.atom");
   $uri->query_form( count => 200, page => $page );

my @timelines   = ();

LOOP: while ( 1 ) {
    my $feed = XML::Feed->parse( $uri )
        or die "Failed to fetch twitter timeline feed: " . XML::Feed->errstr;

    for my $entry ( $feed->entries ) {
        my $twit = $entry->content->body;
           $twit =~ s{^$username:[ ]}{};

        my $date = $entry->issued;
           $date->set_time_zone( $timezone );

        if ( $date->epoch <= $yesterday ) {
            last LOOP;
        }

        push @timelines, +{
            twit => $tiwt,
            date => $date,
        };

        $page++;
        $uri->query_form( count => 200, page => $page );
    }

    sleep 1;
}

for my $timeline ( @timelines ) {
    my ( $twit, $date ) = @{qw( $timeline )}{qw( twit date )};
    # your code here.
}

exit;

なお上記のコードは自由に使って欲しいのでパブリックドメインとします。

んで解説。

最初の方の$username$userid$timezoneは各環境にあわせて書き換えてください。 $usernameはTwitter上のURIに使ってるscree name、$useridはTwitterのタイムラインのFeedのURIのなんかIDっぽい文字列、 $timezoneはPerlのDateTimeに指定できるタイムゾーンという感じです。

で、後の部分はTwitterのタイムラインから一日分の発言を引っこ抜く部分で、 一日分の発言が引っこ抜けるまで無限ループしてページを辿ってって、 引っこ抜けたらループを終わる、というコードです。 まあそのまんまなコード書いてるので、多分コード読めば理解できると思います。

まあコードの話は以上。

で、うちの空繰再繰と無貌断片ではplasxomという自作のBlogシステムを使ってて、 ファイルの管理にgitを使ってるんですが、空繰再繰と無貌断片に記事を投稿する時、

  1. 記事書く
  2. ローカルの管理ディレクトリに記事を追加
  3. git addして新しい記事をgitの管理下に置く
  4. git ci && git pushする
  5. サーバのpost-updateフックスクリプトがサーバ上のplasxomの記事ディレクトリをupdate

という感じの流れになるんですが、サーバ上で記事ファイルを生成とかすると、 git add && git ci && git pushをやらなくちゃいけなくなるのと、 記事を追加する前にローカルでgit pull origin masterしないと問題があるのと、 Twitterの一日のまとめログのタイトルどーすんだべ!?という問題に遭遇し、 なんかスクリプト書くのがめんどくさくなった次第。 まあちょっとした手間なんだけど、やる気が萎えてしまったと言う。

まあTwitterの一日の発言を引っこ抜く部分はできたんで、 まあ良しとしとこうかなという感じです。

以上、スクリプト書いてたらやる気がなえてしまったという話でした。

Params::Validateのバージョンが古いとLog::Dispatchのテストがこける

Posted at 2010-03-16T13:05:29+09:00 in Coding

Params::Validateのバージョンが古いせいでLog::Dispatchのテストがこけてた

タイトルまんまです。

昨日個人的に作ってるWeb Applicationのロギング機能にLog::Dispatch使おうと思ってインストールしようとしたんですが、 http://www.cpantesters.org/cpan/report/6803983と似たようなログが出てテストがこけていました。

で、面倒だったんでnotest install Log::Dispatchでインストールしていたんですが、 どうにも動作がおかしいくて、ぜんぜんきちんと動いてなかったわけです。

で、今日それについて調べて見て、CPAN Testersで僕と同じCygwin + Perl 5.10.1環境で、 テストがfailedしたケースとテストがパスしたケースを調べて見たところ、 Params::Validateのバージョンが違っているということに気が付きました。

で、今日これに気づいてParams::Validateのバージョンをあげてみたところ、 きちんとLog::Dispatchのテストが通るようになりました。

ちなみにParams::ValidateのChangesを読んでみたところ、 version 0.91でLog::Dispatchのテストが失敗するんで修正したよ!と書いてあったんで、 多分それが原因だったみたいです。

まあこれでLog::Dispatchが動かないのは直ったんですが、 昨日作ったLoggerクラスは動かないんで破棄してしまったんで、 最初から作り直しですけどね><

以上Log::DispatchParmas::Validateのバージョンが古いとおかしくなるという話でした。

Perlスクリプトにバイナリファイル埋め込む方法

Posted at 2010-01-24T16:25:28+09:00 in Coding

Perlのスクリプトのバイナリファイルをテキストで埋め込む

しばらく前の話になりますが、この前に書いたGoogle App Engineでホスト名を効率よく取得する方法を考える という記事内の二個目の方法を実現するスクリプトをこの間の記事を書いた後に書いたとき、 画像ファイルをpsgiスクリプトに埋め込むということが必要になったので、そのとき調べたことを今日書いてみます。

まず、画像ファイルをテキスト、つまりバイナリファイルをテキストファイルとして表現するには、 uuencodeというフォーマットがあるらしいです。

で、Perlでバリナリファイルをこのuuencode文字列に変換するには、

#!perl

use strict;
use warings;

my $file = shift @ARGV;

open( my $fh, '<', $file ) or die "Cannot open file: ${file}: ${!}";
binmode $fh;
my $data = do { local $/; <$fh> };
close $fh;

my $text = pack('u*', $data);

print $text;

というスクリプトを例えばbin2text.plという感じで用意して、

$ ./bin2text.pl {binaryfile}

という感じで実行すれば、uuencodeのデータ部分が取得できます。

んで、このテキストをバイナリに戻すには、

#!perl

use strict;
use warnings;

my $source = q{uuencode形式のデータ};
my $binary = unpack('u*', $source);

という感じになります。

んで、例えばpsgiスクリプトか何かで、テキスト化された画像バイナリファイルを返すには、

#!perl

use strict;
use warnings;

# 1 x 1の透明なgifファイル
my $image   = unpack('u*', q{K1TE&.#EA`0`!`(```/_______R'Y!`$*``$`+``````!``$```("3`$`.P``});
my $length  = legnth($image);

return sub {
    return [
        200,
        [
            'Content-Type'      => 'image/gif',
            'Content-Length'    => $length,
        ],
        [ $image ]
    ]
};

という感じになります。

でこの間書いたって言ってる 任意のホストにファイルにアクセスしてきた人のIPアドレスとリモートホストを送信するスクリプトでは、 要約すれなこんな感じの方法でWebビーコンを返しています。

まあこのuuencodeっていう形式は大昔に使われてた形式らしくて、今はBase64がメジャーらしいのですが、 ちょっとしたスクリプトで使うためにMIME::Base64使うのもアレなのでuuencodeを使ってみた次第。

まあちょっと知っておくと便利かもということで今日この間調べたことをまとめてみました。

Google App Engineでホスト名を効率よく取得する方法を考える

Posted at 2010-01-18T17:27:59+09:00 in Coding

現在のGoogle App Engineで取得できないリモートホストを効率よく取得する方法

2010年1月18日現在、Google App Engineではソケットの使用とかDNSの名前解決ができないため、 リモートホストが取得できません。またGoogle App Engine上でリモートホストが必要な場合、 外部のサーバ経由で取得するとかしか方法がないのが現状です。

で、外部サーバ経由でリモートホストを取得する方法について、 今日本屋で行った帰りの電車の中で思いついたので、とりあえず書き留めてみるよ。

方法一、リモートホストを取得するWebサービスにリクエストを送ってリモホを取得する

これは僕が最初に思いついた方法なんですが、

  1. リモートホストが必要になるたびに、リモートホストを返すWebサービスにリクエストを送る
  2. Webサービスから返ってきたホスト名を使用する

という方法です。

シンプルで分かりやすいのが利点といえば利点ですが、

  1. リモートホストが必要になるたびに外部サーバにリクエストを送ることになる
  2. 貴重な外部サーバへのリクエスト要求のリソースを無駄に消費することになる

という欠点があります。

まあキャッシュしたりすればある程度はリクエストを減らすこともできると思いますが、 それでも上記の問題は完全に解決できるわけではありません。

で、今日思いついた方法が次。

方法二、外部サーバにリモートホストをGAEサーバに送らせる

これは今日思いついた方法なんですが、これは、

  1. GAEのサイト上にIPアドレスとリモートホストをGAEサーバに送信するWebアプリケーションを埋め込む
  2. リモートホスト送信サーバから送信されたリモートホストをmemcacheに保存する
  3. リモートホストが必要なときはmemcacheからリモートホストを取得する

という方法です。

一番の埋め込み方法としては、

  1. 隠しiframeとかでリクエストが飛ぶようにする
  2. Webサービスの方でWebビーコン返すようにして画像として埋め込む

という方法が考えられます。

あとIPアドレスをリモートホストを返すWebサービスで、 IPとリモートホストの対応が変更された場合にのみ、GAEサーバにリクエストを送る という風にしておけば、GAE上のリソースの消費を最小限に抑えることができます。

で、この方法の欠点としては、

  1. 結局外部サーバに依存してしまう
  2. 少なめといえGAEのリソースを消費する
  3. GAE上のサイトに初めてアクセスしたユーザーのリモートホストが取得できない(次回以降になる)

という点が挙げられます。

まあ一番二番は方法一でも一緒です。あと三番に関しては、この方法独自の問題と言えますが、 方法一と組み合わせるとなんとかなるんじゃないかと思います。

まとめ

まあ一番最初に思いついた方法も、今日思いついた方法も、 Google App Engineでリモートホストが取得できるようになればいらない子になることが確実な方法です。

つーかあとリモートホストが取れなくても、リモートアドレス取得すればいいじゃないって話なので、 上記の方法はどうしてもリモートホスト取りたいって時以外はあんまり必要無い方法だと思います。

で、なんでリモホにこだわるかっていうと、GAE上で掲示板とかコメントサービス作ろうとしたときに、 リモートホストで規制掛けられると便利じゃないかなぁと思ったからです。

まあ今日まとめた方法がどう考えてもバッドノウハウな気がしてたまらないので、 どうしてもリモートホストが必要なときに試してみてはどうでしょうか。

Net::AamzonでアソシエイトIDを指定する方法

Posted at 2009-04-02T17:58:53+09:00 in Coding

perlのNet::AmazonでアソシエイトIDを指定する方法。

perlのNet::AmazonでアソシエイトIDを指定する方法が分からなくて、 ソース読んでやっと分かったのでメモ。

ぶっちゃけ結論から言うと、Net::Amazon::Request::*系のオブジェクトを生成するときに、 コンストラクタの引数で直接ECS4のパラメーターを指定すればOK。

要するにこんな感じ。

use Net::Amazon;
use Net::Amazon::Request::ASIN;

my $token  = 'XXXXXXXXXXXXXXXXXXXX';
my $locale = 'jp';

my $ua  = Net::Amazon->new( token => $token, locale => $locale );
my $req = Net::Amazon::Request::ASIN->new( asin => 4061826263, AssociateTag => 'example-22' );

my $res = $ua->request( $req );

AssociateTag以外のパラメータも上記の方法で指定できるみたい。 が、Net::Aamzonが内部でいじってるパラメーターを指定するとおかしくなるかもしれない。 まあもっと泥臭いコードを書かなきゃいけないのかと思ったら以外とシンプルに書けた。

まあこれでNet::Amazonを使ったアフィリエイトサイトとか作れそうです。 多分当分はやらないだろうけど。

About Me

name
Naoki Okamura
nick
nyarla
contact
nyarla[ at ]thotep.net
Category
Log
Banners
Powered by
nim