2008/01/31


これでFilterも作りやすくなるのかな...
例えば、はてなブックマークのフィードからShibuya.pmタグが付いてる物のOPMLを作るとか?(自身無さげ)
でもこれ、MIMEパターンをconfig.yamlに上手くめり込ませる方法ってないのかな...
指定する場合、「このURLに対しては変則的なxxxなMIMEで取りたい」って使いたいんだよね。

Index: lib/Plagger/Plugin/Subscription/Feed.pm
===================================================================
--- lib/Plagger/Plugin/Subscription/Feed.pm (revision 1959)
+++ lib/Plagger/Plugin/Subscription/Feed.pm (working copy)
@@ -17,7 +17,6 @@
 sub load {
     my ( $self, $context ) = @_;
 
-    # TODO: Auto-Discovery, XML::Liberal
     my $uri = URI->new( $self->conf->{url} )
       or $context->error("config 'url' is missing");
 
@@ -30,6 +29,20 @@
     my $content = Plagger::Util::load_uri($uri);
     my $feed = eval { Plagger::FeedParser->parse(\$content) };
 
+    if unless($feed) {
+        use HTML::TokeParser;
+        my $parser = HTML::TokeParser->new(\$content);
+        while (my $token = $parser->get_tag("link")) {
+            my $attr = $token->[1];
+            if ($attr->{rel} eq 'alternate'
+                    && ($attr->{type} eq 'application/rss+xml'
+                     or $attr->{type} eq 'application/atom+xml') {
+                $uri = $attr->{href};
+                $feed = eval { Plagger::FeedParser->parse(\$content) };
+                last;
+            }
+        }
+    }
     unless ($feed) {
         $context->log( error => "Error loading feed $uri: $@" );
         return;
Posted at by




こういう使い方もあるね。
で、どうする...って訳でもないけど
※そういうの、「使い道ない」っていうんだよね。そうだよね。
#!/usr/bin/perl

use strict;
use warnings;

use Web::Scraper;
use URI;
use YAML;

my $airlines_accident_scraper = scraper {
  process '//div[@class="entry-content"]//table/tr',
    'airlines[]' => scraper {
      process '//td[1]', title => 'TEXT';
      process '//td[2]', last_accident => 'TEXT';
      process '//td[3]', flight_count => 'TEXT';
      process '//td[4]', death_accident => 'TEXT';
      process '//td[5]', death_rate => 'TEXT';
      process '//td[6]', accident_incidence => 'TEXT';
      process '//td[7]', total_rank => 'TEXT';
    };
  result 'airlines';
};

my $list = $airlines_accident_scraper->scrape(URI->new('http://www.manji.com/jp/2007/08/post_22.html'));
use YAML;
warn Dump $list;
リストは、マスコミが報じない危険な航空会社リストから拝借。

余談ですが...
Web::Scraper 0.16あたりから、@参照するとstringでなく、URIか返ってくるようになってるので、「認証付きのページで@srcを拾い上げて、認証無しでは参照出来ない画像を落とす」なんて事に使えるようになったみたいです。
Posted at by




Web::Scraperの0.15がリリースされたので、色々試してみてます。
ほんのりと変わった所を調べてみます。

UserAgentが置き換えられるようになった

uaのスコープを変えて頂けたので、先日の「WWW::MechanizeとWeb::Scraperでtwitterのfriendsを全部取ってみる」でやった以下の様な無理やりなUA置き換えでなく undef &Web::Scraper::__ua;
*Web::Scraper::__ua = sub {
    $mech;
};
以下のようにキレイな置き換えが出来るようになります。 $Web::Scraper::UserAgent = $mech;
スコープもourになってますから、別のパッケージ作るときにも便利かもしれませんね。


ショートカットとしてRAWが使えるようになった

これはインラインのjavascriptをそのままスクレイピングするのに使えますね。
#!/usr/bin/perl

use strict;
use warnings;
use Web::Scraper;
use URI;
use YAML;
use Data::Dumper;

my $my_script = scraper {
    process '//script[2]', 'code' => 'raw';
    result 'code';
};
my $item = $my_script->scrape( URI->new("http://mattn.kaoriya.net") );
warn Dump $item;
この例では、はてなスターのトークン設定部が取得出来ます。 --- |-
<!--
Hatena.Star.Token = '43aa5d7954c8fc062faae4eaaa864913599c277b';
-->
※htmlというショートカットでも使えます。


相対URLから絶対URLを自前で生成しなくてもよくなった

お腹が空いてきたのでWeb::Scraperでモスバーガーのメニューをスクレイピング」でやったように、今までは process '.', url => sub {URI->new_abs($_->attr('href'), $uri)->as_string;};
というコードが必要でしたが、「@」による属性参照でリンクエレメントであるようならば、自動的に絶対URLを生成してくれます。
上のようなコードも process '.', url => '@href';
と楽になりますね。
以上が私が0.13と0.15のdiffをざーーーーと見た感じの変更点です。


おまけ

今日はこれに付け加え、一つtipsを...
Web::ScraperでCISCO RECORDSをスクレーピングより
たとえば <li><span>1</span>Track 1</li>
というHTMLから"Track1"だけを抽出しようにも process 'li', 'title' => 'TEXT';
だと 1Track1 なんて結果になるのでそれを回避するために process 'li', 'title' => sub {
    my $elem = shift;
    $elem->find_by_tag_name('span')->delete;
    return $elem->as_text;
};
なんてことをしてるのですが、もっといい方法があるはず。
treeを壊さずやるとすれば、TextNodeを参照するのがいいかと思います。
例えば、XPathのnode()を使い、番号指定で取得します。だた現状のWeb::ScraperではTextNodeはショートカットで参照出来ませんので、以下のようにstring_valueを返すように手を加えると上手く行きます。
※相対URLの修正も含んでいます。
--- cisco_scraper.pl    Tue Sep 18 13:30:20 2007
+++ cisco_scraper2.pl   Tue Sep 18 14:12:49 2007
@@ -14,9 +14,7 @@
 
 $scraper{'link'} = scraper {
     process 'a', 'name' => 'TEXT';
-    process 'a', 'uri'  => sub {
-        return URI->new_abs( $_->attr('href'), $uri )->as_string;
-    };
+    process 'a', 'uri'  => '@href';
     result qw/name uri/;
 };
 
@@ -27,23 +25,15 @@
 };
 
 $scraper{'track'} = scraper {
-    process 'li', 'title' => sub {
-        my $elem = shift;
-        $elem->find_by_tag_name('span')->delete;
-        return $elem->as_text;
-    };
-    process 'li>a', 'uri' => sub {
-        return URI->new_abs( $_->attr('href'), $uri )->as_string;
-    };
+   process '//li/node()[4]', 'title' => sub {$_->string_value;};
+    process 'li>a', 'uri' => '@href';
     result qw/title uri/;
 };
 
 $scraper{'item'} = scraper {
     process 'td.de_title',      'title'  => 'TEXT';
     process 'td.de_artist',     'artist' => 'TEXT';
-    process 'td.nm_jacket>img', 'image'  => sub {
-        return URI->new_abs( $_->attr('src'), $uri )->as_string;
-    };
+    process 'td.nm_jacket>img', 'image'  => '@src';
     process 'td.de_price',              'price'   => 'TEXT';
     process 'td.de_label>a',            'label'   => $scraper{link};
     process 'td.de_genre',              'genre'   => $scraper{genre};
@@ -53,12 +43,7 @@
     process 'td[headers="de_sheet"]',   'sheet'   => 'TEXT';
     process 'td[headers="de_arrival"]', 'arrival' => 'TEXT';
     process 'td[headers="de_nomber"]',  'number'  => 'TEXT';
-    process 'p.de_star',                'star'    => sub {
-        my $elem = shift;
-        $elem->find_by_tag_name('span')->delete;
-        return $elem->as_text;
-
-    };
+    process '//p[@class="de_star"]/node()[2]', 'star' => sub {$_->string_value;};
     process 'ul[id="de_sound"]>li', 'tracks[]' => $scraper{track};
     result
         qw/title artist image price label genre format release release country sheet arrival number star tracks/;
このTextNode参照の需要があるならショートカットでも良いと思うんですが、いまんところ無さそうですね。

追記
otsune氏より「diff -uじゃないと...」という指摘で修正。
Posted at by