June 2008 Archives

実用! PerlでコマンドラインからTwitter投稿

| 0 Comments | 0 TrackBacks | このエントリーをはてなブックマークに追加 このエントリーのはてなブックマーク件数

最近WindowsからLinuxへログインして作業していることが多いんですけれども、なんかつぶやきたくなったときにこんなの。Twitterにポスト。Net::Twitterモジュールを使います。

今回の流れは、

  • とりあえず投稿できるスクリプトを作ってみる
  • コマンドラインから投稿できるものにグレードアップ

となっています。

とりあえず投稿できるスクリプトを作ってみる

まず、固定文字列だったらこんな感じで。「もへもへ」という文字列を投げます。username、passwordは自分のを使おう。

Filename:twit_test.pl (UTF-8で)

#!/usr/bin/perl
use strict;
use warnings;
use Net::Twitter;
my $twit = Net::Twitter->new(
  username => 'username',  #ユーザー名
  password => 'password',  #パスワード
);
$twit->update('もへもへ');

実行すると、Twitterに投稿されるはずです。文字化けしてたら、おそらく文字エンコーディングがUTF-8以外です。UTF-8で保存し直すか、Encodeモジュールで適切に変換してあげましょう。

コマンドラインから投稿できるものにグレードアップ

上のスクリプトを生かして、コマンドラインからの文字列を投稿するスクリプトに改造します。今回はタイプ量減らしたいので、コンソールがUTF-8の場合に限定。他の環境の場合、Encodeモジュールやらなにやら使用のこと。

Filename: twitter

#!/usr/bin/perl
use strict;
use warnings;
use Net::Twitter;
my $twit = Net::Twitter->new(
  username => 'username',  #ユーザー名
  password => 'password',  #パスワード
);
$twit->update($_) for @ARGV;

上記twitterというファイルに実行権限を与えておいて、個人用実行ファイルを置くディレクトリ($HOME/bin とかにpath通しておくと便利)に入れるとかしておくと、

$ twitter エロス

とか

$ twitter 'エロス カワユス'

とかで投稿できます。

net-twitter-post.jpg

今回のスクリプトを改造して、定期的にビーコン打ち出すとか、作業が終わったらnotify出すとか応用できます。ちょっと考えたら、うちの業界のサーバには、製版が終わったらメールを送信する機能が付き始めていますが、こういうのもついていてもいいかとも思いました。

※調子こいて投稿しまくっていると連投制限に引っかかるみたいなので注意しましょう。

codereposのコミット数ランキングページを作る

| 0 Comments | 0 TrackBacks | このエントリーをはてなブックマークに追加 このエントリーのはてなブックマーク件数

先日tokuhiromさんのブログで、ohlohというサービスを知りました。

凄いですね、1位tokuhiromさん、2位cho45さん両方とも1000commits超えています。さて今日はこのohlohという開発者向けサイトが提供するAPIを使って、codereposコミット数ランキングを作ってみたいと思います。

ohlohのAPIドキュメントによると、ContributorFactというAPIでtokuhiromさんの記事にある様なランキングが取得出来る事が分かりました。

APIを利用する為にはAPIキーが必要になりますので、アカウントを取得後にAPI Keysのページでキーを取得、リクエストクエリの後にapi_keyというパラメータで送信すればAPIが実行出来ます。単純にDumpしても面白くないので、今日はXMLで結果取得したデータをJSONで出力し、Ajaxで扱えるマッシュアップサイトぽい物を作ってみます。

perlのコードは単純に

#!/usr/bin/perl

use strict;
use warnings;
use LWP::Simple;
use XML::Simple;
use JSON::Syck;
use CGI;

my $api_key = "your-api-key";
my $callback = "";

my $q = new CGI;
$callback = $q->param("callback") if defined $q->param('callback');
my $content = get("http://www.ohloh.net/projects/8359/contributors.xml?api_key=$api_key");
my $parser = XML::Simple->new();
my $xml = $parser->XMLin($content);
my $json = JSON::Syck::Dump($xml);

print $q->header(
  -type    => 'text/javascript',
  -charset => 'utf-8'
  );
print "$callback($json)";

という、XMLinからJSON::Syckへの流し込みのコードになります。

これをCGIとして実行し、JSONを生成します。またクライアント側は

<script type="text/javascript" src="jquery-latest.js"></script>
<script type="text/javascript"><!--
$(function() {
	$('#coderepos-committers').html('');
	$.getJSON("http://your.example.com/coderepos-ranking.cgi?callback=?", function(data) {
		$.each(data.result.contributor_fact, function(index, item) {
			$('<div>').html(
				'<b>' + item.contributor_name + '</b><br />' +
				'<blockquote>' +
				'main language: ' + item.primary_language_nice_name + '<br />' +
				'commits: ' + item.commits + '<br />' +
				'</blockquote>').appendTo('#coderepos-committers');
		});
	});
})();
--></script>
<div id="coderepos-committers"></div>

とすれば、以下の様な画面が表示されます。

http://perl-mongers.org/2008/06/20/coderepos-ranking/coderepos-ranking.png

XMLinからJSON::Syck::Dumpの流れが便利すぎますね。:-)

このランキングを作るロジック自体、手数の多そうな処理になりますからこのAPI便利かもしれません。他のプロジェクトでこの様なランキングページを作ってみても面白いかもしれませんね。

S3に写真を一括アップロード

| 0 Comments | 0 TrackBacks | このエントリーをはてなブックマークに追加 このエントリーのはてなブックマーク件数

Livedoor picsだと容量を気にする必要があるので、AmazonのS3にバックアップするスクリプトを書いてみました。これで、いざFlickrのデータが飛んでも大丈夫ですね。

#!/usr/bin/env perl
use strict;
use warnings;
use Net::Amazon::S3;
use Perl6::Say;
use Path::Class qw(dir);
use List::MoreUtils qw(any);

# config
my $bucketname            = 'Your backect name for backup';
my $aws_access_key_id     = 'Your access id';
my $aws_secret_access_key = 'Your secret access key';

my $s3 = Net::Amazon::S3->new(
    {   aws_access_key_id     => $aws_access_key_id,
        aws_secret_access_key => $aws_secret_access_key,
        retry                 => 1,
    }
);
my $bucket_for_backup = create_backet_for_backup();

main();
sub main {
    my $dir = dir('photos');
    upload_photos_in_dir($dir);
    list_files_in_bucket($bucket_for_backup);
}

sub create_backet_for_backup {
    my $bucket = $s3->add_bucket( { bucket => $bucketname } )
        or die $s3->err . ": " . $s3->errstr;
    return $bucket;
}

sub upload_photos_in_dir {
    my $dir = shift;
    dir($dir)->recurse(
        callback => sub {
            my $file = shift;
            return unless -f $file;
            upload_file($file);
        }
    );
}

sub upload_file {
    my $file = shift;
    return unless ( $file->basename =‾ /¥.jpg$/ );
    my $keys_in_bucket = keys_in_bucket($bucket_for_backup);
    return if any { $file->basename eq $_->{key} } @{$keys_in_bucket};

    my $body   = $file->slurp;
    my $status = $bucket_for_backup->add_key( $file->basename, $body,
        { 'content_type' => 'image/jpeg', } );
    say "Uploaded:" . $file->basename if $status;
}

my $cache_keys;

sub keys_in_bucket {
    my $bucket = shift;
    return $cache_keys if $cache_keys;
    my $response = $bucket_for_backup->list_all
        or die $s3->err . ": " . $s3->errstr;
    $cache_keys = $response->{keys};
    $cache_keys;
}

sub list_files_in_bucket {
    my $bucket = shift;
    say '### list all files in bucket';
    my $response = $bucket->list_all
        or die $s3->err . ": " . $s3->errstr;
    foreach my $key ( @{ $response->{keys} } ) {
        my $key_name = $key->{key};
        my $key_size = $key->{size};
        say "Bucket contains key '$key_name' of size $key_size";
    }
}|

Flickrから一括ダウンロードして、Amazon S3に一括バックアップというのをするのもいいかもしれないですね。


# photosディレクトリ決めうちなので適当に変えてください。写真のあるディレクトリの場所が変わるようであれば、Getoptで写真のあるディレクトリを指定できるように変更するのがいいですね。

Livedoor picsに写真を一括アップロード

| 0 Comments | 0 TrackBacks | このエントリーをはてなブックマークに追加 このエントリーのはてなブックマーク件数

Flickrをメインに使っているのですが、Flickrだけにアップロードしておくというのも少し不安があるので、他のところにバックアップしておくのがいいかもしれない!と思いました。


そこで、ディレクトリ内の写真を一括Livedoor picsにアップロードするスクリプトを作ってみました。


写真の置いてあるディレクトリをphotosディレクトリに決めうってますが、そこらは適当に改造して使ってください。

#!/usr/bin/env perl
use strict;
use warnings;
use XML::Atom::Client;
use XML::Atom::Entry;
use XML::Atom::Content;
use Path::Class qw(dir);
use Perl6::Say;
use Data::Dumper;

# config
my $livedoor_id = 'Your Livedoor ID';
my $password    = 'Your password';

my $endpoint_url = 'http://ws.pics.livedoor.com/atom/' . $livedoor_id;
my $api          = api_client();

sub main {
    my $dir = dir('photos');
    upload_photos_in_dir($dir);
}

sub upload_photos_in_dir {
    my $dir = shift;
    dir($dir)->recurse(
        callback => sub {
            my $file = shift;
            return unless -f $file;
            upload_file($file);
        }
    );
}

sub upload_file {
    my $file  = shift;
    my $entry = create_entry($file);
    my $edit_uri   = $api->createEntry( $endpoint_url, $entry );
    say 'Uploaded file uri:' . $edit_uri if $edit_uri;
}

sub create_entry {
    my $file = shift;

    my $body    = $file->slurp;
    my $content = XML::Atom::Content->new;
    $content->body($body);
    $content->type('image/jpeg');
    my $entry = XML::Atom::Entry->new;
    $entry->title( $file->basename );
    $entry->content($content);
}

sub api_client {
    my $client = XML::Atom::Client->new;
    $client->username($livedoor_id);
    $client->password($password);
    $client;
}

main();

# picsにアップロードできるサイズを考えると、一括リサイズしてからアップロードできるようにするなんていう処理をいれてもいいかもしれないですね。

WebService::SimpleでFlickrのfavoritesの写真を一括ダウンロード

| 0 Comments | 0 TrackBacks | このエントリーをはてなブックマークに追加 このエントリーのはてなブックマーク件数

最近カメラが空前のマイブームで、お気に入りの写真をiPodに入れて持ち歩いたりして、もっと綺麗な写真に触れる機会を増やしたいと思っていました。


そんなこんなで、Flickrのfavoritesにお気に入り写真を登録していってるんですが、Flickrから写真をダウンロードするのがだるい!ということで、favoritesの写真を一括ダウンロードするスクリプトを書いてみました。

#!/usr/bin/env perl
use strict;
use warnings;
use WebService::Simple;
use Digest::MD5 qw(md5_hex);
use Path::Class qw(dir);
use Perl6::Say;
use HTTP::Async;
use HTTP::Request;

# config
my $api_key = "Your API Key";
my $user_id = 'Your nsid';

my $api = WebService::Simple->new(
    base_url => "http://api.flickr.com/services/rest/",
    param    => { api_key => $api_key, }
);

sub main {
    fetch_favorite_photos();
}

sub fetch_favorite_photos {
    my $photos = get_favorite_photo_lists();
    return unless $photos;
    my $photo_urls = collect_photo_urls($photos);

    download_photos($photo_urls);
}

sub get_favorite_photo_lists {
    my $response = $api->get(
        {   method   => "flickr.favorites.getPublicList",
            user_id  => $user_id,
            per_page => 500,
        }
    );
    my $ref = $response->parse_response;
    return unless $ref->{photos}->{total};
    return $ref->{photos}->{photo};
}

sub collect_photo_urls {
    my $photos     = shift;
    my $photo_urls = [];
    foreach my $photo_id ( keys %{$photos} ) {
        my $url = _get_photo_url($photo_id);
        push @{$photo_urls}, $url if $url;
    }
    return $photo_urls;
}

sub _get_photo_url {
    my $photo_id = shift;
    my $response = $api->get(
        {   method   => 'flickr.photos.getSizes',
            photo_id => $photo_id,
        }
    );
    my $ref = $response->parse_response;
    my $url;
    foreach my $size ( @{ $ref->{sizes}->{size} } ) {
        if ( $size->{label} eq 'Original' || $size->{label} eq 'Large' ) {
            $url = $size->{source};
        }
    }
    say "Collecting Photo urls: " . $url if $url;
    $url;
}

sub download_photos {
    my $photo_urls = shift;
    my $ua         = LWP::UserAgent->new;
    my $dir        = setup_download_dir();
    say "### Downloading photos ###";
    foreach my $url ( @{$photo_urls} ) {
        my $file = $dir->file( md5_hex($url) . ".jpg" );
        my $res = $ua->mirror( $url, $file->absolute );
        say "Download status : " . $res->status_line . " - $url";
    }
}

sub setup_download_dir {
    my $dir = dir("photos");
    $dir->mkpath;
    $dir;
}

main();

頻繁にfavoritesを登録する人は、HTTP::Asyncなどを使ってダウンロード部分を並列化したりしてみるといいかもしれないですね。

実用! Perlで縦長連続画像を1フレームずつバラして鑑賞

| 0 Comments | 0 TrackBacks | このエントリーをはてなブックマークに追加 このエントリーのはてなブックマーク件数

(このエントリのオリジナルは、M.C.P.C.: 縦長連続画像を分割してムフフです)

縦長に連続したコマが連なっている画像、どうやって見ていますか? 最近のグラフィックビューアだと、画像サイズに合わせて拡大縮小してくれたりするのですけれども、そもそも縦長に過ぎる画像だと、拡大率を、長辺に合わせたら画像がよく見えないし、かといって短辺に合わせたら、全部見るのにスクロールして見なくてはなりません。

だから、そんな画像、Perlと言う名の青龍刀で切り刻んでしまいましょう!

モジュールの準備

今回使うモジュールは、Image::Magickというものですのが、一昔前まではインストールにちょっとコツが必要なモジュールだったと思います。また、使っているOSやPerlによっても入れ方が微妙に違うので説明が面倒ですから、各自調査してください。

WindowsのActivePerl 5.8.xを使っていて、スタートフォルダのプログラムグループに、Perl Package Managerというプログラムが表示されている場合は、こちらにインストールの仕方を掲載してあります。

スクリプト

Image::Magickの使い方は、

:: Cepheid :: - ImageMagickの使用例に詳しいです。

Filename: split_image.pl

#!/usr/bin/perl
# Filename: split_image.pl
# 400x300の画像に切り出します
 
use strict;
use warnings;
use Image::Magick;
use File::Basename;
 
our $frame_width  = 400;
our $frame_height = 300;
 
# main 
 
foreach ( @ARGV ){
  my $filename = $_;
  print "$filename\n";
  if ( -e $filename ) {
    cut_img( $filename );
  }
}
exit;
 
 
sub cut_img {
  my $filename = shift;
  our ( $frame_width, $frame_height );
  my $img_orig = Image::Magick->new;
  $img_orig->Read( $filename );
  # via http://www.ss.iij4u.or.jp/~somali/web/im_example/get.html
  my ($width, $height) = $img_orig->Get('width', 'height');
  my $frame = $height / $frame_height;
  
  if ( $frame > 1 ) {
    for ( my $i = 0; $i < $frame; $i++ ) {
      my $img = $img_orig->Clone();
      # via http://www.ss.iij4u.or.jp/~somali/web/im_example/crop.html
      $img->Crop( width  => $frame_width,
                     height => $frame_height,
                     x => 0,
                     y => $frame_height * $i,
            );
      $img->write( make_filename( $filename, $i ) );
      undef $img;
    }
  }
  
  undef $img_orig;
  return;
}
 
 
sub make_filename {
  my $path = shift;
  my $num  = shift;
  my($filename, $directories, $suffix)
    = fileparse($path, qr/\.[^.]*/ );
  return "$directories${filename}_$num$suffix";
}
 
 
__END__
おもしろくない解説は、[続きを読む]以降に。


実行例

実行前に警告! ファイル上書きされても関知しません!

懐かしの外人4コマでやってみました。

使用前

4koma-01.jpg

コンソール

D:\4koma>split_image.pl 01_0001_2.jpg
01_0001_2.jpg
 
D:\4koma>
使用後

4koma-02.jpg

というわけで、今回は、縦長連続画像を分割するという、インターネットに長くいる人ほどなぜか有用性が分かるというスクリプトを作ってみました。

これ何に役立つの、ていう人もいるかもしれませんけれども、こういう地味ーなのって、Webのバックヤードでこっそり役立っていたりするんですよね。

URLを触る時は、URI(モジュール名)以外も知っておくと吉

| 0 Comments | 0 TrackBacks | このエントリーをはてなブックマークに追加 このエントリーのはてなブックマーク件数

こんばんは!


URLを触る時は、URI(perlモジュール名)が超便利だと思う。


という話を書きましたが、実は他のモジュールをもう少し知っておくと、さらにハッピーになります。


おっと、それを説明する前に

 my $uri = URI->new('http://perl-mongers.org');
 print $uri->as_string . "\n";
 print "$uri\n";

上記の出力はどちらも、「http://perl-mongers.org」となり、同じ結果です。

これを話しておかないと、めんどうに思う方がいたりすると、Dan the Perl Monger さんにコメントで教えてもらいました。


また、タイトルにURIと、あまり深く考えず書いてたのを、C_L さんに指摘してもらいました。


ありがとうございます。 

URI::QueryParamを活用しよう!

query_form() メソッドは便利なのですが、困るときがあります。例をあげてみましょう。

use strict;
use warnings;
use URI;

use Data::Dumper;

my $uri = URI->new('http://perl-mongers.org');

$uri->query_form( { hoge => 'hoge', foo => [ 1 , 2 ] } ) ;

my %params = $uri->query_form;
warn Dumper \%params;

my @params = $uri->query_form;
warn Dumper \@params;

結果

$VAR1 = {
          'foo' => '2',
          'hoge' => 'hoge'
        };
$VAR1 = [
          'foo',
          '1',
          'foo',
          '2',
          'hoge',
          'hoge'
        ];

そうですね。 query_form の戻り値は、配列なので、fooというパラメータを二つ渡した際に、場合によっては扱いにくくなってしまいます。下記のような、形式でデータを手に入れたいケースがありますよね?

$VAR1 = {
          'foo' => [
                     '1',
                     '2'
                   ],
          'hoge' => 'hoge'
        };

そこで、URI::QueryParamですね。このモジュールを use するといくつかのモジュールが使えるようになります。下記のコードを実行すると、上記の欲しい形で結果を手に入れることができます。

use strict;
use warnings;
use URI;
use URI::QueryParam;

use Data::Dumper;

my $uri = URI->new('http://perl-mongers.org');

$uri->query_form( { hoge => 'hoge', foo => [ 1 , 2 ] } ) ;

my $params = $uri->query_form_hash;
warn Dumper $params;

便利ですね!

URI::Escape

私達は、日本人または、日本語を扱う方々のはずなので、URI::Escapeは、必ずお世話になると思います。


具体的に何をするかというと、URLにマルチバイトや記号などのurlに使うことができない文字を、安全な文字に変換してくれます。

my $a = URI::Escape::uri_escape('日本語') ;
my $b = URI::Escape::uri_escape_utf8(  Encode::decode( 'utf-8' , '日本語' ) ) ;
print "$a\n" ;
print "$b\n" ;
print URI::Escape::uri_unescape($a) ."\n";
print URI::Escape::uri_unescape($b) ."\n";

この結果は、以下のようになります。安全ですね!

%E6%97%A5%E6%9C%AC%E8%AA%9E
%E6%97%A5%E6%9C%AC%E8%AA%9E
日本語
日本語

注意として、utf8のフラグが立っている文字列の場合、専用の関数を使う必要があります。


これで、さらにURLを触るのが楽しくなりますね!


:wq!

URLを触る時は、URI(perlモジュール名)が超便利だと思う。

| 1 Comment | 0 TrackBacks | このエントリーをはてなブックマークに追加 このエントリーのはてなブックマーク件数

こんばんは!


urlにパラメータをつけたり、取り除いたり、パスだけ取得したり、クエリーを作りたいとか、そういった機会に出くわすことが多々ありませんか? そういうときは、URI というモジュールが便利です。


こんな感じで使います。

use URI;
use Data::Dumper;
use strict;
use warnings;

my $uri = URI->new('http://hogehoge.com/hoge/hoge/?hoge=hoge&foo=1&foo=2');

print $uri->as_string . "\n";
print $uri->path . "\n";
print $uri->query . "\n";
print $uri->path_query . "\n";

この表示結果は、こんな風になります。色々なパーツが取れて嬉しいですね。

http://hogehoge.com/hoge/hoge/?hoge=hoge&foo=1&foo=2
/hoge/hoge/
hoge=hoge&foo=1&foo=2
/hoge/hoge/?hoge=hoge&foo=1&foo=2

またurlを構築したいときにも使えます。

use URI;
use Data::Dumper;
use strict;
use warnings;

my $uri = URI->new('http://hogehoge.com/hoge/');

$uri->query_form( hoge => 'hoge' , foo => [1 ,2 ] );

print $uri->as_string . "\n";

結果は、以下のようになりますね。

http://hogehoge.com/hoge/?hoge=hoge&foo=1&foo=2

便利ですね! URL を構築する時には、ぜひ使ってみて下さいね!


:wq!

perl - use warnings; # -w でなくて

| 0 Comments | 0 TrackBacks | このエントリーをはてなブックマークに追加 このエントリーのはてなブックマーク件数

Original: http://blog.livedoor.jp/dankogai/archives/51068305.html

-w って何? warnings って何?

まず、以下のプログラムを見てみよう。

#!/usr/local/bin/perl
use strict;

sub distance {
    my ( $a, $b ) = @_;
    return sqrt( $a**2 + $b**2 );
}

print distance(@ARGV), "\n";
% perl scratch.pl 3 4
5

use strict;しているし、そして期待どおりに動いている。


しかし、以下の場合は期待どおりだろうか。

% perl scratch.pl 3
3

distance()は引数を二つ要求するのに、一つしか入れなくても動いてしまう。これは$bがundefとなり、数値コンテキストでは0として解釈されるため当然なのだけど、あなたは本当は$bを初期化しておきかったとする。どうすればよいか。


こういう時に活躍するのが、-wスイッチであり、use warnings;である。


まずは-wを見てみよう。

% perl -w d0.pl 3
Use of uninitialized value $b in exponentiation (**) at warnings.pl line 6.
3

確かに"Use of uninitialized value $b"という警告が出た。


次にuse warnings;を見てみよう。

% cat scratch.pl 
#!/usr/local/bin/perl
use strict;
use warnings;

sub distance {
    my ( $a, $b ) = @_;
    return sqrt( $a**2 + $b**2 );
}

print distance(@ARGV), "\n";
% perl scratch.pl 3
Use of uninitialized value $b in exponentiation (**) at warnings.pl line 7.
3

こちらもきちんと警告を出している。

-w だと何がいけないの?

それでは、なぜuse warnings;を使うべきで、-wでないのか。-wの方がuse warnings;より短いではないか。


理由は二つある。

-wは.plには有効でも.pmには有効ではない

これが一番の理由である。-wはあくまでも実行時のスイッチなので、スクリプト、すなわち実行ファイルで指定しないと有効にならない。


Distance.pm

package Distance;
use strict;

use base 'Exporter';
our @EXPORT = qw/distance/;

sub distance {
    my ( $a, $b ) = @_;
    return sqrt( $a**2 + $b**2 );
}

1;

scratch.pl

#!/usr/local/bin/perl
use strict;
use Distance;
print distance(@ARGV), "\n";

と別れている場合、-wだと.plの方で指定しないと有効にならないのだ。ところが、use warnings;であれば.pmの方で指定してもきちんと警告してくれるのだ。

no warnings 'whatever';できない

use warnings;ということは、no warnings;も存在するということである。実際これは存在し、かつレキシカルなので、以下のように局所的に警告を止めることも出来る。

sub distance {
    no warnings 'uninitialized';
    my ( $a, $b ) = @_;
    return sqrt( $a**2 + $b**2 );
}
  • wでもこれに相当することは不可能ではないが、
sub distance {
    local $^W = undef;
    my ( $a, $b ) = @_;
    return sqrt( $a**2 + $b**2 );
}

という極めて醜いものである。

まとめ

というわけで、今後スクリプトを書く時には、

use strict;
use warnings;

をお忘れなきよう。


モジュールを書くのであれば、

package MyModule;
use strict;
use warnings;

としていただきたい。ちなみにh2xsやModule::Starterを使ってモジュールを初期生成した場合、この二行は必ず入っている。あと、Mooseにはこの二行と同等の効果も含まれている。


use warnings; # and be safe!


Dan the Perl Monger

実用! Perlで少しでもSBMのブックマーク数を多く見せる

| 0 Comments | 0 TrackBacks | このエントリーをはてなブックマークに追加 このエントリーのはてなブックマーク件数

ソーシャルブックマーク(SBM)が結構使われるようになっていますが、自分のblogのブックマーク数、気にしてますか? 僕がいる業界の人はあんまり気にしてないみたいで、僕のblogにブックマーク数表示がつきまくっているのがなんだか恥ずかしいです。

そんな中、Perlを使い、ブックマーク数を少しでも多く見せる技を編み出すことに成功しました!

インチキじゃないよ!

まずはブックマーク数のおさらい

ブックマーク数というのは、SBMサービス業者が提供している特定の手続きの通りすることで、ブックマーク数が得られたり、ブックマーク数を表す画像が得られたりするものです。

よくblogに付けられているのは、はてなブックマークブックマーク数API によるものが多いです。これは、はてなブックマークというSBMサービス上でのブックマーク数が表示されます。

また、livedoor クリップでも、クリップ数を画像で取得する API - livedoor クリップ まとめサイト - livedoor Wiki(ウィキ)にて、同様に画像でクリップ数(ブックマーク数と同義)が得られます。

というわけで、このサイトのtopをこれらを使ってブックマーク数を取得すると、

<img src="http://b.hatena.ne.jp/entry/image/http://perl-mongers.org/" />
<img src="http://image.clip.livedoor.com/counter/http://perl-mongers.org/" />
と書いて、

こうなります。

......画像並べてたらあんま意味ないので、こいつらを合計してしまいましょう。

SBMのAPIからブックマーク数を数値で取得

画像でブックマーク数をとってもしょうがないので、コンピュータで計算できる数値でブックマーク数を取得します。

はてなブックマーク:はてなブックマーク件数取得APIとは - はてなダイアリー
livedoorクリップ:クリップ件数取得 API - livedoor クリップ まとめサイト - livedoor Wiki(ウィキ)

上記から引用すると、

はてなブックマーク

#!/usr/local/bin/perl
use strict;
use warnings;
use XMLRPC::Lite;
 
our $EndPoint = 'http://b.hatena.ne.jp/xmlrpc';
 
my @urls = (
    'http://d.hatena.ne.jp/',
    'http://b.hatena.ne.jp/',
    'http://www.hatena.ne.jp/',
);
 
my $map = XMLRPC::Lite
    ->proxy($EndPoint)
    ->call('bookmark.getCount', @urls)
    ->result;
 
printf("%d\t%s\n", $map->{$_}, $_) for @urls;
livedoorクリップ
#!/usr/local/bin/perl
use strict;
use warnings;
use XMLRPC::Lite;
 
my @urls = qw(
    http://clip.livedoor.com/
    http://b.hatena.ne.jp/
    http://del.icio.us/
);
 
my $proxy = 'http://rpc.clip.livedoor.com/count';
 
my $result = XMLRPC::Lite->proxy($proxy)
    ->call( 'clip.getCount', @urls )
    ->result;
 
printf( "%d\t%s\n", $result->{$_}, $_ ) for keys %$result;
そっくりすぎるので、ルーチン共用できそうですねー(ちなみに、PingKingのAPIもこれと似ていますので共用できるのでした)。

今回は、こんなふうにしてみました。

Filename: hatena_livedoor.pl

use strict;
use warnings;
use XMLRPC::Lite;
 
our $sbms = { 
       hatena =>
       {
         proxy   => 'http://b.hatena.ne.jp/xmlrpc',
         method  => 'bookmark.getCount',
       },
       livedoor =>
       {
         proxy   => 'http://rpc.clip.livedoor.com/count',
         method  => 'clip.getCount',
       },
};
 
my $url = 'http://perl-mongers.org/';
print get_sbm( 'hatena'  , $url )
    + get_sbm( 'livedoor', $url );
print "\n";
 
exit;
 
 
sub get_sbm {
  my $service = shift;
  my $url     = shift;
  return get_sbm_xmlrpc( $service, $url )
}
 
# XMLRPCによるブックマーク件数取得(livedoor,hatena)
sub get_sbm_xmlrpc {
  our $sbms;
  my $service = shift;
  my $url     = shift;
  my $result  = eval {
    XMLRPC::Lite->proxy(
                  $sbms->{$service}->{proxy},
                  timeout  => 10,
    )->call( $sbms->{$service}->{method}, $url )
     ->result
  };
  return $result->{ $url } || 0;
}
実行例:
$ perl hatena_livedoor.pl 
145
$

ついでにdel.icio.usも

del.icio.usのブックマーク数取得もくっつけて、

Filename: hatena_livedoor_delicious.pl

use strict;
use warnings;
use XMLRPC::Lite;
use JSON::Syck;
use LWP::Simple;
 
our $sbms = { 
       hatena =>
       {
         proxy   => 'http://b.hatena.ne.jp/xmlrpc',
         method  => 'bookmark.getCount',
       },
       livedoor =>
       {
         proxy   => 'http://rpc.clip.livedoor.com/count',
         method  => 'clip.getCount',
       },
       delicious =>
       {
         proxy   => 'http://badges.del.icio.us/feeds/json/url/data?url=',
         entry   => 'http://del.icio.us/url/',
       },
};
 
my $url = 'http://perl-mongers.org/';
print get_sbm( 'hatena'   , $url )
    + get_sbm( 'livedoor' , $url )
    + get_sbm( 'delicious', $url );
print "\n";
 
exit;
 
 
# SBMサービスからブックマーク件数取得
sub get_sbm {
  my $service = shift;
  my $url     = shift;
  if ( $service eq 'delicious' ) {
    return get_sbm_delicious( $url );
  }
  else {
    return get_sbm_xmlrpc( $service, $url );
  }
}
 
# XMLRPCによるブックマーク件数取得(livedoor,hatena)
sub get_sbm_xmlrpc {
  our $sbms;
  my $service = shift;
  my $url     = shift;
  my $result  = eval {
    XMLRPC::Lite->proxy(
                  $sbms->{$service}->{proxy},
                  timeout  => 10,
    )->call( $sbms->{$service}->{method}, $url )
     ->result
  };
  return $result->{ $url } || 0;
}
# del.icio.usブックマーク件数
sub get_sbm_delicious {
  my $url = shift;
  my $data = JSON::Syck::Load(
               get( $sbms->{delicious}->{proxy}.$url )
             );
  return $data->[0]->{total_posts} || 0;
}
実行例:
$ perl hatena_livedoor_delicious.pl 
173
$
となります。

JavaScriptとして貼り込めるようにする

いままではコマンドラインで実行する用だったので、これをblogに貼れるように、JavaScriptをはき出すようにします。

今回は、document.writeをはき出すようにしました。AJAXにしないのは、業界的な要請でいまだにMac IE5が多いものですから、ついついそれに合わせてしまったのです。

あと、ローカルにキャッシュを持たせたり、ブラウザキャッシュを効かせたりにしたりなどしまして、ソースがでかくなったので、[続きを読む]以降に置きました。

使うときには、<script src="http://labo.dtpwiki.jp/sbm/sbm.cgi?url=http://perl-mongers.org/"></script>とかします。このblogは本文中、script要素使用不可なため、使用例は僕のblogに来てみてね。イメージ的には、 みたいになります。

超重要! 複数SBMに重複ブックマークしている人にブックマークしてもらう

あとは、

はてなブックマーク - [観] 複数のソーシャルブックマークサイトに重複投稿されているコメントがうざい件

のコメントしている人の中で複数SBMサービスを利用していそうな人にブックマークしてもらうだけ。

ね、かんたんでしょ?

このエントリのオリジナルは、

M.C.P.C.: 様々なオンラインブックマークサービスのブックマーク件数を画像ではなくて数値で取得(高機能版)です。

フォームを使ってデータをPOSTしてみよう!

| 2 Comments | 0 TrackBacks | このエントリーをはてなブックマークに追加 このエントリーのはてなブックマーク件数

こんにちは!


今日はperlを使って、フォームからのポストのやり方を解説しようと思うよ!一番したにコードを張っておいたので、そのソースの解説をするよ。


作ってるのはこれ

解説

準備

最初の行は、perlのある場所をしていしていますね。もしかしたら、あなたの場合だと、#!/usr/local/bin/perl などと、他のパスにあるかもしれません。

#!/usr/bin/perl 

使うモジュールを読み込みます。strict, warnings はエラーを作った場合に教えてくれるから、書いています。 CGI は、フォーム関連の作業をするときに役にたつモジュールです。

use strict;
use warnings;
use CGI;

HTTPヘッダーしょり

httpへの出力を、$cgi->header() という関数で作ることができるよ。

文字コードなどの指定もできて、今回は utf-8を指定しています。

my $cgi = CGI->new();
print $cgi->header( -charset => 'utf-8' );

きも

nameという値が取れればという処理をしています。取れるということは、フォームからデータをPOSTしたってことですね。


それで、取れた場合、プリントアウトしています。htmlのタグとかを、フォームに入れることができてしまうと、タグが壊れたりするので、escapeHTMLというモジュールで、タグをエスケープするようにしています。

if ( $cgi->param('name') ) {
    my $name = $cgi->param('name');
    $name = $cgi->escapeHTML($name);
    printf( 'あなたのナマエは<b>%s</b>ですね', $name );
}

これは、最初のフォームを表示してる部分です。<DATA> をprintすると、

__END__以下に書いた部分がそこに入ります。便利ですね。

else { 
    print <DATA>;
}


__END__
<html>
<head>
<title>POST Sample</title>
</head>
<body>
    <h1>ポストしようぜ!</h1>
    <form method="post">
        ナマエ : <input type="text" name ="name"><br>
        <input type="submit" value="ポストするぜ">
    </form>
</body>
</html>

まとめ

簡単に違うページに、データを送ることができましたね。実際、複雑なデータを次のページになげたりしてるのも、ただ数が多かったりするだけで、同じような仕組みでできています。


perlで動きのある、webサイトを作るのは結構簡単だったりします。

プログラミングは、難しいと思ってるあなた、以外と簡単なので遊んでみてはどうでしょうか?

全コード

#!/usr/bin/perl 

use strict;
use warnings;
use CGI;

my $cgi = CGI->new();
print $cgi->header( -charset => 'utf-8' );
if ( $cgi->param('name') ) {
    my $name = $cgi->param('name');
    $name = $cgi->escapeHTML($name);
    printf( 'あなたのナマエは<b>%s</b>ですね', $name );
}
else { 
    print <DATA>;
}

__END__
<html>
<head>
<title>POST Sample</title>
</head>
<body>
    <h1>ポストしようぜ!</h1>
    <form method="post">
        ナマエ : <input type="text" name ="name"><br>
        <input type="submit" value="ポストするぜ">
    </form>
</body>
</html>

CPAN::Mini と glastree をつかって CPAN のデイリースナップショットを作ろう!

| 0 Comments | 0 TrackBacks | このエントリーをはてなブックマークに追加 このエントリーのはてなブックマーク件数

CPAN::Mini って便利ですね。

CPAN::Mini って何をするモジュールかというと、CPAN に登録されているモジュールの最新版をまるごと取ってくる便利なモジュールです。(CPAN::Mini について詳しくは、富田さんの use CPAN::Mini; - 今日のCPANモジュール をごらんください)

CPAN のデイリースナップショットがほしい

日頃使っている CPAN モジュール群に大きな変更が入るとしたら、確実に動くことが分かっている現時点でのアーカイブがほしいです。欲しいと思ったときにミラーを取るのでもいいのですが、できれば、デイリースナップショットを自動的に生成したいですね。Debian Linux の場合は、snapshot.debian.net という神サイトがあります。今回は CPAN の配布物で同じようなものを作りたいと思います。

デイリースナップショットを作るソフトウェア

こういうデイリースナップショットを作る場合、まず思い浮かぶのが ruby で書かれた pdumpfs というバックアップソフトではないでしょうか。pdumpfs は、まず、初回動作時に丸ごとコピーを取ります。次回からは増加分のみをコピーし、変更のない部分はハードリンクをはるという素晴らしいソフトウェアです。snapshot.debian.net では pdumpfs を使っているようですね。

今回は、pdumpfs と同趣旨で、perl でかかれたバックアップソフト glastree (配布先) を少し hack してデイリーアーカイブを作ることにします。

glastree を hack

基本的に、minicpan で取ってきたファイルをさらにコピーしてバックアップする必要ないと思います。というのも、毎日のスナップショットに /archive/200806/09/CPAN/ などというディレクトリでアクセスできることが今回の目的だからです。この目的に合うように glastree の初回動作を少し変更したいと思います。

glastree は、初回動作時にコピー動作をしていますが、これをハードリンク動作に変更します。

glastree の変更点は以下のようになります。ハードリンクを作ってみて、だめならコピーという動作にします。

--- glastree.orig       2003-01-27 09:26:26.000000000 +0900
+++ glastree    2008-06-09 00:54:56.980871566 +0900
@@ -193,7 +193,10 @@
 {
     my ($frompath, $topath, $stat) = @_;

-    copy  ($frompath, $topath);
+    my $res = link ($frompath, $topath);
+    unless ($res) {
+        copy ($frompath, $topath);
+    }
     chown ($stat->uid, $stat->gid, $topath) if $EUID == 0;
     chmod ($stat->mode, $topath);
     utime ($stat->mtime, $stat->mtime, $topath);

cron に登録しての運用

glastree にパッチを当てたら、以下のようにスクリプトを書いておいて、一日一回実行されるように cron に登録しておきましょう。

cd ~/cpanmirror
minicpan -r http://your/favorite/mirror/of/CPAN/ -l CPAN
mkdir -p archive
glastree CPAN archive

これで、~/cpanmirror/archive/200806/09/CPAN/ などの日付ディレクトリで過去の CPAN にアクセスできるようになります。

会社の LAN 内サーバなどで apache から見える場所に置いておくといいかもしれませんね!

ディスク消費量

初回実行分で 850MB 消費していました。

次回実行時からは、日々追加されるモジュールの分程度の消費なので、ディスクが CPAN のミラーでいっぱいであたまを抱えるようなこともなくていいですね!


デイリースナップショットを作っておけば、備えあれば憂いなしですね!

レンタルサーバーでhello world!

| 0 Comments | 0 TrackBacks | このエントリーをはてなブックマークに追加 このエントリーのはてなブックマーク件数

こんにちわ、まめこです。
yusukebe先生に「cgiでflickr検索」という宿題を貰ってcgiに挑戦しています!

自分はレンタルサーバーなので、いろいろと設定に苦労しました。
まだflickr検索は出来ていないのですが、helloworldだけ表示することに成功!

ソースはこんな感じです。

#!/usr/local/bin/perl

print "Content-type: text/html\n\n";
print "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\"><HTML><HEAD><TITL        E>hello world!</TITLE></HEAD><BODY>\n";
print "hello world!\n";
print "</BODY></HTML>\n";

exit;


print文でヘッダ情報を表示します。この行です。

print "Content-type: text/html\n\n";


調べてみると

この行では、CGI から出力されるものをブラウザにHTMLページですよ と指示しています。http://www.aimix.jp/cgi/syohohtmlv.html


ということらしいです。

後はhtmlを頭からprintしていくだけでした。結構簡単。
毎回print文でこんな長文打つのはめんどくさいよー。という場合はCGI.pmを使うと楽できるようです。

#!/usr/local/bin/perl

use strict;
use CGI;

my $q = new CGI;
print $q->header;
print $q->start_html(-title=>'hello world');
print "Hellow World!";
print $q->end_html."\n";

exit;


これだけ! 作ってくれた人に感謝ですね。モジュールってスゴい!
あとは、ファイル名をcgiにしてレンタルサーバーに転送しました。

以下のことに気をつけるとすんなり出来るかもですよ!

1.perlのパスは合っているか
2.ヘッダ情報はちゃんと書いているか content-type=text/html
3.権限は合っているか

自分はなかなか動かなかったです><レンタルサーバーによってcgiの設定が微妙に違うので注意が必要かもです。

引き続き、yusukebe先生の宿題をやっつけなくちゃー!

実用! Perlでプリンタのメーター確認をする

| 1 Comment | 1 TrackBack | このエントリーをはてなブックマークに追加 このエントリーのはてなブックマーク件数

みなさんの会社では裏紙を使っていますか?

裏紙使うことの是非はともかく [www.itmedia.co.jp]、環境コンサルに相談すると「裏紙使わないのが許されるのは小学生までだよね」、とか言われるようですので、言われてしまったからには素直に裏紙使いましょう。

それで、裏紙を使うだけではなくて、実際に使った量を測定しないと環境活動にならないわけですけれども、プリンタに通した紙の枚数のチェック、いわゆるメーター確認をしないと、効果測定の計算ができず、会議で報告できないですよね。

というわけで、メーター確認をしてくれるPerlスクリプトです。

スクリプトの内容

最近のプリンタは、Webサーバを内蔵しているものが多く、ブラウザからアクセスして、特定のページにアクセスすると、メーター確認ができるようになっています。そこで、そのページをPerlのLWP::Simpleという一番簡単なアクセス手段を使って丸ごと引っ張ってきて、正規表現で、カウンタ数値を取得、表示します。

というわけで、これ作るには、プリンタにブラウザでアクセスし、HTML構造をやフレームの構造を解析して、正規表現を作る必要があります。HTMLソース見て自由気ままに作ってください。

今回は、ゼロックスのDocuPrint用です。プリンタのファームウェアのバージョンによって動かない可能性もあるので注意。

ソース

Filename: check_counter.pl (UTF-8です)

#!/usr/bin/perl
#DocuPrint Counter Check
use strict;
use warnings;
use LWP::Simple;
use utf8;
use Encode;
my $printers = [
                 { 
                   name => 'DocuPrint No.1',
                   ip   => '192.0.2.110',
                 },
                 { 
                   name => 'DocuPrint No.2',
                   ip   => '192.0.2.111',
                 },
               ];
 
foreach my $printer ( @$printers ) {
  print "$printer->{name}\n";
  print "$printer->{ip  }\n";
  print getDocuPrint_count($printer->{ip})."\n\n";
}
 
exit;
 
 
sub getDocuPrint_count {
  my $ip   = shift;
  my $html = decode( 'cp932', get('http://' . $ip . '/prcnt.htm') );
  my $regexp = q(var info=\['総プリントページ数',([\d,]+?)\]);
  my $count;
  if ( $html =~m|$regexp| ) {
    $count = $1;
  }
  return $count;
}
※実際のIPから例示用IPに書き換えていますので注意

実行結果

今回動作確認はWindowsのActivePerl 5.8.8で行っています。

D:\>check_counter.pl
DocuPrint No.1
192.2.0.110
66570

DocuPrint No.2
192.2.0.111
131128


D:\>
※実際の表示から例示用IPに書き換えていますので注意

ポイント

プリンタから出てくるHTML文書のcharsetがShift_JISだったので、LWP::SimpleモジュールのgetメソッドでHTML文書を読み込んだあと、キャラクターエンコーディングがCP932であると見なし、use utf8;でutf8プラグマを有効にしたPerlにおいて扱いやすい、UTF-8 flagが上がったUTF-8文字列に変換しちゃっています(decodeっていうところ)。

UTF-8 flagが上がったUTF-8文字列になってしまえば、正規表現で日本語文字列による検索がしやすくなりますので、キャラクターエンコーディングは意識した方がいいというお話でした。

補足

このプログラムをメールで送信するように改造して、cron(UNIXライクなOS)やらAT(Windows)やらで定時実行させるようにすると、いちいちプリンタまで行ってメーター確認しなくてすみますね!

但し、プリンタの電源が付いていないとダメ。これを実行するために常にプリンタの電源を付けておくなどという本末転倒なことは、くれぐれもなさらぬよう。

perl/Encode - 7bit-jis != iso-2022-jp

| 1 Comment | 0 TrackBacks | このエントリーをはてなブックマークに追加 このエントリーのはてなブックマーク件数

Original: http://blog.livedoor.jp/dankogai/archives/51061651.html


ちょちょまwww


http://perl-mongers.org/2008/06/is2022jp.html

WIDE系 IRCで使われている、IS2022JPの(いわゆる)半角カナにはいろいろ種類がありますが、これをPerlでコード変換しようとした場合、 Encodeモジュールではコード変換に失敗してしまいます。(2008年6月6日0:20追記:Encodeモジュールでは半角カナの認識に失敗するだけで他のコードは自動認識します)

それは、文字コードそのものが微妙ながら決定的に違うのです。


http://search.cpan.org/perldoc?Encode::JP

  7bit-jis    /\bjis$/i         7bit JIS
  iso-2022-jp                   ISO-2022-JP                  [RFC1468]
                = 7bit JIS with all Halfwidth Kana 
                  converted to Fullwidth

半角カナをそのまま使いたかったら、7bit-jisをiso-2022-jpの代わりに指定すればよいだけです。ちなみにjisは、7bit-jisの別名として扱われます。


Dan the Encode Maintainer

IS2022JPの半角カナ対応

| 0 Comments | 0 TrackBacks | このエントリーをはてなブックマークに追加 このエントリーのはてなブックマーク件数

WIDE系 IRCで使われている、IS2022JPの(いわゆる)半角カナにはいろいろ種類がありますが、これをPerlでコード変換しようとした場合、Encodeモジュールではコード変換に失敗してしまいます。(2008年6月6日0:20追記:Encodeモジュールでは半角カナの認識に失敗するだけで他のコードは自動認識します)(2008年6月7日0:50訂正)

筆者の間違いでした。Encodeモジュールメンテナンス各位の方へお詫びいたします。

この場合、skfを使うと解決できます。(2008年6月7日0:50訂正)

Encodeモジュールでも変換できますが、今回はskfを使った方法を紹介します。

SourceForge.JP: Project Info - skf - simple kanji filter

skfにはPerlエクステンションがあり、エクステンションを使うと解決できます。

Perlエクステンションの導入方法

上記のURLからソースコードをDLして、

>configure
>make perlext

これで、Skf.pmとSkf.soファイルができますので、

>sh ./scripts/find-perl

の結果を元にSkf.pmとSkf.soを移動します。


私の環境では、結果が /usr/local/lib/perl5/5.8.8/mach/CORE でしたので、

/usr/local/lib/perl5/5.8.8 に Skf.pm を

/usr/local/lib/ に Skf.so をおきました。

動作確認用コード

#!/usr/local/bin/perl
use Skf;
my $test = Skf::convert("--ic=CP932 --oc=UTF-8", "\x82\xA0");
print $test . "\n";

"--oc"が出力コードですので、任意のコードに変えて「あ」が出ることを確認してください。

パラメータの詳細は、ヘルプを見ましょう。


注)この記事はhttp://d.hatena.ne.jp/UnderDone/20061031/p1より筆者が転載

WWW::Mechanize::AutoPagerにWebService::Wedataから得たSITEINFOを渡しtwitterのfriends一覧を作る

| 0 Comments | 0 TrackBacks | このエントリーをはてなブックマークに追加 このエントリーのはてなブックマーク件数

タイトルが長いのは釣りですね。わかります。


Firefox/greasemonkeyにはAutoPagerizeというユーザスクリプトがあり、決まったフォーマットに従いページ送り出来る機能があります。Perlにおいてもmiyagawaさん作のWWW::Mechanize::AutoPagerというモジュールがあり、WWW::Mechanizeでページ送りしやすくなっています。

ただし最近AutoPagerizeのSITEINFOはwedataに移行しており、以前までのURLであるhttp://swdyh.infogami.com/autopagerizeを使っているWWW::Mechanize::AutoPagerのSITEINFOは若干古くなっているかと思います。


さて今日はwedataのデータを扱えるCPANモジュール、WebService::Wedataを使い、最新のSITEINFOを使ってWWW::Mechanize::AutoPagerを動かすサンプルをご紹介します。


SYNOPSIS通り

my $wedata = WebService::Wedata->new;
my $database = $wedata->get_database('AutoPagerize');
my @items = $database->get_items();

でデータベースの取得およびアイテム一覧が取得出来ます。

今日はtwitterのfriendsページをスクレイピングするのでurlにマッチしたSITEINFOを検索しなければなりません。

ここで少し引っかかったのがwedataには