Web::ScraperでXPath式を使ってパースする

今までHTML:TagParser*1で一生懸命切ったりはったりして所蔵検索の結果を組み立てていましたがWeb::Scraperを使うと同じ事がかなり楽にできるようなので試してみた

HTML::TagParserだと

〜略〜
Readonly my $QUERY_URL_PREFIX =>
    'https://library.city.iwaki.fukushima.jp/wehome/we/opac/kensakucheck.jsp?'
    . 'kensaku.x=35&kensaku.y=22&sryskb0=1&allsryskb0=1&sryskb1=2&allsryskb1=2&sryskb2=3'
    . '&allsryskb2=3&sryskb_length=3&taisyokan1=0&kanmei_length=6&max_kensu=10&KSKNO1=019'
    . '&KEYWORD1=&ITTI1=1&f_kanzen1=0&ANDOR2=0&KSKNO2=020&KEYWORD2=&ITTI2=1&f_kanzen2=0'
    . '&ANDOR3=0&KSKNO3=005&KEYWORD3=&ITTI3=1&f_kanzen3=0&ANDOR4=0&KSKNO4=070&KEYWORD4='
    . '&ITTI4=1&f_kanzen4=0&tandoku=120&tandoku_keyword='
    ;
Readonly my $QUERY_URL_SUFFIX =>
    '&siborikomi=040&hanni1=&hanni2='
    ;
Readonly my $DETAIL_URL =>
    'https://library.city.iwaki.fukushima.jp/wehome/we/opac/'
    ;

Readonly my $LIBNAME => {
    '総合' => '01',
    '小名' => '02',
    '勿来' => '03',
    '常磐' => '04',
    '内郷' => '05',
    '四倉' => '06',
};

my $obj = {
    key   => '07204',
    stock => 'false',
    uri   => [],
    lib   => {
        '01' => 'false',
        '02' => 'false',
        '03' => 'false',
        '04' => 'false',
        '05' => 'false',
        '06' => 'false',
    },
    name  => {
        '00' => 'いわき市立総合図書館',
        '01' => 'いわき市立総合図書館',
        '02' => 'いわき市立小名浜図書',
        '03' => 'いわき市立勿来図書館',
        '04' => 'いわき市立常磐図書館',
        '05' => 'いわき市立内郷図書館',
        '06' => 'いわき市立四倉図書館',
    },
};

my $html = HTML::TagParser->new($QUERY_URL_PREFIX . $isbn . $QUERY_URL_SUFFIX);
if (my @list = $html->getElementsByTagName( 'a' )) {
    for my $elem ( @list ) {
        my $attr = $elem->attributes;
        for my $vals ( values %$attr ) {
            if ( $vals =~ m{ \A itiranview }xms ) {
                push(@{ $obj->{uri} }, $DETAIL_URL . $vals );
                $obj->{stock} = 'true';
            }
        }
    }
};
for my $vals ( @{$obj->{uri}} ) {
    $html = HTML::TagParser->new( $vals );
    if (my @list = $html->getElementsByTagName( 'tr' )) {
        for my $elem ( @list ) {
            my $text = $elem->innerText();
            if ( $text =~ m{ \d{6,} }xms ) {
                ($text) = split(/\n/, $text);
                ${ $obj->{lib} }{ $LIBNAME->{ $text } || '01' } = 'true';
            }
        }
    }
}

指定したタグ配下がフラットなテキストになってしまうので切り出しが大変><

Web:Scraperだと

〜略〜
Readonly my $XPATH_1 => '//p[3]/table/tr/td[3]/a';
Readonly my $XPATH_2 => '//form/p[4]/table/tr/td[1]/b';
〜略〜
my $urls = scraper {
        process $XPATH_1, \&get_urls;
    }->scrape(URI->new($QUERY_URL_PREFIX . $isbn . $QUERY_URL_SUFFIX));


sub get_urls {
    my $node = shift;
    my $url  = URI->new_abs($node->attr('href'), $DETAIL_URL)->as_string;
    push(@{ $obj->{uri} }, $url);
    my $res = scraper {
            process $XPATH_2, \&get_details;
        }->scrape(URI->new($url));
    $obj->{stock} = 'true';
}

sub get_details {
    my $node = shift;
    my $text = encode_utf8($node->as_text);
    ${ $obj->{lib} }{ $LIBNAME->{ $text } || '01' } = 'true';
}

一寸試しただけでここまでシンプルに記述できるとは思いませんでした

追記

scraper {
        process $XPATH_1, \&get_urls;
    }->scrape(URI->new($QUERY_URL_PREFIX . $isbn . $QUERY_URL_SUFFIX));

resultを返さないタイプなので別に代入は不要

*1:このモジュールがダメなわけではなく、この使い方には向いてない