実用! 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.: 様々なオンラインブックマークサービスのブックマーク件数を画像ではなくて数値で取得(高機能版)です。

【ここからは長いだけでおもしろくないソースコーナーです】

一回取得したブックマーク数をローカルにキャッシュしたり、ブラウザキャッシュと協調したり、mod_perl下でも動いたりなど、よく分からないけれども高機能版みたいです。

http://labo.dtpwiki.jp/sbm/sbm.cgi

に設置してあるよ。使うときは、<script src="http://labo.dtpwiki.jp/sbm/sbm.cgi?url=http://perl-mongers.org/"></script> みたいにして使う。

#!/usr/bin/perl
use strict;
use warnings;
use Cache::File;
use CGI;
use Digest::MD5;
use HTML::Template;
use JSON::Syck;
use LWP::Simple;
use HTTP::Date;
use utf8;
use XMLRPC::Lite;
binmode STDOUT => ':utf8';

sbm();
exit; # オワタ


sub sbm {
  # 初期設定
  our $cachedir = '/home/cl/www/labo.dtpwiki.jp/html/sbm/cache';
    # mod_perl下ではフルパス必須。
  our $sbms = { 
       hatena =>
       {
         proxy   => 'http://b.hatena.ne.jp/xmlrpc',
         entry   => 'http://b.hatena.ne.jp/entry/',
         method  => 'bookmark.getCount',
         message => 'このエントリーをはてなブックマークで'
                   .'ブックマークしているユーザ数',
       },
       livedoor =>
       {
         proxy   => 'http://rpc.clip.livedoor.com/count',
         entry   => 'http://clip.livedoor.com/page/',
         method  => 'clip.getCount',
         message => 'このエントリーを'
                   .'livedoor Clipでクリップしているユーザ数',
       },
       delicious =>
       {
         proxy   => 'http://badges.del.icio.us/feeds/json/url/data?url=',
         entry   => 'http://del.icio.us/url/',
         message => 'このエントリーを'
                   .'del.icio.usでブックマークしているユーザ数',
       },
       total =>
       {
         entry   => 'http://labo.dtpwiki.jp/sbm/sbm.cgi?url=',
         message => 'このエントリーをブックマークしている'
                   .'総ユーザ数',
       }
  };
  
  
  # 開始
  my $q      = CGI->new();
  my $url    = $q->param('url') || 'http://www.yahoo.co.jp/';
  
  # キャッシュ
  my $data   = check_cache( $url );
  my $counts = $data->{counts};
  my $expiry = $data->{expiry};
  my $ctime  = $data->{ctime};
  
  # ブラウザキャッシュと比較
  if ( $q->http('If-Modified-Since') eq time2str($ctime) ) {
    print $q->header(
       -status         => '304 Not Modified',
       'Cache-Control' => "max-age=$expiry",
    );
  }
  else { # ブラウザキャッシュきかなかった
    show_js( $q, $url, $counts, $expiry, $ctime );
  }
  return; # オワタ
  
  
  # JS出力モード
  sub show_js {
    my $q      = shift;
    my $url    = shift;
    my $counts = shift;
    my $expiry = shift;
    my $ctime  = shift;
    print $q->header(
      -type    => 'text/javascript',
      -charset => 'UTF-8',
      -expires => "+${expiry}",
      "Last-modified" => time2str($ctime),
    );
    my $tmpl_html = <<"    END_OF_JS";
      document.write( ''
        + '<img src="http://labo.dtpwiki.jp/'
        + 'sbm/images/total.gif"'
        + ' width="16" height="16" border="0"'
        + ' style="vertical-align: middle;" />'
        + '<tmpl_var name="users">'
      );
    END_OF_JS
    my $tmpl = HTML::Template->new( scalarref => \$tmpl_html );
    (my $users = sbm_html('total', $url, $counts->{total} ) )
      =~ s|[\x0a\x0d]||g;
    $users =~ s|\s\s+| |g;
    $tmpl->param( users => $users );
    print $tmpl->output();
    return;
  }
  
  
  # 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;
  }
  
  # ブックマーク User数 HTML生成
  sub sbm_html {
    my $service = shift;
    my $url     = shift;
    my $count   = shift;
    
    my $tag;
    my $users = 'users';
    $users = 'user'   if $count == 1;
    $tag   = 'em'     if $count > 2;
    $tag   = 'strong' if $count > 9;
    my $tag_s = $tag ? "<$tag>" : q();
    my $tag_e = $tag ? "</$tag>": q();
    
    if ( $service eq 'delicious' ) { # del.icio.usの場合
      my $ctx = Digest::MD5->new;
      $ctx->add( $url );
      $url = $ctx->hexdigest; # del.icio.us用MD5生成
    }
    my $tmpl_html = << '    END_OF_HTML';
          
          <tmpl_var name="tag_s">
          <a href="<tmpl_var name="entry"><tmpl_var
            name="url" escape="html">"
            title="<tmpl_var name="message">"
            rel="nofollow" target="_blank">
            <tmpl_var name="count"> <tmpl_var name="users"></a
          ><tmpl_var name="tag_e">
    END_OF_HTML
    
    my $tmpl = HTML::Template->new( scalarref => \$tmpl_html);
    $tmpl->param(
      url     => $url,
      entry   => $sbms->{$service}->{entry},
      message => $sbms->{$service}->{message},
      tag_s   => $tag_s,
      tag_e   => $tag_e,
      count   => $count,
      users   => $users,
    );  
    return $tmpl->output();
  }
  
  # キャッシュ問い合わせ
  sub check_cache {
    our $cachedir;
    my $url = shift;
    my $data;
    my $cache = Cache::File->new( 
      cache_root      => $cachedir,
      default_expires => '3600 sec',
    );
    if ( $cache->exists( $url ) ) { # キャッシュ生きてる?
      $data = $cache->thaw( $url );
      $data->{expiry}  = $cache->expiry($url) - time();
      return $data;
    }
    else {                          # キャッシュ無効?
      my $expiry = 50 + int( rand( 50 ) ).'min';
      my $counts = { # SBMサービスからブックマーク件数取得
        hatena    => get_sbm( 'hatena',   $url ),
        delicious => get_sbm( 'delicious',$url ),
        livedoor  => get_sbm( 'livedoor', $url ),
      };
      $counts->{total} += $counts->{$_} foreach keys %$counts;
                                       # ブックマーク数合計
      $data = { 
        counts => $counts,
        ctime  => time(),
        expiry => $expiry,
      };
      $cache->freeze( $url, $data, $expiry ); # キャッシュ書き込み
      return $data;
    }
  }
}
__END__

No TrackBacks

TrackBack URL: http://perl-mongers.org/MT/mt-tb.cgi/56

Leave a comment

About this Entry

This page contains a single entry by CL published on June 14, 2008 2:40 AM.

フォームを使ってデータをPOSTしてみよう! was the previous entry in this blog.

perl - use warnings; # -w でなくて is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.

Categories

Pages

Creative Commons License
This blog is licensed under a Creative Commons License.
Powered by Movable Type 4.21-en