Amazonからデータを取得して、ハッシュに変換する
というモジュールを作ってみた。XML::SimpleやXML::LibXML::Simpleを使った方が楽なんだけど、
$xml->{BrowseNodeResponse}->{BrowseNodes}->{BrowseNode};
というふうに書くのが面倒なので、XML::LibXMLを使って任意のハッシュデータを作るようにしてみた。ただ、XPathのハッシュを書かなきゃいけないので、面倒くささは大して変わらない気もする。
package Amazon; use strict; use warnings; use URI; use Carp; use LWP::Simple; use XML::LibXML; sub new { my ($class, $access_key, $associate_tag) = @_; unless ($access_key) { Carp::croak("required access key"); } my $self = { aws_url => "http://webservices.amazon.co.jp/onca/xml", params => { Service => "AWSECommerceService", Version => "2008-04-07", AWSAccessKeyId => $access_key, AssociateTag => $associate_tag, }, parser => XML::LibXML->new }; return bless $self, $class; } sub request { my ($self, $params, $xpath, $force_array) = @_; my $uri = URI->new($self->{aws_url}); $uri->query_form( %{$self->{params}}, %{$params} ); my $content = LWP::Simple::get($uri) or Carp::croak("cannot get content from $uri"); my $root = $self->{parser}->parse_string($content)->documentElement; my @errors = $root->findnodes( $self->replace_xpath("//Errors/Error/Message") ); if (@errors) { Carp::croak(join ";", map { $_->textContent } @errors); } $xpath = {root => $xpath}; $force_array ||= []; push @$force_array, "root" unless $force_array == 1; my $hash = $self->node2hash($root, $xpath, $force_array); return @{$hash->{root}}; } sub node2hash { my ($self, $node, $xpath, $force_array) = @_; my $result = {}; while (my ($key, $xp) = each %$xpath) { next if $key eq "root_xpath"; my @values; if (ref $xp eq "HASH") { if (exists $xp->{root_xpath}) { map {push @values, $self->node2hash($_, $xp, $force_array)} $node->findnodes( $self->replace_xpath($xp->{root_xpath}) ); } else { push @values, $self->node2hash($node, $xp, $force_array); } } else { @values = map {$_->textContent} $node->findnodes($self->replace_xpath($xp)); } if (@values > 1 or $force_array == 1 or (ref $force_array eq"ARRAY" and grep {$_ eq $key} @$force_array)) { $result->{$key} = \@values; } else { $result->{$key} = $values[0]; } } return $result; } sub replace_xpath { my ($self, $xpath) = @_; $xpath =~ s!<([^/]+)>!*[local-name()='$1']!g; return $xpath; } 1;
#!/usr/bin/perl use strict; use warnings; use utf8; use Amazon; use YAML::Syck; my $access_key = "1K6RFH5CWTBQWH0Y6702"; my $browse_node_id = "492332"; my $amazon = Amazon->new($access_key); my $params = { Operation => "BrowseNodeLookup,ItemSearch", "BrowseNodeLookup.1.BrowseNodeId" => $browse_node_id, "ItemSearch.1.BrowseNode" => $browse_node_id, "ItemSearch.1.SearchIndex" => "Books" }; my $xpath = { root_xpath => "//<BrowseNodes>/<BrowseNode>", BrowseNodeId => "<BrowseNodeId>", Name => "<Name>", Children => { root_xpath => "<Children>/<BrowseNode>", BrowseNodeId => "<BrowseNodeId>", Name => "<Name>", }, Ancestors => { root_xpath => "//<Ancestors>/<BrowseNode>", BrowseNodeId => "<BrowseNodeId>", Name => "<Name>", }, TotalItems => "//<TotalResults>", TotalPages => "//<TotalPages>" }; my $force_array = ["Children", "Ancestors"]; my ($result) = $amazon->request($params, $xpath, $force_array); print YAML::Syck::Dump($result);
--- Ancestors: - BrowseNodeId: 466298 Name: コンピュータ・インターネット - BrowseNodeId: 465610 Name: ジャンル別 - BrowseNodeId: 465392 Name: 本 BrowseNodeId: 492332 Children: - BrowseNodeId: 525588 Name: インターネット入門書 - BrowseNodeId: 502720 Name: ブラウザ - BrowseNodeId: 492062 Name: e-ビジネス - BrowseNodeId: 502718 Name: 通信・メール - BrowseNodeId: 515206 Name: Web開発 - BrowseNodeId: 542586 Name: セキュリティー管理 - BrowseNodeId: 886896 Name: ネット社会 - BrowseNodeId: 3431651 Name: インターネット・Web開発 全般 Name: インターネット・Web開発 TotalItems: 7854 TotalPages: 786