« 2007年6月 | メイン | 2007年9月 »

2007年8月 アーカイブ

2007年8月 3日

Perlでニコニコ動画のflvとコメントxmlをダウンロードする

さんざん既出かもしれないけどPerlでニコニコ動画のflvファイルとコメントのxmlファイルをダウンロードするスクリプト。 非公式ながら扱うことのできるニコニコ動画のAPIなんだけど、いくつかポイントがある。flvファイルのパスやコメントを表すスレッドのIDなどを取得するためのURLは

http://www.nicovideo.jp/api/getflv?v=ビデオID

になっていてる。ここにアクセス(GET)して得られる値のサンプルは以下のようなもの。 主なものを解説すると、urlというのがflvのパス、thread_idがコメントを示すスレッドのID、msがそのスレッドを取得できるAPIのURL。

thread_id:1184806392
l:91
url:http://smile-clb51.nicovideo.jp/smile?v=659632.44622
link:http://www.smilevideo.jp/view/659632/991267
ms:http://msg41.nicovideo.jp/api/
user_id:991267
is_premium:0
nickname:yournickname
done:true

このAPIをたたく時、またはflvをダウンロードするときには注意がすべきことがある。 まずニコニコ動画にログインした状態のセッションを保持したクッキーをもったUserAgentでアクセスしなくてはいけないのはまず当たり前で、 さらに取得しようとしている動画のパーマリンク、つまり以下のアドレスにアクセスした状態でなくてはエラーがでてしまうのだ。

http://www.nicovideo.jp/watch/ビデオID

これがWebフォーム型のニコニコ動画ダウンロードがうまくいかない理由であり、一方、動画のページにダウンロードボタンを追加するGreasemonkeyではダウンロードが成功するゆえんでもある。 また、コメントのXMLを取得するには、上記APIで取得したmsの値であるURLに対して以下のXMLをPOSTすれば最近の500件を得ることができる。

<thread res_from="-500" version="20061206" thread="スレッドID" />

以上を踏まえて以下のflvとコメントxmlをダウンロードするスクリプトが完成。

#!/usr/bin/perl

use strict;
use LWP::UserAgent;
use HTTP::Cookies;
use URI::Escape;
use HTTP::Request;
use HTTP::Headers;

my $video_id = $ARGV[0] || "sm721154";

my $mail = "yourmailaddress";
my $password = "yourpassword";

my $ua = LWP::UserAgent->new;
$ua->cookie_jar( HTTP::Cookies->new(
                                                                        file => 'cookie.lwp',
                                                                        autosave => 1,
                                                                ));
&login();
$ua->get( "http://www.nicovideo.jp/watch/$video_id" );

my $res = $ua->get( "http://www.nicovideo.jp/api/getflv?v=$video_id" );

my $content = uri_unescape($res->content);
my %data;
my @temp = split("&",$content);
foreach my $prop (@temp){
        if($prop =~ /(.*?)=(.*)/){
                $data{$1} = $2;
        }
}

#save flv
$res = $ua->get( $data{url} );
&save($res->content, $video_id . ".flv");

#save xml
my $post_data = "<thread res_from=\"-500\" version=\"20061206\" thread=\"" . $data{thread_id} ."\" />";
my $header = HTTP::Headers->new;
$header->header('Content-Type' => 'text/xml');
my $req = HTTP::Request->new('POST', $data{ms}, $header, $post_data );
$res = $ua->request($req);
&save($res->content, $video_id . ".xml");

sub login{
        $ua->post( "http://www.nicovideo.jp/login",
                             [
                                mail => $mail,
                                password => $password,
                        ]);
}

sub save{
        my ($content, $filename) = @_;
        open FH, ">$filename";
        binmode FH;
        print FH $content;
        close FH;
}

2007年8月 6日

Danさんに勝手に添削されて学んだこと

Danさんに初めて勝手に添削された→ 404 Blog Not Found:perl - 勝手に添削 - ニコニコ動画ダウンローダー。 「勝手に」添削されるって不思議な気分。ぶっちゃけこの記事見つけたときには、「勝手に」添削されているだけあって一瞬ムっとしたw。だけど、Danさんのようなお方に直接コードを直してもらうってこと自体、ペーペーの俺にとっては嬉しいことだし、実際に直されたコードは、機能が追加されたにもかかわらず俺が書いたコード数の半分くらいになっていて読みやすくなっているのでPerlのノウハウとしても学ぶことが多かった。ってかこんだけ変わると自分の書いたコードが恥ずかしくなってくる。が、そこは、Perlのモットー「There's More Than One Way To Do It」に習って、「いろんな書き方があって面白いなー」とPerlの奥深さを楽しみつつ、自分もスマートなコードを書けるように鍛錬する。 以下、Danさんが書いたコードからいくつか学んだ点を、引用しつつ、自分でまとめる。

ユーザー名(mail)とパスワード(password)は、~/.nicovideo.ymlに移動。

これは「軽く試す」という目的のスクリプトなのでハードコンフィグでいいや、と思ってた。 コンフィグを分離するにはYAMLは便利ですね。コードもスマート。

UAでKeep Aliveを有効に。少しでも鯖をニコニコさせたいので。

keep_aliveオプションって使ったことないんだけどApacheのKeepAliveディレクティブと同様に「通常だとサイトへの接続が終了した後、続けて再度接続しようとすることがある。このオプションを使用すると接続を開いたままの状態で維持し、再接続時の遅延を最小限に抑える。」という解釈でいいのかな? 確かに相手の鯖のことも考えることは大事。

cookieは、必ずloginするので非保存に。

なるほど。無駄なファイルは作らなくて済むし。

queryの処理はCGIモジュールにおまかせ

これは驚いた。CGIモジュールってこんなことできるんだ。ソースで言うと以下の部分。

#Danさんのソース
#$res->contentは url=value&ms=value... といった文字列
my $q   = CGI->new( $res->content );

#俺のソース、一度splitで分解してから%dataというハッシュに入れてた
my %data;
my @temp = split("&",$res->content);
foreach my $prop (@temp){
        if($prop =~ /(.*?)=(.*)/){
                $data{$1} = $2;
        }
}

CGIのドキュメントみると「You can also initialize the query object from an associative array reference or from a properly formatted, URL-escaped query string ...」とか書かれてて例も出てた。こういうのを知っているか知らないかの差はでかいな。

    $query = new CGI('dinosaur=barney&color=purple');
ファイルへの保存をLWPに直接おまかせ。この方が$res->contentを使うよりメモリー節約になる。

今思うと確かになんで、いちいちファイルハンドル開いて$res->contentをprintするっていう面倒なことをしていたのか、と自己反省。ただLWPで直接保存するにはmirrorを使う方法しかしらなかった。 これもドキュメント読むと、$ua->requestの引数2つ目にファイル名が指定したり、$ua->getのcontent_fileオプションにもファイル名を指定してファイルとして保存することができる旨が書いてある。

コードの大幅な簡素化

まじ、簡素になった。これだけ機能追加した上に、コード量少なくなってかこいい。

qq{<thread res_from="-500" version="20061206" thread="$thread_id" />}

最後に、このqq/STRING/という書き方。 STRINGがダブルクォートに囲まれたという意味なんだけど、こう書けば、STRING内のダブルクォートをエスケープしなくて済んで楽。

ということでDanさん添削してくださってありがとうございます。

2007年8月 8日

Plagger::Plugin::Filter::FetchNicoVideo / ver0.01

非公式なニコニコ動画のAPIの制限を加味して、linkがニコニコ動画のビデオページ(http://www.nicovideo.jp/watch/sm000000形式)のエントリーだった場合に、動画のFLVとコメントのXMLをダウンロードできるPlaggerのPluginを作った。 制限のあるニコニコ動画向けに、Filter::FindEnclosuresとFilter::FechEnclosureをあわせたようなもの。 外部クッキーを使ってユーザー名とパスワードを書かなくてよくすることもできるが、とりあえず、必須にした。あとコメント数をオプションで設定するようにしたい。今は500件。 詳しい使い方は→ゆーすけべー日記: Plaggerでニコニコ動画のFLVとコメントを一括ダウンロード!


package Plagger::Plugin::Filter::FetchNicoVideo;
use strict;
use base qw( Plagger::Plugin );

our $VERSION = 0.01;

use URI::Escape;
use File::Spec;
use HTTP::Request;
use Plagger::Enclosure;
use Plagger::UserAgent;

sub register {
    my($self, $context) = @_;
    $context->register_hook(
        $self,
        'update.entry.fixup' => \&filter,
    );
        my $ua  = Plagger::UserAgent->new;
        $ua->cookie_jar( $self->cookie_jar );
        $self->{ua} = $ua;
        $self->login();
}

sub init{
    my $self = shift;
    $self->SUPER::init(@_);

    defined $self->conf->{mail} or Plagger->context->error("conifg 'mail' is not set.");
    defined $self->conf->{password} or Plagger->context->error("config 'password' is not set.");
    defined $self->conf->{dir} or Plagger->context->error("config 'dir' is not set.");

    if ($self->conf->{dir} =~ /^[a-zA-Z]/ && $self->conf->{dir} !~ /:/) {
        $self->conf->{dir} = File::Spec->catfile( Cwd::cwd, $self->conf->{dir} );
    }
    
    unless (-e $self->conf->{dir} && -d _) {
        Plagger->context->log(warn => $self->conf->{dir} . " does not exist. Creating");
        mkpath $self->conf->{dir};
    }
}


sub filter {
    my($self, $context, $args) = @_;
        my $ua = $self->{ua};
        my $entry = $args->{entry};

        #get video_id
        my $video_id;
        $_ = $entry->link;
        return unless (m!www.nicovideo.jp/watch/(.*)!);
        $video_id = $1;

        #get flv url
        my %prop = $self->find_property($video_id);
        my $flv_url = $prop{url};
        unless($flv_url){
                $context->log(warn => "Not Found FLV URL $prop{error}: $video_id");
                return;
        }
        $context->log(info => "Found FLV URL $flv_url");
        my $enclosure = Plagger::Enclosure->new;
        $enclosure->url( URI->new($flv_url) );
        $enclosure->media_type( "video/x-flv" );

        #set local path
        my $filename = $self->conf->{id_as_filename} ? $video_id : $entry->title;
        utf8::encode($filename);
        if($self->conf->{filename_encode}){
                Encode::from_to($filename, "utf-8", $self->conf->{filename_encode});
        }
        my $path = File::Spec->catfile($self->conf->{dir}, $filename . ".flv");

        #access video page
        $ua->get( "http://www.nicovideo.jp/watch/$video_id" );

        #download flv file
        my $req = HTTP::Request->new(GET => $enclosure->url);
        $context->log(info => "Fetching $video_id FLV File ... " );
        my $res = $ua->request($req, $path);
        $context->log(warn => "Fetch FLV Error: $video_id" ) if $res->is_error;

        #download xml file
        if($self->conf->{download_comment}){
                $path = File::Spec->catfile($self->conf->{dir}, $filename . ".xml");
                my $post_data = qq!<thread res_from="-500" version="20061206" thread="$prop{thread_id}" />"!;
                my $header = HTTP::Headers->new;
                $header->header('Content-Type' => 'text/xml');
                my $req = HTTP::Request->new('POST', $prop{ms}, $header, $post_data );
                $context->log(info => "Fetching $video_id XML File ... " );
                $res = $ua->request($req, $path);
                $context->log(warn => "Fetch XML Error: $video_id" ) if $res->is_error;
        }
        
        $enclosure->filename($filename);
        $enclosure->local_path($path); # set to be used in later plugins
        if ($res->header('Content-Length')) {
                $enclosure->length( $res->header('Content-Length') );
        }
        $entry->add_enclosure($enclosure);

}

sub login {
        my $self = shift;
        my $res = $self->{ua}->post( "http://www.nicovideo.jp/login",
                             [
                                mail => $self->conf->{mail},
                                password => $self->conf->{password},
                        ]);
        return $res->content;
}

sub find_property {
        my($self, $video_id) = @_;
        my $res = $self->{ua}->get( "http://www.nicovideo.jp/api/getflv?v=$video_id");
        return if $res->is_error;
        my $content = URI::Escape::uri_unescape($res->content);
        my %data;
        my @temp = split("&",$content);
        foreach my $prop (@temp){
                if($prop =~ /(.*?)=(.*)/){
                        $data{$1} = $2;
                }
        }
        return %data;
}


1;

__END__

=head1 NAME

Plagger::Plugin::Filter::FetchNicoVideo - Fetch flv file from NicoVideo link

=head1 SYNOPSIS

  - module: Filter::FetchNicoVideo
    config:
      mail: your@mailadddres
      password: yourpassword
      dir: /path/to/files
      download_xml: 1 #optional default is 0
      filename_encode: euc-jp #optional default is utf-8

=head1 DESCRIPTION

This plugin downloads flv file for each entry which has NicoVideo link.

=head1 CONFIG

=over 4

=item mail password

Your NicoVideo login mail address and password.

=item dir

Directory to store downloaded enclosures. Required.

=item download_xml

IF set, download comment xml file. Optional. Default is off.

=item filename_encode

File name encode. Example: euc-jp / shift-jis. Optional. Default is utf-8.

=item id_as_filename

IF set, set video id as flv file. Default file name is entry title. Optional.

=back

=head1 AUTHOR

Yusuke Wada

=head1 SEE ALSO

L<Plagger>, L<http://www.nicovideo.jp/>

=cut


2007年8月10日

Catalystのモデルを外部スクリプトから操作する(Loaderが0.03007の場合)

Catalystのモデルのモデルを外部スクリプトからいじる方法。 最新のDBIx::Class::Schema::Loader(0.03007)の場合。 そして、libディレクトリと同じ階層のディレクトリ内にスクリプトがある時。

#!/usr/bin/perl

use strict;
use warnings;
use FindBin;

use lib "$FindBin::Bin/../lib";

{
    package MyApp::Model::DBIC;
    use base qw/DBIx::Class::Schema::Loader/;

        __PACKAGE__->connection('dbi:SQLite:myapp.db');
        __PACKAGE__->loader_options();
}

#これで以下のようにDBICのモデルが使える
my $rs = MyApp::Mode::DBIC->resultset("Hoge")->seach;

RSS2.0の雛形

自分用メモ

<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title></title>
    <link></link>
    <description></description>
    <language>ja</language>
    <copyright></copyright>
    <lastBuildDate></lastBuildDate>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs> 
    <item>
      <title></title>
      <description><![CDATA[
    ]]></description>
      <link></link>
      <guid></guid>
      <category></category>
      <pubDate></pubDate>
    </item>
  </channel>
</rss>

sixapart-standard テンプレートの雛形

自分用メモ。 MTに使われていたテンプレートで変数部分を抜き取った雛形。 Plagger::Publish::Planetのassetsを改変。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <link rel="alternate" type="application/rss+xml" title="" href="" />
  <link rel="stylesheet" type="text/css" href="" />
  <title></title>
</head>
<body class="layout-two-column-right">
  <div id="container">
    <div id="container-inner" class="pkg">
      <div id="banner">
        <div id="banner-inner" class="pkg">
          <h1 id="banner-header"></h1>
          <h2 id="banner-description"></h2>
        </div>
      </div>
      <div id="pagebody">
        <div id="pagebody-inner" class="pkg">
          <div id="alpha">
            <div id="alpha-inner" class="pkg">

          <h2 class="date-header"></h2>
              <div class="entry">
                <h3 class="entry-header">
                <a href=""></a>
                </h3>
                <div class="entry-content">
                  <div class="entry-body">
            
            <p class="entry-footer">
              <span class="post-footers">Posted by 
            at 
              </span>
              <span class="separator">|</span>
              <a class="permalink" href="">Permalink</a>
            </p>
                  </div>
                </div>
          </div>
          <!-- end of entry -->
          
            </div>
          </div>
      <!-- end of alpha -->
      
          <div id="beta">
            <div id="beta-inner" class="pkg">
              <div class="module">
                <h2 class="module-header"></h2>
                <div class="module-content">
                  <ul class="module-list">
                    <li class="module-list-item">
                    </li>
                  </ul>
                </div>
              </div>
              <div class="module-powered module">
                <div class="module-content">
                </div>
              </div>
            </div>
          </div>
      <!-- end of beta -->
      
        </div>
      </div>
    </div>
  </body>
</html>

2007年8月11日

TTのホームページが

今までださくてもったりしてたTemplate Toolkitのホームページが かっこよくなって軽快になってるー

2007年8月18日

SOAP::Liteで日本語がhexになったよ

まっさらなdebianにcpanでSOAP::Lite入れたら、SOAPでやり取りされる日本語が

&#x0000

みたいなhex型になっちゃってびびったが、どうもparserがよくないようで、 aptのライブラリを入れたら、他にもいろいろインストールしてくれて直った。

# apt-get isntall libsoap-lite-perl

まっさらなdebianにCatalystを入れる

まっさらなdebian(etch)にCatalystを入れたときのメモ。 抜けてるところもあるかもしれないし、必要ないところもあるかもしれない。

まず、ビルドに必要最小限っぽいのをaptで入れる。

# apt-get install gcc make

とりあえずcpanシェルで「まるごとPerl」にかかれてたようにしてみる。

cpan> install Module::Find Path::Class
cpan> install Task::Catalyst

途中HTML::TokeParserのところで止まるのでaptで入れる。 ついでにlibwwwも入れる。

# apt-get install libwww-perl libhtml-tokeparser-simple-perl

次に発生したのはDevel::Callerが入らない問題。「Module::Build is not configured with C_support」というエラーログで検索してExtUtils::ParseXSを入れればOKなことが判明。→Module::Build is not configured with C_support の対処方法 :: Drk7jp

なんとなくうまくいった風な感じだったので、 catalyst.pl testと打ってみると、

Weak references are not implemented in the version of perl

って怒られる。これもGoogle先生に助けられた。Scalar::Util::weakenが原因らしく再インストールすればOK。 →unknownplace.org - 2006/06/26 - Weak references are not implemented in the version of perl

うんで見事動いた。 なんかはしょってる感じがするけど、ようはTask::Catalystを実行して出てきたエラーをつぶせばよいよね。

まるごとPerl! Vol.1
posted with yusukebe.com::AmazonSearch on 2007.8.18
  • 小飼 弾 宮川 達彦 伊藤 直也 川合 孝典 水野 貴明
  • 大型本 / インプレスコミュニケーションズ (2006/08/24)
  • Amazon 売り上げランキング: 28222
  • Amazon おすすめ度の平均: 5.0
    • 5 技術書・解説書というよりはマイルストーン
Amazon.co.jpで詳細を見る

2007年8月19日

LDRはRSSのContent-Typeがtext/plainだったりすると読んでくれない

くえ☆すたのRSSのContent-Typeの出力をしていないのが原因で、LDRに登録しようとしたら、

登録可能なフィードを発見できませんでした。

なんていわれてできなかった。Content-Typeがapplication/rss+xmlとかじゃないと読んでくれないみたいね。 というわけでコントローラーに以下を追加。これで登録ができるようになりました。 参照:Catalyst Advent Calendar - Day 16

$c->res->content_type('application/rss+xml');

Apache2.0(mod_proxyでリバースプロキシ)+Apache1.3(mod_perl)環境に移行した

今までsuexecで動くスクリプトもmod_perlアプリも全てApache1.3使ってサーブしてたんだけど、 さすがにクローラーとかが一度に来たときなど、mod_perlでモジュールを読み込んだ分重たい子プロセスをたくさん走らせるのはなんだな、というわけでApache2.0をインストしてリバースプロキシ環境を導入したみた。 基本的にはTechknow Movable Type: Apache 2.0 + mod_proxy によるリバース・プロキシの構築を参考に、ってか必要なことはだいたいこれに書いてあって素晴らしいね。 今回構築した構成はこんな具合。

Apache2.0 (with mod_proxy)
フロントエンド。80番ポート。静的コンテンツやユーザー毎のsuexecで動かさなくてはいけないようなスクリプトを返す。 またリクエストされたURLによってはバックエンドのサーバーに飛ばす
Apache1.3 (with mod_perl)
バックエンド。8080番ポート。mod_perlのアプリケーションを動かす。 俺の場合、今のところ全てCatalystで作ったもの。

個人的な方針としてdebianのaptで入れれるものは全部それで入れる、というポリシーでやっているので、今回もaptでApache2.0を導入。設定も含め、とりわけ目だってはまったところは無いけど、軽く気になった点をメモっておく。

  • 当初apache2-mpm-workerを選択したが、aptでphpモジュールを入れようとするとworkerが削除され、 代わりにapache2-mpm-preforkがインストールされる
  • --suexec-docrootは/var/wwwになっているので、ソースを持ってきて修正を加えた後、debuildする
  • Apache1.3と2.0のconfig設定の主な変更点はVirtualHost中のUserとGroup がSuexecUserGroupになったこと
  • Apache2のVirtualHostディレクティブではCustomLogが2つ指定してあるとサーバー起動時にfailする

というわけで、移行が完了して今のところ不具合が見られずに、いい感じ。 abでmod_perl関係なしにcgiスクリプトの処理速度を測定したらApache2の方が速かったんだけど、これって進化しているってことなのかな?どちらにしろ、気分的には速く感じるよ。

2007年8月20日

「これはすごい」「これはひどい」のデータ構造をPlaggerのconfigだけで作る

タイトルの通り

global:
  timezone: Asia/Tokyo
    
plugins:
  
  - module: Subscription::XPath
    config:
      url: http://b.hatena.ne.jp/t/%E3%81%93%E3%82%8C%E3%81%AF%E3%81%99%E3%81%94%E3%81%84
      xpath: //a[img[@src="/images/page.gif"]]
      
  - module: Filter::Rule
    rule:
        #add created_date
        require DateTime::Format::W3CDTF;
        my $dt = Plagger::Date->parse(DateTime::Format::W3CDTF->new,$args->{entry}->date);
        $dt->set_time_zone('Asia/Tokyo');
        if( !($args->{feed}->meta->{created_date}) ||
        $args->{feed}->meta->{created_date} > $dt->epoch ){
        $args->{feed}->meta->{created_date} = $dt->epoch; }
        #add tag_count
        foreach my $tag (@{$args->{entry}->tags}){ 
        if($tag=~m/(?:(これはすごい)/){
        $args->{feed}->meta->{tag_count} ++;
        }}
        #add comment_count
        $args->{feed}->meta->{comment_count} ++ if $args->{entry}->body;
        #add feed tag
        map { $args->{feed}->add_tag($_); } @{$args->{entry}->tags};
        #add title
        return 1 if $args->{feed}->title;
        my $res = Plagger::UserAgent->new->get($args->{feed}->link);
        $res->content =~ m!&gt;\s(.*?)</div>!;
        $args->{feed}->title($1);
        1;
      

2007年8月28日

Plagger::Plugin::Filter::AddHatenaBookmarkTagsCount

エントリーに対して指定したはてなブックマークのタグがいくつ付いているかを数えてmetaに設定するPlaggerのFilter Plugin。 例えば、『はてなブックマーク - タグ 「perl」を含む注目エントリー』のRSSからさらに、特定のタグ「plagger」とか「catalyst」とかをつけられたエントリーを探すのに便利かも。 オプションでrateにも強引に突っ込むことができます。 foreachが3重になってちょっとかっこ悪いけど、こんな感じです。

package Plagger::Plugin::Filter::AddHatenaBookmarkTagsCount;
use strict;
use base qw( Plagger::Plugin );

our $VERSION = '0.01';

sub register {
    my ($self, $context) = @_;
    $context->register_hook( 
                                                        $self,
                                                        'plugin.init'        => \&init_add_hatena_bookmark_tags_count,
                                                        'update.entry.fixup' => \&filter,
                                                );
}

sub init_add_hatena_bookmark_tags_count {
    my ($self, $context, $args) = @_;
        $self->{meta_name} = $self->{conf}->{meta_name} || "tatena_tags_count";
}

sub filter {
    my ($self, $context, $args) = @_;
        my $uri = "http://b.hatena.ne.jp/entry/rss/" . $args->{entry}->link;
        my $content = Plagger::Util::load_uri(URI->new($uri),$self);

        # parse by XML::LibXML because Plagger::FeedParser can't get dc:subject
        my $parser = XML::LibXML->new;
        my $doc = $parser->parse_string($content);
        my @items = $doc->findnodes("//*[local-name()='item']");
        foreach my $item (@items){
                my $user = $item->findvalue("*[local-name()='title']/text()");
                my @subjects = $item->findnodes("dc:subject");
                foreach my $subject (@subjects){
                        foreach my $tag (@{$self->conf->{tags}}){
                                my $str = $subject->findvalue("./text()");
                                $str =~ s/\*//g;
                                if (lc($tag) eq lc($str)){
                                        $args->{entry}->meta->{$self->{meta_name}} ++;
                                }
                        }
                }
        }
        
        $args->{entry}->date(pop(@items)->findvalue("./dc:date/text()"));
        $args->{entry}->rate($args->{entry}->meta->{$self->{meta_name}})
                if $self->conf->{add_count_to_rate} && $args->{entry}->meta->{$self->{meta_name}};
}

1;

__END__

=head1 NAME

Plagger::Plugin::Filter::AddHatenaBookmarkTagsCount - add specified Hatena Bookmark tags count

=head1 SYNOPSIS

  - module: Filter::AddHatenaBookmarkTagsCount
    config:
            tags:
              - plagger
              - catalyst
      add_count_to_rate: 1 # optional
      meta_name: hatebu_count # optional

=head1 AUTHOR

Yusuke Wada

=head1 SEE ALSO

L<Plagger>

=cut

config.yamlの例

global:
  timezone: Asia/Tokyo
    
plugins:
  
  - module: Subscription::Config
    config:
      feed:
        - url: http://b.hatena.ne.jp/t/perl?mode=rss&sort=hot&threshold=3

  - module: Filter::AddHatenaBookmarkTagsCount
    config:
      tags:
        - plagger
        - catalyst
        - dbix
        - dbic
      add_count_to_rate: 1

  - module: Publish::Serializer
    config:
      dir: ./
      serializer: YAML
      filename: %i.yaml

Serializerで見た結果より

  - author: ~
    body: ' アプリを作ってると色々ログ系をDBに保存しておきたかったりするのですが...'
    date: 2007-08-24T17:06:25+09:00
    enclosures: []
    feed_link: http://b.hatena.ne.jp/t/perl
    id: http://d.hatena.ne.jp/nekokak/20070824/1187941930
    link: http://d.hatena.ne.jp/nekokak/20070824/1187941930
    meta:
      tatena_tags_count: 9
    rate: 9
    summary: ' アプリを作ってると色々ログ系をDBに保存しておきたかったりするのですが、...'
    tags:
      - ormap
      - MySQL
      - Perl
      - dbic
    title: 'Hatena::Diary::Neko::kak 500 Internal Server Error - DBICでテーブル名切替の術'
    widgets: []

2007年8月29日

sqliteのdbの文字コードを変換する

id:kamadangoがMT4に移行しようとしてたんだけど、dbの中身がeuc-jpで文字化けたりして戸惑ってたので、 utf8に変換してからインポートしてみたらうまくいった。 kamadango.comのミラーサイトができた。 ということでsqliteのdbファイル内データの文字コードを変換するのは以下でいけます。

$ echo '.dump' | sqlite3 mt.db | nkf -w > dump.txt
$ sqlite3 mt_utf8.db < dump.txt

2007年8月31日

CPANに☆が

CPANに☆のレビューがつきだした。 らき☆すたとはて☆すたはやっぱすごいな。

About 2007年8月

2007年8月にブログ「Yusukebe::Tech」に投稿されたすべてのエントリーです。過去のものから新しいものへ順番に並んでいます。

前のアーカイブは2007年6月です。

次のアーカイブは2007年9月です。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。


ブログSEO対策:track word seo