Perlでブックオフの店舗を検索し、結果をハッシュの配列に格納する

HTMLを解析する練習です。Perlの配列とかハッシュの扱いも少し分かりました。 以下のブックオフの検索をPerlでやっただけです。

今回の経験を経て、まぁAPIが公開されてなくても、検索フォームとかがあればいくらでも いじれるということが分かりました。

動作は、引数の単語を「駅名」と「店名/地名」の両方にいれて検索しています。POSTで投げているだけ。 続いて、返ってきた結果をHTML::TreeBuilderを使って木構造へと解析します。さすがに 正規表現は疲れました^^

出来上がった木構造から、TDタグを探し、一段上ってTRタグの中身を1行分として配列に 格納し、そこからTDタグをもう一回抽出してハッシュを作り、1店舗分のデータを作り、 最後に配列につっこんでいます。口で説明しても分かりにくいのでソースをどうぞ。

#!/usr/bin/perl -w

use Jcode;
use strict;
use HTTP::Request::Common;
use LWP::UserAgent;
use HTML::TreeBuilder;
use Encode;
use Encode::Guess;

sub bookoff{
# リクエストを整える
    my ($str) = @_;
    my $req = POST('http://www.bookoff.co.jp/shop/shop.php', [action => 'search', station => $str, shop_name => $str]);

# 実際にリクエスト
    my $ua = LWP::UserAgent->new;
    $ua->parse_head(0);
    my $res = $ua->request($req);
    my $data = $res->content;

# TreeBuilderはUTF8で文字化けするので、一度デコードする
    my $encode = guess_encoding($data, qw/ euc-jp shiftjis 7bit-jis /);
    $data = decode($encode->name, $data) unless (utf8::is_utf8($data));

# パーサにかける
    my $tree = HTML::TreeBuilder->new();
    $tree->parse($data);

# TDタグを探してlinks配列に入れる
    my @links = $tree->look_down(
        _tag => 'td'
        );

# TDタグを一つ上ってTRタグの中身をrows配列へ。当然重複するのでそれを削除
    my @rows = map { $_->parent } @links;
    my %count;
    @rows = grep {!$count{$_}++} @rows;

# 各行からTDタグの中身をハッシュにし、それをshop配列に順に格納
# UTF8はエンコードしてから入れる
    my @shop;
    for my $row (@rows) {
        my @cells = $row->look_down( _tag => 'td' );
        my $shop_data = {
            name => encode($encode->name, $cells[0]->as_text),
            time => encode($encode->name, $cells[1]->as_text),
            tel => encode($encode->name, $cells[2]->as_text),
            place => encode($encode->name, $cells[3]->as_text)
        };
        push(@shop, $shop_data);
    }

    return @shop;

    $tree = $tree->delete;
}

my @shop = bookoff("新宿");

foreach my $hash (@shop) {
    print $hash->{name}, "\t", $hash->{time}, "\t", $hash->{tel}, "\t", $hash->{place}, "\n";
}

実行結果はこちら。

BOOKOFF 高田馬場店      10:00~23:45    03-5272-3668    新宿区高田馬場1-33-6平和相互ビル1F
BOOKOFF 大久保明治通り店        10:00~24:00    03-5155-1971    新宿区大久保2-5-20
BOOKOFF 新宿靖国通り店  10:00~23:00    03-5368-0654    新宿区新宿5-2-1
BOOKOFF 西新宿小滝橋通り店      10:00~23:00    03-5337-3533    新宿区西新宿7-7-29

新宿付近のブックオフが格納できていますね。ついでに時間・電話番号・住所も。

Perlは検索するとたくさんの参考サイトがひっかかるので助かりますね。さすが歴史が違う。 ということで、参考サイトさんには陳謝してリンクを貼らせて頂きます。