090718

| | コメント(1)


Product Advertising API(旧:Amazon アソシエイト Web サービス)の署名認
証開始が、あと一ヶ月とせまってきたので、とても重い腰をあげて、本棚シス
テムを変更しました。perlを思い出すのが億劫で放置していたのだけど、そろ
そろ。

なんか小粋なスクリプト言語を一個くらい自由自在に使えるようにしたいと思 うのだけど...。と思い続けて10年以上。
結局一日かかった。うっかりTimestampを二重にURLエンコードしていて大ハマり。
package uch_aws;

use strict;
use Data::Dumper;
use Jcode;
use WebService::Simple;

use POSIX qw (strftime);
use Digest::SHA qw (hmac_sha256_base64);
use URI::Escape;

my $AMAZON_HOST = 'ecs.amazonaws.jp';
#my $AMAZON_HOST = 'webservices.amazon.com';
my $AMAZON_DIR = '/onca/xml';

our @media_type =
    (
     { media => 'book',	id => 'ISBN',	index => 'Books' },
     { media => 'cd',	id => 'EAN',	index => 'Music' },
     { media => 'dvd',	id => 'EAN',	index => 'DVD' },
     { media => 'misc',	id => 'UPC',	index => 'Blended' },
    );

# These parameters are not changed.
our %aws_base_params =
    (
     Service        => 'AWSECommerceService',
     locale         => 'jp',
     ContentType    => 'text/xml',
     Version        => '2009-03-31',
    );

sub new
{
    my $class = shift;
    my $self =
    {
	public_key => shift,  # AWS Access Key ID
	private_key => shift, # AWS Secret Access key.
	associate_tag => shift,  # Associate ID (optional)
	aws => '',
    };
    $aws_base_params{'AssociateTag'} = $self->{associate_tag};
    $aws_base_params{'AWSAccessKeyId'} = $self->{public_key};

    $self->{aws} = WebService::Simple->new (
	base_url => 'http://' . $AMAZON_HOST . $AMAZON_DIR);

    return bless $self, $class;
}

sub aws_request
{
    my $self = shift;
    my %args = %{ shift @_ };
    my $key;
    my %timestamp =
	(
	 Timestamp => strftime("%Y-%m-%dT%TZ", gmtime())
	);
# Concatinate all parameters.
    my %all = (%args , %aws_base_params, %timestamp);
#    foreach $key (sort keys %all) {
#	print $key, '=', $all{$key}, "\n";
#    }

# Sort by key and URL-encode. (Cannonicalized)
    my $parameter = join '&', map { $_ . '=' . uri_escape ($all{$_}) }
    sort keys %all;
# Add GET method for signature.
    my $signature_text = join "\n", 'GET', $AMAZON_HOST, $AMAZON_DIR,
    $parameter;
# Signature.
    my $signature = hmac_sha256_base64 ($signature_text, $self->{private_key});
    $signature .= '=' while length ($signature) % 4;
# Prepare actual request. signature is URL-encoded.
    my $request = sprintf ("http://%s%s?%s&Signature=%s", $AMAZON_HOST,
			   $AMAZON_DIR, $parameter, uri_escape ($signature));
#    printf ("<%s>\n", $request);
# Access to AMAZON.
    my $response =  $self->{aws}->get_simple ($request);
#    print Dumper $response->parse_response;

    return $response;
}

sub item_info
{
    my ($self, $code, $title, $content) = @_;
    my $asin;
    my $media;
# Get ASIN from ISBN, EAN, UPC
    foreach my $type (@media_type) {
	$media = $type->{media};
	$asin = $self->code_to_asin ($code, $type);

	if (defined ($asin)) {
	    last;
	}
    }

    if (!defined ($asin)) {
	print STDERR "no ASIN for ", $code, "\n";
	return '';
    }

# Get Item information.
    my $item_query = $self->aws_request (
	{
	    Operation		=> 'ItemLookup',
	    ItemId		=> $asin,
	    ResponseGroup	=> 'Small,Images',
	}
	);
#print Dumper $item_query->parse_response;
    my $item = $item_query->parse_response->{Items}->{Item};
    if (!defined ($item)) {
	print STDERR "ASIN exists (", $asin, "), but no information\n";
	print STDERR $item_query->parse_response->{Items}->{Request}->
	{Errors}->{Error}->{Code}, "\n";
	return '';
    }

# Construct html tag.
    $$title .= sprintf ("%s ", $item->{ItemAttributes}->{Title});
    my $author = $item->{ItemAttributes}->{Author};
    if (ref ($author) eq 'ARRAY') {
	for (my $i = 0; $author->[$i]; $i++) {
	    $$title .= sprintf ("%s ", $author->[$i]);
	}
    } else {
	$$title .= sprintf ("%s ", $author);
    }

    my $creator = $item->{ItemAttributes}->{Creator};
    if (defined ($creator)) {
	if (ref ($creator) eq 'ARRAY') {
	    for (my $i = 0; (my $c = $creator->[$i]); $i++) {
		$$title .= sprintf ("%s ", $c->{content});
	    }
	} else {
	    $$title .= sprintf ("%s ", $creator->{content});
	}
    }

    my $image;
    if (defined (($image = $item->{SmallImage}->{URL}))) {
	$$content .= sprintf ("<a href=\"%s\"><img src=\"%s\"></a>\n",
			      $item->{DetailPageURL},
			      $image);
    } else {
	$$content .= sprintf ("<a href=\"%s\">%s</a>\n",
			      $item->{DetailPageURL},
			      $$title);
    }

    return $media;
}

sub code_to_asin
{
    my ($self, $code, $type) = @_;

    my $id = $type->{id};
    my $index = $type->{index};
    my $asin_query;

    if ($id eq 'UPC') {
	$asin_query = $self->aws_request (
	    {
		Operation	=> 'ItemSearch',
		'ItemSearch.Shared.Keywords' => $code,
		IdType		=> $id,
		SearchIndex	=> $index,
		ResponseGroup	=> 'Small',
	    }
	    );
    } else {
	$asin_query = $self->aws_request (
	    {
		Operation	=> 'ItemLookup',
		ItemId		=> $code,
		IdType		=> $id,
		SearchIndex	=> $index,
		ResponseGroup	=> 'Small',
	    });
    }

    return $asin_query->parse_response->{Items}->{Item}->{ASIN};
}
1;

こんな感じで使います。

#!/usr/pkg/bin/perl

BEGIN
{
    my $MT_DIR = '/home/uch/public_html/sn/';
    my $WEBTOOLS_DIR = '/home/uch/webtools/';
    push @INC, $MT_DIR . 'extlib';
    push @INC, $MT_DIR . 'lib';
    push @INC, $WEBTOOLS_DIR . 'lib';
    push @INC, $WEBTOOLS_DIR;
}

#6, DVD
#5, CD
#4, 本
#3, 本棚
#2, モブログ
#1, 日記

use strict;
use uch_mtrpc;
use uch_aws;

$uch_mtrpc::mt = new uch_mtrpc 'http://www.vnop.net/~uch/sn/mt-xmlrpc.cgi ', 'uch', 'MovabletypeのXMLRPC用のパスワード', 1;
$uch_aws::aws = new uch_aws '01XB2AJX6DH0D8W91S02', 'AWSのプライベートキー',
'uchamazon-22';

my $barcode;
my $title;
my $content;
my $category;
%::category_map = (
    misc	=> 3,
    book	=> 4,
    cd		=> 5,
    dvd		=> 6,
    );

while (<STDIN>) {
    $barcode = $barcode.$_;
}

if ((my $media = $uch_aws::aws->item_info ($barcode, \$title, \$content, \$category))) {
    print $barcode;
#    print $media;
#    print $::category_map{$media};
    my $post_id = $uch_mtrpc::mt->post
	($title, $content, $::category_map{$media}, 1);
}

exit;

コメント(1)

Thanks, quite useful.