2008/01/31


Web::Scraper 0.15とcisco_scraper.pl
問題が一つ。添削してくださったパッチだと process '//li/node()[4]', 'title' => sub {$_->string_value;};
となっているのですが、4番目とは限らないんです。
たとえば、
http://www.cisco-records.co.jp/html/item/004/010/item393180.html
は何曲か試聴サンプルがないために、この処理だと取得できないです。
おろろ...
これはtext()でTextNodeを参照するしかないですね。
ただ、text()では改行等のゴミまで拾ってしまうので、以下のようにnormalize-space()で空文字ノードを省いています。
もしかすると、node()[2]も同じように修正した方がいいかもしれませんね。
#!/usr/bin/perl

use strict;
use warnings;

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

my $uri = shift;

my %scraper;

$scraper{'link'} = scraper {
    process 'a', 'name' => 'TEXT';
    process 'a', 'uri'  => '@href';
    result qw/name uri/;
};

$scraper{'genre'} = scraper {
    process '//a[1]', 'top'   => $scraper{link};
    process '//a[2]', 'style' => $scraper{link};
    result qw/top style/;
};

$scraper{'track'} = scraper {
    process '//li/text()[normalize-space(.)!=""]', 'title' => sub {
        my $s = $_->as_XML;
        $s =~ s/\s+$//;
        return $s;
    };
    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'  => '@src';
    process 'td.de_price',              'price'   => 'TEXT';
    process 'td.de_label>a',            'label'   => $scraper{link};
    process 'td.de_genre',              'genre'   => $scraper{genre};
    process 'td[headers="de_format"]',  'format'  => 'TEXT';
    process 'td[headers="de_release"]', 'release' => 'TEXT';
    process 'td[headers="de_country"]', 'country' => 'TEXT';
    process 'td[headers="de_sheet"]',   'sheet'   => 'TEXT';
    process 'td[headers="de_arrival"]', 'arrival' => 'TEXT';
    process 'td[headers="de_nomber"]',  'number'  => 'TEXT';
    process '//p[@class="de_star"]/node()[2]', 'star' => 'TEXT';
    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/;
};

my $item = $scraper{'item'}->scrape( URI->new($uri) );
warn Dump $item;
あと、ブックマークコメント
コールバック渡しだと相対URLの展開がされないのは僕だけ?
との事ですが...少し調べてみた所Web::Scraper側でパッチが必要かもしれません。
以下svn/trunk(rev2351)からの差分です。
Index: lib/Web/Scraper.pm
===================================================================
--- lib/Web/Scraper.pm  (revision 2351)
+++ lib/Web/Scraper.pm  (working copy)
@@ -152,12 +152,12 @@
         local $_ = $node;
         return $val->($node);
     } elsif (blessed($val) && $val->isa('Web::Scraper')) {
-        return $val->scrape($node);
+        return $val->scrape($node, $uri);
     } elsif ($val =~ s!^@!!) {
         my $value =  $node->attr($val);
         if ($uri && is_link_element($node, $val)) {
             require URI;
-            $value = URI->new_abs($value, $uri);
+            $value = URI->new_abs($value, $uri)->as_string;
         }
         return $value;
     } elsif (lc($val) eq 'content' || lc($val) eq 'text') {
Posted at by




muumoo.jpPlaggerで取得したGoogleブックマークのフィードを整えるFilter:GoogleBookmarksFeedを書いたけど日本語消えちゃう (管理人日記)という記事より。
喜んだのもつかの間、日本語の文字を含むタグやコメントを書くと、その文字が消えてしまうようです。Plaggerではありがちな問題なような気がしますが、このPluginでも起きてしまいました。
確かに、ブラウザ上からだと日本語は見えるんですが、どうやらGoogleさんはUser-Agentを見て勝手にencodingをISO-8859-1に変えておられるようです。
# curl -L 'https://www.google.com/bookmarks/?output=rss' -u username:password
<?xml version="1.0" encoding="ISO-8859-1"?><rss vers
...
config.yamlの先頭に global:
  timezone: Asia/Tokyo
  user_agent:
    agent: Mozilla/5.0
を入れたら取得出来ました。
ブクマコメントで書こうかと思いましたが、記事が半月程前のものなので管理人さんも見てないかと思い、記事にしました。

それよりも...LivedoorClip.pmで Plagger [info] plugin Plagger::Plugin::Subscription::Config loaded.
Plagger [info] plugin Plagger::Plugin::UserAgent::AuthenRequest loaded.
Plagger [info] plugin Plagger::Plugin::Filter::GoogleBookmarksFeed loaded.
Plagger [info] plugin Plagger::Plugin::Publish::LivedoorClip loaded.
Plagger [info] plugin Plagger::Plugin::Bundle::Defaults loaded.
Plagger [info] plugin Plagger::Plugin::Aggregator::Simple loaded.
Plagger [info] plugin Plagger::Plugin::Summary::Auto loaded.
Plagger [info] plugin Plagger::Plugin::Summary::Simple loaded.
Plagger [info] plugin Plagger::Plugin::Namespace::HatenaFotolife loaded.
Plagger [info] plugin Plagger::Plugin::Namespace::MediaRSS loaded.
Plagger [info] plugin Plagger::Plugin::Namespace::ApplePhotocast loaded.
Plagger::Plugin::Aggregator::Simple [info] Fetch https://www.google.com/bookmarks/?output=rss
Plagger::Plugin::UserAgent::AuthenRequest [info] Adding credential to Google Search History at www.google.com:443
Plagger::Cache [debug] Cache HIT: Aggregator-Simple|https://www.google.com/bookmarks/?output=rss
Plagger::Plugin::Aggregator::Simple [debug] 200: https://www.google.com/bookmarks/?output=rss
Plagger::Plugin::Aggregator::Simple [info] Aggregate https://www.google.com/bookmarks/?output=rss success: 15 entries.
Died at C:/Perl/site/lib/WWW/Mechanize.pm line 1705.
なエラーが出る。なんぞ?
とりあえずcpan upgrade行ってきます。

追記1
GoogleBookmarksFeedで、tagsは1個でも配列で返ってきてそうだったので以下のように修正してます。もしかしたら間違ってるかも *** GoogleBookmarksFeed.pm.orig Tue Sep 04 11:39:49 2007
--- GoogleBookmarksFeed.pm  Tue Sep 04 11:40:15 2007
***************
*** 22,28 ****
              $args->{entry}->body($orig_body);
              $context->log(info => "Parsing Google Bookmarks title " . $args->{entry}->permalink);
          }
!         if (my @orig_tags = @{$args->{orig_entry}->{entry}->{$ns}->{bkmk_label}}) {
              $args->{entry}->tags(@orig_tags);
          }
      }
--- 22,28 ----
              $args->{entry}->body($orig_body);
              $context->log(info => "Parsing Google Bookmarks title " . $args->{entry}->permalink);
          }
!         if (my @orig_tags = $args->{orig_entry}->{entry}->{$ns}->{bkmk_label}) {
              $args->{entry}->tags(@orig_tags);
          }
      }
追記2
大嘘ついてました。tagsは1つの場合は文字、2つ以上の場合は配列で戻るみたいです。 *** GoogleBookmarksFeed.pm.orig Tue Sep 04 11:39:49 2007
--- GoogleBookmarksFeed.pm  Tue Sep 04 14:54:17 2007
***************
*** 22,29 ****
              $args->{entry}->body($orig_body);
              $context->log(info => "Parsing Google Bookmarks title " . $args->{entry}->permalink);
          }
!         if (my @orig_tags = @{$args->{orig_entry}->{entry}->{$ns}->{bkmk_label}}) {
!             $args->{entry}->tags(@orig_tags);
          }
      }
  }
--- 22,33 ----
              $args->{entry}->body($orig_body);
              $context->log(info => "Parsing Google Bookmarks title " . $args->{entry}->permalink);
          }
!         if (my $orig_tags = $args->{orig_entry}->{entry}->{$ns}->{bkmk_label}) {
!           if (ref($orig_tags) eq "ARRAY") {
!               $args->{entry}->tags($orig_tags);
!           } else {
!               $args->{entry}->tags([$orig_tags]);
!           }
          }
      }
  }
Posted at by




twitterのAPIでfriendsが100件しか取れなくなって久しいですが...
WWW::MechanizeとXPathでtwitterの全friendsを取得するサンプル作ってみました。 あまりやり過ぎると、オフィシャル側に怒られそうな気もしますが...
後の使い方は、適当で... #!/usr/local/bin/perl

use warnings;
use strict;
use LWP::Simple;
use XML::Simple;
use WWW::Mechanize;
use HTML::TreeBuilder::XPath;
use HTML::Selector::XPath qw(selector_to_xpath);
use Data::Dumper;

my $username = 'your_username';
my $password = 'your_password';

my $m = WWW::Mechanize->new(timeout => 10);
$m->get('http://twitter.com/login');
$m->submit_form(
    form_number => 1,
    fields    => {
        username_or_email  => $username,
        password           => $password,
    },
    button    => 'commit',
);

my $xpath = selector_to_xpath('tr.vcard');
my @friends;

my $num_page = 1;
while (1) {
    my $res = $m->get("http://twitter.com/friends/?page=$num_page");
    my $encoding = $res->header('Content-Encoding');
    my $content = $res->content;
    $content = Compress::Zlib::memGunzip($content) if $encoding =~ /gzip/i;
    $content = Compress::Zlib::uncompress($content) if $encoding =~ /deflate/i;

    my $tree = HTML::TreeBuilder::XPath->new;
    $tree->parse($content);
    $tree->eof;
    my @nodes = $tree->findnodes($xpath);
    for my $tr (@nodes) {
        push(@friends, {
                nick => $tr->findnodes('td/strong/a')->[0]->as_text,
                image => $tr->findvalue('td[@class="thumb"]//img/@src')->as_string,
                name => $tr->findvalue('td[@class="thumb"]//img/@alt')->as_string,
                description => $tr->findvalue('td/strong/a/@title')->as_string,
                url => $tr->findvalue('td[@class="thumb"]/a/@href')->as_string,
            });
    }
    $tree->delete;
    @nodes or last;
    $num_page++;
}

print Dumper @friends;

最近遊んでる物、ほとんどmiyagawa氏のものばっかだな...
Posted at by