Net::Amazonでページング

Net::Amazonでページングがうまくいかなかったので、それに対応した方法を書く。


Amazon APIのページの概念は、1ページ10アイテム。ページ指定が1なら検索結果の1件目から10件目、2なら11件目から20件目のデータが取得される。


で、Net::Amazonだとどうなってるか。
ページ関連のパラメータは2つある。max_pagesと、page。
max_pagesは、1回の検索で取得するデータ量を指定する。デフォルト値は5。1回の検索で5ページ分=50アイテム分のデータを取得する。
変更するには、Catalystでモデルを作ってる場合だと /lib/MyApp/Model/Net/Amazon.pm をいじる。

__PACKAGE__->config(
    token => 'XXXXXXXXXXXXXXXXXXXX',
    secret_key   => 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
    locale       => 'jp',
    max_pages       => 3, 
);

上記のようにmax_pagesを追記してやる。ここでは3を指定しているので、3ページ分のデータが取得されるはず。
実際、次のように検索すると、30件のデータが取得される。

	my $ua = $c->model('Net::Amazon');
	
	my $response = $ua->search( 
		keyword => 'perl',
		mode => 'books',
	);


もうひとつのpageは、検索の際、データ取得の始点を指定する。こんな感じ。

	my $ua = $c->model('Net::Amazon');
	
	my $response = $ua->search( 
		keyword => 'perl',
		mode => 'books',
		page => 2,
	);

この結果、11件目から30件目までのデータが取得される。Amazon APIにとっての2ページ目(11件目〜20件目)から、max_pagesで指定したところまでのデータが取得されている模様。


実際のページングの動作を考えると、max_pagesで指定したデータ件数を1ページとみなして、2ページ目で31〜60件目、3ページで61〜90件目のデータを取得するように変更したい。
ソースを見ると、Net::Amazonのrequestの中、213行目からが、ページングの処理になっている。

        # Stop if we've fetched max_pages already
        if(defined $page && $self->{max_pages} <= $page) {
            DEBUG("Fetched max_pages ($self->{max_pages}) -- stopping");
            last;
        }

        if($res->is_page_available($ref, $new_items, $page)) {
            $page++;
            redo REQUEST;
        }

1回のリクエストで取ってこれるデータ量は10件に決まってるので、現在のページ数がmax_pages未満ならページ数を増やして再度リクエストするロジックになっている。
これだと、pageでどんな値を指定しようが、max_pagesで指定したページまでのデータしか取ってきてくれない。指定したpageがmax_pages以上であれば、1ページ分のデータになる。


では変更。
まず、request()メソッド開始直後の、変数の指定部分。
2行目をコメントアウトして3行目、5行目を追記。

    my $url  = URI->new($self->intl_url($request->amzn_xml_url()));
#    my $page = $request->page();
    my $page = ( $request->page() - 1 ) * $self->{max_pages} + 1;
    my $ref;
    my $max_pages_in_this_search = $self->{max_pages} + $page - 1;

続いて、先ほど参照したページングのロジック部分。
2〜5行目をコメントアウトして、続きの4行を追記。

        # Stop if we've fetched max_pages already
#        if(defined $page && $self->{max_pages} <= $page) {
#            DEBUG("Fetched max_pages ($self->{max_pages}) -- stopping");
#            last;
#        }
        if(defined $page && $max_pages_in_this_search <= $page) {
            DEBUG("Fetched max_pages ($max_pages_in_this_search) -- stopping");
            last;
        }

        if($res->is_page_available($ref, $new_items, $page)) {
            $page++;
            redo REQUEST;
        }


解説。
$pageの指定を変更して、max_pagesで指定した分を1pageとして、Amazon APIの開始ページを算出している。以下例。

  • max_pages = 3
  • page = 2
  • $page = (2-1)*3-1 = 4

max_pages=3の場合の2ページ目は、Amazon APIでいう4ページ目となる。
$max_pages_in_this_searchの指定も追加している。この変数には、page指定を考慮して、Amazon APIで何ページ目まで検索するかを指定している。同じく例。

  • max_pages = 3
  • $page = 4
  • $max_pages_in_this_search = 3+4-1 = 6

ここで使ってる$pageは、先ほど算出したほうの値。で、$max_pages_in_this_searchは6となる。
ロジック部分では、max_pagesではなく、$max_pages_in_this_searchを判定に使っている。
ということで、max_pages=3、page=2を指定した場合、Amazon APIでの4〜6ページ目のデータを取得するようになった。