2008/01/31


iTunesのライブラリ情報XMLファイルをアップロードする事で自分に合ったアーティスト情報を教えてくれるサービス、「veena!」の検索ボックスを使って、指定のアーティストに関連する
  • YouTube動画
  • Yahooオークション情報
をWeb::Scraperでスクレイピングしてみようと思います。
ソースはそれ程難しくもなく
#!/usr/bin/perl

use strict;
use warnings;

use URI;
use URI::Escape qw(uri_escape_utf8 uri_unescape);
use Web::Scraper;
use YAML::Syck;

my $artist = shift || 'Ozzy Ozbourne';
my $uri = URI->new('http://www.veena.jp/srch_artist.php?artist_name='
    . uri_escape_utf8($artist));

my $youtube_list = scraper {
    process '//table[@class="info_tbl"]/tr/td',
        'video[]' => scraper {
            process '//a[1]', url => sub {
                my $url = shift->attr('href');
                $url =~ s/^.*\?url=(.*)$/$1/;
                uri_unescape($url);
            };
            process '//a[2]', title => 'TEXT';
            process '//img', image => '@src';
        };
    result 'video';
};

my $auction_list = scraper {
    process '//table[@class="info_tbl"]/tr/td',
        'auction[]' => scraper {
            process '//a[1]', url => '@href';
            process '//a[2]', title => 'TEXT';
            process '//img', image => '@src';
        };
    result 'auction';
};

my $artist_list = scraper {
    process '//a[contains(@href, "artist.php")]',
        'artists[]' => scraper {
            process 'a', id => sub {
                my $url = shift->attr('href');
                $url =~ s/^.*id=(.*)$/$1/;
                $url;
            };
            process 'a', 'youtube' => sub {
                my $url = shift->attr('href');
                $url =~ s/artist\.php/http:\/\/veena.jp\/list_youtube\.php/;
                my $list = $youtube_list->scrape(URI->new($url));
                \@$list;
            };
            process 'a', 'auction' => sub {
                my $url = shift->attr('href');
                $url =~ s/artist\.php/list_auction\.php/;
                my $list = $auction_list->scrape(URI->new_abs($url, $uri));
                \@$list;
            };
            process 'a', name => 'TEXT';
        }
};
my $result = $artist_list->scrape($uri);
warn Dump $result;
って感じ。YouTube動画情報一覧とYahooオークション情報はアーティスト情報にぶら下がる形で出力したかったので検索結果一覧用のscraperとその結果を取得するscraperを親子関係にしてあります。
結構一覧としてはキレイに出力されているかと思います。
---
artists: 
  - 
    auction: 
      - 
        image: !!perl/scalar:URI::http http://ac.c.yimg.jp/7/1026/1783/000/img305.auctions.yahoo.co.jp/users/6/4/6/7/rosiertrueblue-thumb-119657918759294.jpg
        title: Ozzy Osbourne
        url: !!perl/scalar:URI::http http://page.auctions.yahoo.co.jp/jp/auction/108393777
      - 
        image: !!perl/scalar:URI::http http://ac.c.yimg.jp/7/1022/1783/000/img245.auctions.yahoo.co.jp/users/6/4/6/7/rosiertrueblue-thumb-119657997018368.jpg
        title: Ozzy Osbourne
        url: !!perl/scalar:URI::http http://page11.auctions.yahoo.co.jp/jp/auction/n61267094
      - 
        image: !!perl/scalar:URI::http http://a1017.lm.a.yimg.com/7/1017/1783/000/img257.auctions.yahoo.co.jp/users/8/2/8/3/kokita74-thumb-119486785113507.jpg
        title: Ozzy Osbourne
        url: !!perl/scalar:URI::http http://page8.auctions.yahoo.co.jp/jp/auction/h52088580
   ...
    id: 216546
    name: Randy Rhoads (Ozzy Ozbourne)
    youtube:
      -
        image: !!perl/scalar:URI::http http://img.youtube.com/vi/MEUbYkLe_wo/default.jpg
        title: Ozzy Ozbourne's top 10 songs
        url: http://www.youtube.com/watch?v=MEUbYkLe_wo
      - 
        image: !!perl/scalar:URI::http http://img.youtube.com/vi/GLtjWi4qkIY/default.jpg
        title: Goodbye to Romance - Ozzy/Randy Rhoads (solo)
        url: http://www.youtube.com/watch?v=GLtjWi4qkIY
      - 
        image: !!perl/scalar:URI::http http://img.youtube.com/vi/AQqbNHhBWcI/default.jpg
        title: iron man
        url: http://www.youtube.com/watch?v=AQqbNHhBWcI
   ...
Ozzy OzbourneのキーワードでRandy Rhoadsも引っかかってウハウハです。
で、このYAMLをどうするか...
use LWP::UserAgent;
my $ua = LWP::UserAgent->new;
$ua->timeout(10);
$ua->agent('Mozilla');
for my $artist (@{$result->{artists}}) {
  for my $video (@{$artist->{youtube}}) {
    my $url = $video->{url};
    my $req = HTTP::Request->new(GET => $url);
    $req->header('Accept-Encoding', 'identity');
    my $res = $ua->request($req);
    if ($res->is_error) {
      if ((my $verify_url = $res->request->uri) =~ /\/verify_age\?/) {
        my $verify_req = HTTP::Request->new(POST => $verify_url, {action_confirm => 'Confirm'});
        $res = $ua->request($verify_req);
        $res = $ua->request($req) if $res->is_success;
      }
    }
    if ($res->content =~ /video_id=([^&]+)&l=\d+&t=([^&]+)/gms) {
      my $flv = "http://youtube.com/get_video?video_id=$1&t=$2";
      print "Downloading $flv\n";
      my $download_req = HTTP::Request->new(GET => $flv);
      $download_req->referer($url);
      my $res = $ua->request($download_req);
      if ($res->is_success) {
        open FH, ">$2.flv";
        binmode FH;
        print FH $res->content;
        close FH;
        print "Downloaded $2.flv\n";
      } else {
        print "Failed to download $2.flv\n";
      }
    } else {
      print "Not found flv file\n";
    }
  }
}
やっぱこうなりますわね...

mattn the crazy train scraper!
Posted at by




なんにつかうねん!www
/lang/perl/Acme-Jyogakusei/trunk/ - CodeRepos::Share - Trac
こうか!? #!/usr/bin/perl

use strict;
use warnings;
use utf8;
use Acme::Jyogakusei;

my $jyogakusei = '女子高生';
my $re = Acme::Jyogakusei::regexp;
print "Jyogakusei\n" if $jyogakusei =~ /$re/;
あと #!/usr/bin/perl

use strict;
use warnings;
use utf8;
use Acme::Jyogakusei;

my $jyogakusei = 'なんちゃって女子高生';
my $re = Acme::Jyogakusei::regexp;
$jyogakusei =~ s/(なんちゃって$re)/おばちゃん/g;
print $jyogakusei;
こんなんもありか...
Posted at by




いいなぁiPod touch欲しいなぁ。

Plaggerでニコニコ動画を一括ダウンロード&変換 Podcast を生成して iPod touch で見る - 2007年11月最新版
うちでは臨時予算案は可決されそうにありません。宝くじが当たるま携帯で我慢したいと思います。

今日は、ゆーすけべさんの所に置いてあるYAMLをちょこっと弄って、携帯向け変換をやろうかと思います。ちょっとだけオリジナリティを出そうとニコニコではなく、YouTubeから...
まず、こんなYAMLを用意します。

youtube_hatena_tagged.yaml
global:
  assets_path: /home/user/plagger/assets/
  timezone: Asia/Tokyo
  log:
    evel: info

plugins:

  - module: Subscription::Config
    config:
      feed:
          - url: http://b.hatena.ne.jp/t/youtube?mode=rss&sort=hot&threshold=3
          
  - module: Filter::FindEnclosures
  - module: Filter::FetchEnclosure
    config:
      dir: /home/user/plagger/out
  - module: Filter::FLVInfo
  
  - module: Filter::FFmpeg
    config:
      command: /usr/bin/ffmpeg
      ext: 3gp
      dir: /home/user/plagger/out
      encoding: utf-8
      options:
        video_codec:         mpeg4
        audio_codec:         libamr_nb
        audio_sampling_rate: 8000
        audio_bit_rate:      12.2k
        frame_size:          176x144
      extra_options: -ac 1 -f 3gp

  - module: Publish::Feed
    config:
      format: RSS
      dir: /home/user/plagger/out
      filename: youtube_hatena_tagged.xml
はてなで「youtube」タグが付いているフィードを取ってきて、Enclosureをフェッチ&解析。最後にFFmpegで変換というフェーズになります。Feed作成は意味なさそうですが、この後役に立ちます。
私の携帯SB810T向けには、映像コーデックにmpeg4、音声コーデックにamr-nb(libamr_nb)を指定します。メーカーの仕様によると、AACでも行けそうな事が書いてありますが、どうやら動画でない音声ファイルの場合のみAACが再生出来るようです。
また、サンプルレート8000はamr-nbの標準値で、オーディオチャネル(ac)は1にしないといけない様です。 もちろん
  • lib/Plagger/Plugin/Filter/FFmpeg.pm
  • lib/Plagger/Plugin/Filter/FLVInfo.pm
はCodeReposから取得して下さい。
殆んどyusukebeさん(yousukebeさんではありません。ここ大事)のと同じです。大概はffmpegのオプションでなんとか出来るかと思います。ただ現状Plaggerのtrunkに入ってるFind-Enclosuresのyoutube.plでは、jp.youtube.comドメインのリソースが対象外になっていますので、以下のパッチを当てます。
Index: assets/plugins/Filter-FindEnclosures/youtube.pl
===================================================================
--- assets/plugins/Filter-FindEnclosures/youtube.pl (revision 1988)
+++ assets/plugins/Filter-FindEnclosures/youtube.pl (working copy)
@@ -3,7 +3,7 @@
 
 sub handle {
     my ($self, $url) = @_;
-    $url =~ qr!http://(?:www.)?youtube.com/(?:watch(?:\.php)?)?\?v=.+!;
+    $url =~ qr!http://(?:www.|jp.)?youtube.com/(?:watch(?:\.php)?)?\?v=.+!;
 }
 
 sub find {
※パッチが当たった物はCodeReposに入れてあります。チンして食べてください。ただGData APIでないのでカッコよくはありませんが。
あとは # plagger -c youtube_hatena_tagged.yaml を実行すると、上のYAMLで指定している「out」フォルダにゴッチョリと3gpファイルが出来上がっている筈です。
携帯の場合はPodcast出来ませんし、動画のサイズが大きくなるとメールで送ったりHTTPでダウンロードする事も出来ません。結局はメモリ転送になりますが、Feed.pmにちょっとだけ手を加えて...
Index: lib/Plagger/Plugin/Publish/Feed.pm
===================================================================
--- lib/Plagger/Plugin/Publish/Feed.pm  (revision 1988)
+++ lib/Plagger/Plugin/Publish/Feed.pm  (working copy)
@@ -122,6 +122,15 @@
     open my $output, ">:utf8", $filepath or $context->error("$filepath: $!");
     print $output $xml;
     close $output;
+
+    if ($self->conf->{command_after}) {
+        my $command = $self->conf->{command_after};
+        my $dir = $self->conf->{dir};
+        $filepath =~ s!\\!/!g;
+        $command =~ s!\$\(filename\)!$filepath!g;
+        $command =~ s!\$\(dir\)!$dir!g;
+        system($command);
+    }
 }
 
 sub make_author {
こんなパッチを当てると、フィード出力時に「command_after」が実行されますので   - module: Publish::Feed
    config:
      format: RSS
      dir: /home/user/plagger/out
      filename: youtube_hatena_tagged.xml
      command_after: find $(dir) -name "*.3gp" -exec cp \{\} /media/usbdisk/private/myfolder/My Items/Video
こんなYAMLにしておけば、出来上がった3GPがUSB越しに携帯のmicroSDカードにズコーーーーンと転送されるって仕組です。
microSD-card
ズゴーーーーン
まぁ、Podcastと違って出来上がるまで、良い子ちゃんで待ってなきゃいけないですが...

追記
plagger流儀で言えば、Notify::Commandを使うべきですね。
Posted at by