Adobe Creative Suite 出力対応店一覧をGoogle Mapsで表示させる(19)
スポンサードリンク
アドビのCS5対応プリントショップのリストのページから、Google Mapsに表示させるやつの19回目。
前回は、HTMLをスクレーピングして得られる住所から、Google Maps APIのジオコーダに問い合わせするところを、辞書にしてしまい、Googleのお世話になる頻度を少なくし、高速にレスポンスを返せられるようにしました。
今回は、Webブラウザの要求に応じてジオコーディングした結果をJSONで用意するCGIスクリプトを用意します。
アドビのサイトに掲載されている企業の所在地をGoogle Mapsに表示するためには、Webブラウザで動いているJavaScriptに所在地を伝えなくてはならないのですが、JavaScriptで外部からデータを取得するときに便利なのが、JSONというシリアライズ形式です。
前回の例で言うと、
なまえ: もょもと
LV: 48
HP: 229
MP: 0
G: 27671
EX: 942197
STR: 155
DEX: 130
ATK: 155
DEF: 65
↓JSON形式でシリアライズ
{
"name":"もょもと",
"LV":48,
"MP":0,
"HP":229,
"G":27671,
"EX":942197,
"STR":155,
"DEX":130,
"ATK":155,
"DEF":65
}
となります。実際にはこんな感じでJSON形式にシリアライズします。
Filename: test22.pl
use strict; use warnings; use utf8; use JSON; my $param = { name => 'もょもと', LV => 48, HP => 229, MP => 0, G => 27671, EX => 942197, STR => 155, DEX => 130, ATK => 155, DEF => 65, }; print encode_json( $param )."\n"; exit __END__
実行結果:
$ perl test22.pl {"STR":155,"EX":942197,"ATK":155,"DEF":65,"name":"もょもと","MP":0,"LV":48,"G":27671,"HP":229,"DEX":130} $
さて、Webブラウザに適したJSON形式の出力の方法を確認しましたので、実際にWebブラウザからCGIが呼ばれた時に、JSON形式で値を渡すCGIスクリプトを用意します。
CGIとしては、「1時間以上間隔の開いたリクエストのときだけアドビのウェブサイトのHTMLを取得する」「アドビのウェブサイトのHTMLを取得したときデータ量を測定し、前回の測定から増減していない場合は、前回のデータをそのまま出す」などの機能が組み込まれています。
CGIなので、パーミッションの設定などは適切に行ってください。
#!/usr/bin/perl # Filename: printshop.cgi use strict; use warnings; use utf8; use CGI; use Encode; use FindBin; use JSON; use Time::HiRes qw(sleep); use URI::Fetch; use Web::Scraper; use WebService::Simple; use Data::Dumper; use CGI::Carp qw(fatalsToBrowser); my $WAIT = 0.6; # GETしてきたコンテンツの長さを記録するファイル my $cwd = $FindBin::RealBin; # カレントディレクトリ my $length_file = "$cwd/length.txt"; # GETするコンテンツのURL(STATUSが常に200なので # 内容変更調べるにはコンテンツの大きさを使う) my $url= 'http://www.adobe.com/jp/print/printshop/'; my $jsonfilename = 'printshop.json'; my $jsonfile = "$cwd/$jsonfilename"; my $redirect_url = "http://labo.dtpwiki.jp/printshop/$jsonfilename"; my $cachefile = "$cwd/googlemaps.dump"; my $q = CGI->new; # Adobeサイトでの表記揺れをGoogle用に置き換える辞書 my %dict = ( '岐阜県岐阜市長良福田町1-30' => '岐阜県岐阜市福田町1-30', '京都府京都市下京区烏丸松原下ル五条烏丸町407-2烏丸KT第2ビル2F' => '京都府京都市下京区五条烏丸町407-2', ); # Google Maps API ジオコーダの返答を記録している辞書をファイルから読み込み my $dict2 = {}; if ( open my $fh, '<', $cachefile) { my $dump = decode_utf8(do { local $/; <$fh> }); close $fh; $dict2 = eval $dump; } # ジオコーダ呼び出しオブジェクト準備 my $geo = WebService::Simple->new( response_parser => 'JSON', base_url => 'http://maps.google.com/maps/api/geocode/', param => { region => 'jp', language => 'ja', sensor => 'false', }, ); # main my $html = decode_utf8( get_html($url) ); if ($html) { # modified -> スクレーピング->ジオコーディング->JSON生成 # スクレーピング用オブジェクト準備 my $s = scraper { process '//div[@class="tabcontent"][1]//table[@class="data-bordered max"]/tbody/tr', 'codes[]' => scraper { process '//td[1]', pref => [ 'TEXT', sub { s/\s//g; } ]; process '//td[2]', company => [ 'TEXT', sub { s/\s//g; } ]; process '//td[2]//a', url => [ '@href', sub { s/\s//g; } ]; process '//td[3]', address => [ 'TEXT', sub { s/\s//g; s/TEL:.+$//i; return $_; } ]; process '//td[3]', phone => [ 'TEXT', sub { s/\s//g; if(m/TEL:([\d-]+)/i){return $1;};}]; process '//td[3]', fax => [ 'TEXT', sub { s/\s//g; s/:/:/g; if(m/FAX:([\d-]+)/i){return $1;};undef;}]; process '//td[4]', illustratorcs5 => [ 'TEXT', sub { return $_ =~m|yes|i ? 1 : undef; } ]; process '//td[5]', indesigncs5 => [ 'TEXT', sub { return $_ =~m|yes|i ? 1 : undef; } ]; process '//td[6]', pdfx1a => [ 'TEXT', sub { return $_ =~m|yes|i ? 1 : undef; } ]; process '//td[7]', pdfx4 => [ 'TEXT', sub { return $_ =~m|yes|i ? 1 : undef; } ]; }; }; # スクレーピング my $scraped = $s->scrape($html); my $data = []; # スクレーピング結果を一つずつ取り出し処理 foreach my $item ( @{$scraped->{codes}} ) { my $address = $item->{address}; my $res = geo_get($address); $item->{location} = $res; push @$data, $item; } # JSON書き出し my $json = encode_json($data); { open my $fh, '>', $jsonfile or die $!; print $fh $json; close $fh; } # CGI出力 print $q->header( -type=> 'text/plain', -charset => 'UTF-8', ); print $json; } else { # not modified ->JSONファイルへredirect # CGI出力(リダイレクトヘッダ) print $q->redirect( $redirect_url ); } exit; sub get_html { my $url = shift; # length.txtから前回取得時のコンテンツの大きさを # 取得するよ my $length = 0; return undef if ( ( -e $length_file ) && ((-M $length_file) * 86400) < 3600); if ( -e $length_file ) { open my $fh, '<', $length_file or die $!; $length = <$fh>; close $fh; } # Webからコンテンツを取得するよ my $ua = new LWP::UserAgent; $ua->timeout(10); $ua->agent('DTPWiki.jp AdobeJpPrintShopMap/0.1 ' . '(+http://labo.dtpwiki.jp/printmap/)' ); my $res = URI::Fetch->fetch( $url, UserAgent => $ua, ) or die URI::Fetch->errstr; my $html = $res->content; # 今回取得したWebコンテンツの大きさを記録するよ my $length_new = length($html); { open my $fh, '>', $length_file or die $!; print $fh $length_new; close $fh; } # 前回と大きさが変わらない場合はundef、 # 変わっている場合はコンテンツを返すよ return ( $length == $length_new ? undef : $html ); } sub geo_get { my $address = shift; my $result; if ( !$dict2->{$address} ) { #print encode_utf8("キャッシュミス!\n"); my $res = $geo->get( 'json', { address => ( $dict{$address} or $address ) } ); my $val = $res->parse_response(); $result = $val->{results}->[0]->{geometry}->{location}; $dict2->{$address} = $result; local $Data::Dumper::Indent = 0; local $Data::Dumper::Terse = 1; open my $fh, '>', 'googlemaps.dump' or die $!; print $fh Dumper($dict2); close $fh; sleep $WAIT; } else { #print encode_utf8("キャッシュヒット!\n"); $result = $dict2->{$address}; } return $result; } __END__
どんなリザルトが帰ってくるか、というのは、実際に
http://labo.dtpwiki.jp/printshop/printshop.cgi
にアクセスしてみてください。
次回は、JSONが用意できましたので、やっとGoogle Mapsに表示していく、ということをやってみたいと思います。
投稿 大野 義貴 [GoogleMaps] | 固定リンク |
スポンサードリンク
トラックバック(0)
トラックバックURL: http://blog.dtpwiki.jp/MTOS/mt-tb.cgi/3430
コメントする