Perl/Amazon/getImages.md
... ...
@@ -6,329 +6,453 @@
6 6
- 「content-type」が「image/jpeg」なら存在する、「image/gif」なら存在しない。
7 7
8 8
# 動作サンプル
9
-- [CGI Top](http://www.takeash.net/cgi-bin/Amazon/getImages.cgi)
10
- - &amazon(B001EQ6A80);<br />
11
-[画像一覧](http://www.takeash.net/cgi-bin/Amazon/getImages.cgi?ASIN=B001EQ6A80)
12
- - &amazon(B002MZZ33K);<br />
13
-[画像一覧](http://www.takeash.net/cgi-bin/Amazon/getImages.cgi?ASIN=B002MZZ33K)
14
- - &amazon(B0026RIRRS);<br />
15
-[画像一覧](http://www.takeash.net/cgi-bin/Amazon/getImages.cgi?ASIN=B0026RIRRS)
16
- - &amazon(B002H9WBNQ);<br />
17
-[画像一覧](http://www.takeash.net/cgi-bin/Amazon/getImages.cgi?ASIN=B002H9WBNQ)
18
- - &amazon(B002L16FZU);<br />
19
-[画像一覧](http://www.takeash.net/cgi-bin/Amazon/getImages.cgi?ASIN=B002L16FZU)
20
- - &amazon(B0019JNTQS);<br />
21
-[画像一覧](http://www.takeash.net/cgi-bin/Amazon/getImages.cgi?ASIN=B0019JNTQS)
22
- - &amazon(B002SW3N16);<br />
23
-[画像一覧](http://www.takeash.net/cgi-bin/Amazon/getImages.cgi?ASIN=B002SW3N16)
24
- - &amazon(4775307614);<br />
25
-[画像一覧](http://www.takeash.net/cgi-bin/Amazon/getImages.cgi?ASIN=4775307614&SHOT=IN)
9
+- [CGI Top](https://www.takeash.net/cgi-bin/Amazon/getImages.cgi)
10
+ - [figma 初音ミク](https://www.takeash.net/cgi-bin/Amazon/getImages.cgi?ASIN=B001EQ6A80)
11
+ - [ねんどろいどぷち ボーカロイド シリーズ01 BOX](https://www.takeash.net/cgi-bin/Amazon/getImages.cgi?ASIN=B002MZZ33K)
12
+ - [figma ファイアボール ドロッセル](https://www.takeash.net/cgi-bin/Amazon/getImages.cgi?ASIN=B0026RIRRS)
13
+ - [ファイアボール ねんどろいど ドロッセル](https://www.takeash.net/cgi-bin/Amazon/getImages.cgi?ASIN=B002H9WBNQ)
14
+ - [RVR-14 フェイ・イェン・ザ・ナイト DNA SIDE 1/100スケールプラスチックキット](https://www.takeash.net/cgi-bin/Amazon/getImages.cgi?ASIN=B002L16FZU)
15
+ - [BAYONETTA 特典 スペシャルサウンドトラック「RODIN'S SELECTION」付き](https://www.takeash.net/cgi-bin/Amazon/getImages.cgi?ASIN=B0019JNTQS)
16
+ - [BAYONETTA (特典無し)](https://www.takeash.net/cgi-bin/Amazon/getImages.cgi?ASIN=B002SW3N16)
17
+ - [大槍葦人画集 Chronicle LIMITED EDITION](https://www.takeash.net/cgi-bin/Amazon/getImages.cgi?ASIN=4775307614&SHOT=1)
18
+ - 数学文章作法 推敲編 [Page:0](https://www.takeash.net/cgi-bin/Amazon/getImages.cgi?ASIN=4480095268) [Page:1](https://www.takeash.net/cgi-bin/Amazon/getImages.cgi?ASIN=4480095268&PAGE=1) [Page:2](https://www.takeash.net/cgi-bin/Amazon/getImages.cgi?ASIN=4480095268&PAGE=2)
26 19
27 20
# Bookmarklet
28 21
```javascript
29
-javascript:location='http://www.takeash.net/cgi-bin/Amazon/getImages.cgi?ASIN='+(location.href.match(/\/(dp|ASIN|product)\/([\w]+)/))[2];
22
+javascript:
23
+location = 'https://www.takeash.net/cgi-bin/Amazon/getImages.cgi?ASIN=' + (location.href.match(/\/(dp|ASIN|product)\/(\w+)/))[2];
30 24
```
31 25
32 26
# スクリプト
33
-- [getImages.zip](getImages.zip)
27
+- getImages.cgi
34 28
```perl
35 29
#!/usr/bin/perl
36 30
37 31
use strict;
38 32
use warnings;
39 33
use utf8;
34
+use feature qw(say);
40 35
use Encode;
41 36
use LWP::UserAgent;
42 37
use URI::Amazon::APA;
43 38
use XML::Simple;
44
-use YAML::Syck;
45
-use CGI::Pretty qw( -no_xhtml *table *ol ); # //HTML 4.01 Transitional//EN
39
+use YAML::Syck qw(LoadFile DumpFile Dump);
40
+use CGI;
46 41
use URI::Escape;
42
+use Template;
43
+use FindBin::libs;
44
+use TakeAmazon;
45
+use Term::Encoding qw(term_encoding);
46
+use open ':std' => ( $^O eq 'MSWin32' ? ':locale' : ':utf8' );
47 47
48 48
$YAML::Syck::ImplicitUnicode = 1;
49
-$CGI::POST_MAX = 1024;
50
-
51
-binmode( STDIN, ":utf8" );
52
-binmode( STDOUT, ":utf8" );
53
-binmode( STDERR, ":utf8" );
54
-
55
-# 自動フラッシュ有効化 (バッファリングを無効化)
56
-$| = 1;
57
-
58
-my $confyaml = './ID.yaml';
59
-my $conf = YAML::Syck::LoadFile( $confyaml ) or die( "$confyaml: $!\n" );
60
-
61
-my $cginame = 'Amazon getImages-JP';
62
-my $ImageUriBase =
63
- 'http://images-jp.amazon.com/images/P/%ASIN%.09.%SHOT%._SC%SIZE%ZZZZZZ_.jpg';
64
-my $NoImageUri = 'http://images-jp.amazon.com/images/G/09/nav2/dp/no-image-no-ciu.gif';
65
-my $DetailUri = 'http://www.amazon.co.jp/dp/%ASIN%/';
66
-my $CustomerImageUri = 'http://www.amazon.co.jp/gp/customer-media/product-gallery/%ASIN%';
67
-
68
-my @ShotsFixList = (
69
- { 'name' => 'Main', 'key' => 'MAIN', },
70
- { 'name' => 'Top', 'key' => 'TOPP', },
71
- { 'name' => 'Bottom', 'key' => 'BOTT', },
72
- { 'name' => 'Left', 'key' => 'LEFT', },
73
- { 'name' => 'Right', 'key' => 'RGHT', },
74
- { 'name' => 'Front', 'key' => 'FRNT', },
75
- { 'name' => 'Back', 'key' => 'BACK', },
49
+$CGI::POST_MAX = 1024;
50
+$| = 1;
51
+
52
+my $conf = loadConfig();
53
+
54
+#say Dump($conf);
55
+my $tt = Template->new(
56
+ { INCLUDE_PATH => "${FindBin::RealBin}/templates",
57
+ ENCODING => 'utf-8',
58
+ }
59
+) or die( Template->error() );
60
+
61
+my $q = new CGI;
62
+$q->charset('utf-8');
63
+my $cgiPath = $q->url( -absolute => 1 );
64
+my $args = checkArgs(
65
+ $conf,
66
+ scalar $q->param('URI'),
67
+ scalar $q->param('ASIN'),
68
+ scalar $q->param('SHOT'),
69
+ scalar $q->param('PAGE'),
70
+ scalar $q->param('MOD'),
76 71
);
77 72
78
-my @ShotsVarList = qw( PT IN );
79
-my %ShotsTypeList = (
80
- 0 => 'PIECE SHOTS / 書籍以外のその他の画像',
81
- 1 => 'INTERIOR SHOTS / 書籍のその他の画像',
73
+my $out = $q->header(
74
+ -type => 'text/html',
75
+ -charset => 'utf-8',
82 76
);
83 77
84
-my @SizeList = (
85
- { 'name' => 'Large', 'key' => 'RM' },
86
- { 'name' => 'Medium', 'key' => 'L' },
87
-# { 'name' => 'Small', 'key' => 'M' },
78
+#my $r = requestAws(
79
+# $conf,
80
+# { Operation => 'ItemSearch',
81
+# SearchIndex => 'All',
82
+# ResponseGroup => 'ItemAttributes,Images',
83
+# ItemId => $args->{'ASIN'},
84
+# },
85
+#);
86
+#say Dump($r);
87
+
88
+my $detail = getDetail( $conf, $args->{'URI'} );
89
+
90
+$tt->process(
91
+ "form.html",
92
+ { urlCgiTop => $cgiPath,
93
+ args => $args,
94
+ detail => $detail,
95
+ shots => makeShots( $conf->{'VariableShots'} ),
96
+ pages => makePages( $conf->{'PageLabels'} ),
97
+ images => searchImages( $conf, $args ),
98
+ },
99
+ \$out
100
+) or die( $tt->error );
101
+print $out;
102
+```
103
+
104
+- conf/config.yml
105
+```yaml
106
+CgiName: Amazon getImages-JP
107
+ImageUriBase: https://images-na.ssl-images-amazon.com/images/P/{ASIN}.09.{SHOT}._SC{SIZE}ZZZZZZ_.jpg
108
+NoImageUri: https://images-na.ssl-images-amazon.com/images/G/09/nav2/dp/no-image-no-ciu.gif
109
+DetailUri: https://www.amazon.co.jp/dp/{ASIN}/
110
+CustomerImageUri: https://www.amazon.co.jp/gp/customer-media/product-gallery/{ASIN}
111
+FixShots:
112
+ - Name: Main
113
+ Key: MAIN
114
+ - Name: Top
115
+ Key: TOPP
116
+ - Name: Bottom
117
+ Key: BOTT
118
+ - Name: Left
119
+ Key: LEFT
120
+ - Name: Right
121
+ Key: RGHT
122
+ - Name: Front
123
+ Key: FRNT
124
+ - Name: Back
125
+ Key: BACK
126
+VariableShots:
127
+ - Name: "PIECE SHOTS / 書籍以外のその他の画像"
128
+ Key: PT
129
+ - Name: "INTERIOR SHOTS / 書籍のその他の画像"
130
+ Key: IN
131
+Sizes:
132
+ - Name: Large
133
+ Key: RM
134
+ - Name: Medium
135
+ Key: L
136
+ #- Name: Small
137
+ # Key: M
138
+PageStep: 10
139
+AWS:
140
+ SERVICE_URI: https://webservices.amazon.co.jp/onca/xml
141
+ AWS_ACCESS_KEY_ID: <your access key>
142
+ SECRET_ACCESS_KEY: <your secret key>
143
+ ASSOCIATE_TAG: <your associate tag>
144
+```
145
+
146
+- lib/TakeAmazon.pm
147
+```perl
148
+# TakeAmazon.pm
149
+# Amazon サポートモジュール
150
+
151
+package TakeAmazon;
152
+
153
+use strict;
154
+use warnings;
155
+use utf8;
156
+use feature qw(say);
157
+use Encode;
158
+use Exporter 'import';
159
+use YAML::Syck qw(LoadFile DumpFile Dump);
160
+use Path::Class;
161
+use LWP::UserAgent;
162
+use URI::Amazon::APA;
163
+use XML::Simple;
164
+use Lingua::JA::Regular::Unicode;
165
+use lib qw( /home/Shared/lib C:/_Take/lib );
166
+use Term::Encoding qw(term_encoding);
167
+use open ':std' => ( $^O eq 'MSWin32' ? ':locale' : ':utf8' );
168
+
169
+$YAML::Syck::ImplicitUnicode = 1;
170
+$XML::Simple::PREFERRED_PARSER = 'XML::Parser';
171
+
172
+our @EXPORT = qw(
173
+ loadConfig startsWith
174
+ makePageLabels checkArgs makeShots makePages getDetail
175
+ makeAwsUri requestAws
176
+ searchImages searchForShots searchForSizes
88 177
);
89 178
90
-my $Step = 20;
91
-my %PageLabels = ();
92
-for( my $i=0; $i * $Step < 99; ++$i){
93
- $PageLabels{ $i } = 'PT' . substr( '0' . ( $i * $Step + 1 ), -2 )
94
- . '-PT'. substr( '0' . ( ( $i + 1 ) * $Step ), -2 )
179
+my $ua = LWP::UserAgent->new( keep_alive => 6 );
180
+
181
+sub loadConfig {
182
+ my $file = file(__FILE__)->absolute->dir . '/../conf/config.yml';
183
+ my $conf = LoadFile($file) or die("${file}: $!");
184
+ $conf->{'PageLabels'} = makePageLabels( $conf->{'PageStep'} );
185
+ return $conf;
95 186
}
96
-$PageLabels{ 0 } = 'Main, ' . $PageLabels{ 0 };
97
-my $PageMax = @{ [ sort { $b <=> $a } ( keys( %PageLabels ) ) ] }[ 0 ];
98
-$PageLabels{ $PageMax } =~ s/\d+$/99/;
99 187
100
-my $q = new CGI;
101
-$q->charset('utf-8');
102
-my $cgipath = $q->url( -absolute => 1 );
103
-
104
-my $URI = $q->param( 'URI' ) || '';
105
-my $ASIN = '';
106
-unless ( $ASIN = $q->param( 'ASIN' ) ){
107
- # Perl 5.10
108
- $URI =~ /\/(dp|ASIN|product)\/(?'asin'[\w]+)/;
109
- $ASIN = $+{asin};
110
- # Perl 5.8
111
- #$URI =~ /\/(dp|ASIN|product)\/([\w]+)/;
112
- #$ASIN = $2;
188
+sub startsWith {
189
+ my $text = shift or return;
190
+ my $prefix = shift or return;
191
+ return $prefix eq substr( $text, 0, length($prefix) );
192
+}
193
+
194
+sub makePageLabels {
195
+ my $step = shift || 20;
196
+ my @pageLabels = map {
197
+ 'PT'
198
+ . substr( '0' . ( $_ * $step + 1 ), -2 ) . '-PT'
199
+ . substr( '0' . ( ( $_ + 1 ) * $step ), -2 )
200
+ } ( 0 .. ( 99 / $step ) );
201
+ $pageLabels[0] = 'Main, ' . $pageLabels[0];
202
+ $pageLabels[$#pageLabels] =~ s/\d+$/99/;
203
+ return [@pageLabels];
204
+}
205
+
206
+sub checkArgs {
207
+ my $conf = shift or return;
208
+ my $uri = shift || '';
209
+ my $asin = shift || '';
210
+ my $shotType = shift || 0;
211
+ my $page = shift || 0;
212
+ my $mod = !!shift;
213
+ if ( !$asin ) {
214
+ $uri =~ /\/(dp|ASIN|product)\/(?<asin>[\w]+)/;
215
+ $asin = $+{asin};
216
+ }
217
+ if ($asin) {
218
+ $uri = $conf->{'DetailUri'};
219
+ $uri =~ s/\{ASIN\}/$asin/;
220
+ }
221
+ if ( $shotType < 0 || $#{ $conf->{'VariableShots'} } < $shotType ) {
222
+ $shotType = 0;
223
+ }
224
+ if ( $page < 0 || $#{ $conf->{'PageLabels'} } < $page ) {
225
+ $page = 0;
226
+ }
227
+ return {
228
+ URI => $uri,
229
+ ASIN => $asin,
230
+ ShotType => $shotType,
231
+ Page => $page,
232
+ Mod => $mod,
233
+ };
234
+}
235
+
236
+sub makeShots {
237
+ my $variableShots = shift or return;
238
+ my $index = 0;
239
+ return [ map { { value => $index++, label => $_->{'Name'}, }; } @{$variableShots} ];
113 240
}
114
-if ( $ASIN ){
115
- $URI = $DetailUri;
116
- $URI =~ s/%ASIN%/$ASIN/;
241
+
242
+sub makePages {
243
+ my $pageLabels = shift or return;
244
+ my $index = 0;
245
+ return [ map { { value => $index++, label => $_, }; } @{$pageLabels} ];
117 246
}
118
-my $SHOT = ( $q->param( 'SHOT' ) ) ? 1 : 0;
119
-my $shottype = $ShotsVarList[ $SHOT ];
120 247
121
-my $PAGE = $q->param( 'PAGE' ) || 0;
122
-if ( $PAGE < 0 || $PageMax < $PAGE ){
123
- $PAGE = 0;
248
+sub getDetail {
249
+ my $conf = shift or return;
250
+ my $uri = shift or return;
251
+ my $r = $ua->get($uri);
252
+ my $html = $r->decoded_content;
253
+ $html =~ /<meta\scontent="(?<content>[^"]+)"\s+name="description"\s*\/>/;
254
+ my $title = $+{'content'};
255
+ $title =~ s/^Amazon\.\S+\s*//i;
256
+ $title = katakana_h2z($title);
257
+ return {
258
+ Title => $title || 'No Title',
259
+ URI => $uri . '?tag=' . $conf->{'AWS'}{'ASSOCIATE_TAG'},
260
+ };
124 261
}
125 262
126
-my $MOD = $q->param( 'MOD' ) || 0;
127
-
128
-my $ua = LWP::UserAgent->new( keep_alive => ( 7 + $Step ) * 3 );
129
-
130
-my @Anchors = ();
131
-
132
-printHeader();
133
-
134
-if ( !$ASIN || $MOD ){
135
- print $q->start_form( -action => $q->url ),
136
- $q->table( { -summary => 'enter ASIN and so on.', -border => 1 },
137
- $q->Tr(
138
- $q->th( 'ASIN' ),
139
- $q->td( $q->textfield( -name => 'ASIN', -value => $ASIN ) )
140
- ),
141
- $q->Tr(
142
- $q->th( 'URI' ),
143
- $q->td( $q->textfield( -name => 'URI', -value => $URI, -size => 80 ) )
144
- ),
145
- $q->Tr( $q->th( 'SHOT' ), $q->td( $q->popup_menu(
146
- -name => 'SHOT',
147
- -values => [sort {$a<=>$b} keys(%ShotsTypeList)],
148
- -default => $SHOT,
149
- -labels => \%ShotsTypeList,
150
- ) ) ),
151
- $q->Tr( $q->th( 'PAGE' ), $q->td( $q->popup_menu(
152
- -name => 'PAGE',
153
- -values => [sort {$a<=>$b} keys(%PageLabels)],
154
- -default => $PAGE,
155
- -labels => \%PageLabels,
156
- ) ) ),
157
- $q->Tr( $q->td(
158
- { -colspan => 2, -align => 'center' },
159
- $q->submit, " ", $q->reset
160
- ) ),
161
- ),
162
- $q->end_form;
163
-} else {
164
- my $u = URI::Amazon::APA->new( 'http://ecs.amazonaws.jp/onca/xml' );
165
- $u->query_form(
166
- Service => 'AWSECommerceService',
167
- Operation => 'ItemLookup',
168
- ResponseGroup => 'ItemAttributes,Images',
169
- AssociateTag => $conf->{'ASSOCIATE_TAG'},
170
- ItemId => $ASIN,
171
- );
172
- $u->sign(
173
- key => $conf->{'AWS_ACCESS_KEY_ID'},
174
- secret => $conf->{'SECRET_ACCESS_KEY'},
175
- );
176
-
177
- my $r = $ua->get($u);
178
- if ( $r->is_success ){
179
- print $q->h2( 'Bibliography' );
180
- my $content = XMLin(
181
- $r->content,
182
- ForceArray => [
183
- 'Item', 'Error', 'DetailPageURL', 'SalesRank', 'ProductGroup', 'Title',
184
- ],
185
- KeyAttr => { 'Item' => '+ASIN', },
186
- );
187
- my( $title, $detailpageurl, $customerimage );
188
- if ( my $errors = $content->{'Items'}->{'Request'}->{'Errors'}->{'Error'} ){
189
- printErrors( $errors );
190
- } else {
191
- my $items = $content->{'Items'}->{'Item'};
192
- my $item = $items->{$ASIN};
193
- $title = $item->{'ItemAttributes'}->{'Title'};
194
- $detailpageurl = $item->{'DetailPageURL'}[0];
195
- $detailpageurl = decode( 'utf8', uri_unescape( $detailpageurl ) );
196
- # $detailpageurl =~ s#/[^/]+/(?=dp/)#/#;
197
- # $detailpageurl = uri_unescape( $detailpageurl );
198
- $customerimage = $CustomerImageUri;
199
- $customerimage =~ s/%ASIN%/$ASIN/;
200
- }
201
-
202
- my $pagelabel = $PageLabels{ $PAGE };
203
- $pagelabel =~ s/PT/$ShotsVarList[ $SHOT ]/g;
204
- print $q->table( { -summary => 'Bibliography', -border => 1 },
205
- ( $title ) ? $q->Tr(
206
- $q->th( 'TITLE' ) ,
207
- $q->td(
208
- $q->a(
209
- { -href => $detailpageurl || $URI . $conf->{'ASSOCIATE_TAG'} },
210
- $title
211
- ), $q->br(),
212
- $q->a( { -href => $customerimage }, 'Customer Image' )
213
- )
214
- ) : '',
215
- $q->Tr( $q->th( 'ASIN' ), $q->td( $ASIN ) ),
216
- $q->Tr( $q->th( 'SHOT' ), $q->td( $ShotsTypeList{ $SHOT } ) ),
217
- $q->Tr( $q->th( 'PAGE' ), $q->td( $pagelabel ) ),
218
- );
219
-
220
- print $q->h2( 'Search Results' );
221
- $ImageUriBase =~ s/%ASIN%/$ASIN/;
222
- print $q->p( { -id => 'status' }, 'Now searching...' );
223
- if ( $PAGE == 0 ){
224
- foreach my $shot ( @ShotsFixList ){
225
- my $ImageUri = $ImageUriBase;
226
- my $shotkey = $shot->{'key'};
227
- $ImageUri =~ s/%SHOT%/$shotkey/;
228
- searchSize( $ImageUri, $shot->{'name'} );
229
- }
230
- }
231
- for( my $i=$PAGE*$Step; $i<($PAGE+1)*$Step && $i<99; ++$i ){
232
- my $ImageUri = $ImageUriBase;
233
- my $shotkey = $shottype . substr( '0' . ( $i + 1 ), -2 );
234
- $ImageUri =~ s/%SHOT%/$shotkey/;
235
- searchSize( $ImageUri, $shotkey );
236
- }
237
- if ( @Anchors ){
238
- print $q->h2( $q->a( { -name => 'INDEX', -id => 'INDEX' }, 'Index' ) ),
239
- $q->start_ol(),
240
- $q->li( [ map { $q->a( { -href => '#' . $_ }, $_ ) } @Anchors ] ),
241
- $q->end_ol();
242
- } else {
243
- print $q->p( $q->img(
244
- { -src => $NoImageUri, -title => 'No Image', -alt => 'No Image' }
245
- ) );
246
- }
247
- print $q->p( $q->a( { -href => '#top', -name => 'bottom' }, 'Page Top' ) );
248
- } else {
249
- print $q->p( $r->status_line );
250
- }
263
+sub makeAwsUri {
264
+ my $conf = shift or return;
265
+ my $opt = shift or return;
266
+ my $aws = $conf->{'AWS'};
267
+ my $u = URI::Amazon::APA->new( $aws->{'SERVICE_URI'} );
268
+ $u->query_form(
269
+ Service => 'AWSECommerceService',
270
+ AssociateTag => $aws->{'ASSOCIATE_TAG'},
271
+ %{$opt},
272
+ );
273
+ $u->sign(
274
+ key => $aws->{'AWS_ACCESS_KEY_ID'},
275
+ secret => $aws->{'SECRET_ACCESS_KEY'},
276
+ );
277
+ return $u;
251 278
}
252 279
253
-printFooter();
254
-
255
-exit;
256
-
257
-sub printHeader
258
-{
259
- my $jscript = qq{
260
- function changeStatus(){
261
- document.getElementById('status').innerHTML = 'Finished.'
262
- + ( ( document.getElementById( 'INDEX' ) )
263
- ? '<br><a href=\"#INDEX\">Index<'+'/a>'
264
- : '' );
265
- }
266
- };
267
- print $q->header(),
268
- $q->start_html(
269
- -title => $cginame,
270
- -lang => 'ja-JP',
271
- -head => [
272
- $q->meta( { -http_equiv=>'Content-style-type', -content=>"text/css" } ),
273
- $q->meta( { -http_equiv=>'Content-script-type', -content=>"text/javascript" } ),
274
- ],
275
- -style => { 'src'=>'/take.css' },
276
- -script => $jscript,
277
- -onLoad => 'changeStatus()',
278
- ),
279
- $q->h1( $q->a( { -name => 'top' }, $cginame ) ),
280
- $q->ul(
281
- $q->li( $q->a( { -href => $cgipath }, 'CGI Top' ) ),
282
- ( $ASIN && !$MOD )
283
- ? $q->li( [ $q->a( {
284
- -href => "$cgipath?ASIN=$ASIN&SHOT=$SHOT&PAGE=$PAGE&MOD=1&URI="
285
- . uri_escape_utf8( $URI )
286
- }, 'Modify' ), ] )
287
- : '',
288
- );
280
+sub requestAws {
281
+ my $conf = shift or return;
282
+ my $opt = shift or return;
283
+ my $r = $ua->get( makeAwsUri( $conf, $opt ) );
284
+ return {
285
+ IsSuccess => $r->is_success,
286
+ Status => $r->status_line,
287
+ Content => XMLin(
288
+ $r->decoded_content,
289
+ ForceArray =>
290
+ [ 'Item', 'Error', 'DetailPageURL', 'SalesRank', 'ProductGroup', 'Title', ],
291
+ KeyAttr => { 'Item' => '+ASIN', },
292
+ ),
293
+ };
289 294
}
290 295
291
-sub printFooter
292
-{
293
- print $q->end_html . "\n";
296
+sub searchImages {
297
+ my $conf = shift or return;
298
+ my $args = shift or return;
299
+ if ( !$args->{'ASIN'} ) {
300
+ return;
301
+ }
302
+ my @images = ();
303
+ my $uriBase = $conf->{'ImageUriBase'};
304
+ $uriBase =~ s/\{ASIN\}/$args->{'ASIN'}/e;
305
+ if ( $args->{'Page'} == 0 ) {
306
+ push( @images, searchForShots( $conf, $uriBase, $conf->{'FixShots'} ) );
307
+ }
308
+ my @shots = map {
309
+ my $shot
310
+ = $conf->{'VariableShots'}[ $args->{'ShotType'} ]{'Key'}
311
+ . substr( '0' . ( $args->{'Page'} * $conf->{'PageStep'} + $_ ), -2 );
312
+ { Name => $shot, Key => $shot, };
313
+ } ( 1 .. $conf->{'PageStep'} );
314
+ push( @images, searchForShots( $conf, $uriBase, \@shots ) );
315
+ return [@images];
294 316
}
295 317
296
-sub printErrors
297
-{
298
- my( $errors ) = @_;
299
-# print $q->h2( 'Error' );
300
- print $q->start_table( { -summary => 'Error Message', -border => 1 } );
301
- foreach my $error ( @{$errors} ){
302
- print $q->Tr( $q->th( $error->{'Code'} ), $q->td( $error->{'Message'} ) );
303
- }
304
- print $q->end_table();
318
+sub searchForShots {
319
+ my $conf = shift or return;
320
+ my $uriBase = shift or return;
321
+ my $shots = shift or return;
322
+ my @images = ();
323
+ foreach my $shot ( @{$shots} ) {
324
+ my $uri = $uriBase;
325
+ $uri =~ s/\{SHOT\}/$shot->{'Key'}/e;
326
+ if ( my $image = searchForSizes( $conf, $uri, $shot->{'Name'} ) ) {
327
+ push( @images, $image );
328
+ }
329
+ }
330
+ return @images;
305 331
}
306 332
307
-sub searchSize
308
-{
309
- my( $ImageUriBase, $Shot ) = @_;
310
- foreach my $size ( @SizeList ){
311
- my $ImageUri = $ImageUriBase;
312
- my $sizekey = $size->{'key'};
313
- $ImageUri =~ s/%SIZE%/$sizekey/;
314
- my $title = $Shot . '_' . $size->{'name'};
315
- #print $title . ': ';
316
- my $r = $ua->request( HTTP::Request->new( HEAD => $ImageUri ) );
317
- if ( $r->header( 'content-type' ) eq 'image/jpeg' ){
318
- #print 'Exist.' . $q->br();
319
- print $q->p(
320
- $title, $q->a( { -name => $title, -href => '#INDEX' }, '*' ), $q->br(),
321
- $q->img( { -src => $ImageUri, -title => $title, -alt => $title, } )
322
- );
323
- push( @Anchors, $title );
324
- last;
325
- } else {
326
- #print 'Not exist.' . $q->br();
327
- }
328
- }
333
+sub searchForSizes {
334
+ my $conf = shift or return;
335
+ my $uriBase = shift or return;
336
+ my $shot = shift or return;
337
+ foreach my $size ( @{ $conf->{'Sizes'} } ) {
338
+ my $uri = $uriBase;
339
+ $uri =~ s/\{SIZE\}/$size->{'Key'}/e;
340
+ my $r = $ua->get($uri);
341
+ if ( $r->is_success && $r->header('content-type') eq 'image/jpeg' ) {
342
+ return { Title => $shot . '_' . $size->{'Name'}, Uri => $uri, };
343
+ }
344
+ }
345
+ return;
329 346
}
330 347
331
-# EOF
348
+1;
349
+```
350
+
351
+- templates/form.html
352
+```html
353
+<!DOCTYPE html>
354
+<html lang="ja">
355
+
356
+<head>
357
+ <meta charset="UTF-8">
358
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
359
+ <meta http-equiv="X-UA-Compatible" content="ie=edge">
360
+ <link rel="stylesheet" type="text/css" href="/take.css" />
361
+ <link rel="stylesheet" type="text/css" href="/Amazon.css" />
362
+ <title>Amazon getImages-JP</title>
363
+</head>
364
+
365
+<body>
366
+ <h1><a name="top" href="[% urlCgiTop %]">Amazon getImages-JP</a></h1>
367
+ <div>
368
+ <form method="POST" action="[% urlCgiTop %]" enctype="application/x-www-form-urlencoded">
369
+ <table>
370
+ <tbody>
371
+ [% IF detail %]
372
+ <tr>
373
+ <th>TITLE</th>
374
+ <td><a href="[% detail.URI %]" target="_blank">[% detail.Title %]</a></td>
375
+ </tr>
376
+ [% END %]
377
+ <tr>
378
+ <th>ASIN</th>
379
+ <td><input name="ASIN" type="text" value="[% args.ASIN %]"></td>
380
+ </tr>
381
+ <tr>
382
+ <th>URI</th>
383
+ <td><input name="URI" type="text" size="50" value="[% args.URI %]"></td>
384
+ </tr>
385
+ <tr>
386
+ <th>SHOT</th>
387
+ <td>
388
+ <select name="SHOT">
389
+ [% FOREACH shot IN shots %]
390
+ <option value="[% shot.value %]" [% IF shot.value==args.ShotType %] selected [% END %]>[% shot.label %]
391
+ </option>
392
+ [% END %]
393
+ </select>
394
+ </td>
395
+ </tr>
396
+ <tr>
397
+ <th>PAGE</th>
398
+ <td>
399
+ <select name="PAGE">
400
+ [% FOREACH page IN pages %]
401
+ <option value="[% page.value %]" [% IF page.value==args.Page %] selected [% END %]>[% page.label %]
402
+ </option>
403
+ [% END %]
404
+ </select>
405
+ </td>
406
+ </tr>
407
+ <tr>
408
+ <td colspan="2">
409
+ <input name=".submit" type="submit">
410
+ <input name=".reset" type="reset">
411
+ </td>
412
+ </tr>
413
+ </tbody>
414
+ </table>
415
+ </form>
416
+ </div>
417
+ [% IF images %]
418
+ <div>
419
+ [% FOREACH image IN images %]
420
+ <h2><a href="#top" name="[% image.Title %]">[% image.Title %]</a></h2>
421
+ <img alt="[% image.Title %]" title="[% image.Title %]" src="[% image.Uri %]">
422
+ [% END %]
423
+ </div>
424
+ <div class="panelIndex">
425
+ <ol class="default">
426
+ [% FOREACH image IN images %]
427
+ <li><a href="#[% image.Title %]">[% image.Title %]</a></li>
428
+ [% END %]
429
+ </ol>
430
+ <p><a href="#top">Top</a></p>
431
+ </div>
432
+ [% END %]
433
+</body>
434
+
435
+</html>
436
+```
437
+
438
+- Amazon.css
439
+```css
440
+/* Amazon.css */
441
+
442
+.default {
443
+ text-align: initial;
444
+}
445
+
446
+.panelIndex {
447
+ position: fixed;
448
+ top: 0em;
449
+ right: 0em;
450
+ padding: 0em 0.5em 0em 0em;
451
+ text-align: right;
452
+ background-color: #d0d0f0;
453
+ border: solid;
454
+ border-width: thin;
455
+}
332 456
```
333 457
334 458
# リンク
... ...
@@ -343,4 +467,4 @@ sub searchSize
343 467
344 468
- [CPAN:URI-Amazon-APA](http://search.cpan.org/dist/URI-Amazon-APA)
345 469
- [CPAN:CGI](http://search.cpan.org/dist/CGI)
346
-- [CPAN:URI](http://search.cpan.org/dist/URI) / [CPAN:URI/URI/Escape.pm](http://search.cpan.org/dist/URI/URI/Escape.pm)
347 470
\ No newline at end of file
471
+- [CPAN:URI](http://search.cpan.org/dist/URI) / [CPAN:URI/URI/Escape.pm](http://search.cpan.org/dist/URI/URI/Escape.pm)