Hatena::ブログ(Diary)

サンプルコードによるPerl入門 このページをアンテナに追加 RSSフィード

  サンプルコード中心のPerlの入門サイトです。現代的なPerl(v5.8.1以降)の書き方に準拠しています。モダンPerlを効率よく体系的に学習することができます。社内での勉強会や講習のテキストとしてもお使いください。またPerlの初学者の方にもぜひ教えてあげてください。Perlは実用的、高速で、業務を大幅に効率化できる、とても優れたプログラミング言語です。ぜひお役立てください。

2008-10-11

Google Calendar Data API で祝日を取得する

 祝日というのは人間が決めたものなので非常に扱いにくいものです。春分秋分という前年の2月に決まる祝日や、振替休日などもあります。

 今回のサンプルではGoogleがGoogle Calendar Data APIで提供してくれている日本の祝日のデータを取得することにします。

 あくまでGoogleが調べた日本の休日のデータなので絶対的な信頼性はありません。Googleが間違っていれば間違った祝日のデータを取得しますので、日付の間違いが損害を及ぼすようなアプリケーションにそのまま使用してはいけません。かならず他の手段で正しいことを確かめるようにします。

use strict;
use warnings;
use LWP::UserAgent; # Web上から情報を取得するのでHTTPクライアントを使用する。
use XML::Simple; #フィードがXMLで与えられるのでXML::Simpleで解析。

# Google Calendar Data API のURL( 日本の祝日を取得するためのURL )
my $feed_base = 'http://www.google.com/calendar/feeds/japanese@holiday.calendar.google.com/public/full';

# フィードに与える情報
my $start_date = '2007-01-01'; # この日を含む開始日
my $end_date = '2008-01-01'; # この日を含まない終了日
my $max_results = 100; # フィードの最大取得件数

my $query = "start-min=${start_date}&start-max=${end_date}&max-results=${max_results}";

my $feed_url = $feed_base . '?' . $query;

# WebからXMLを取得
my $http_client = LWP::UserAgent->new;
my $response = $http_client->get( $feed_url );

my $holidays_xml;
if( $response->is_success ){
    $holidays_xml = $response->content;
}
else{
    die "フィードの取得に失敗しました。";
}

# XMLを解析
my $xml_parser = XML::Simple->new();
$XML::Simple::PREFERRED_PARSER = 'XML::Parser'; # XML::SAXがない人のため
                                                # パーサを変更
my $holidays_data = $xml_parser->XMLin( $holidays_xml );

my $holidays = [];
foreach my $key ( keys %{ $holidays_data->{ 'entry' } } ){
    
    my $holiday_day = $holidays_data->{ entry }{ $key }{ 'gd:when' }{ startTime };
    my $holiday_name =$holidays_data->{ entry }{ $key }{title}{content};
    push @{ $holidays }, { day => $holiday_day, name => $holiday_name };
}

# 文字コードがPerlの内部表現( UTF8 )になっているのでお好みに応じてエンコード
use Encode;
my $encode_to;
if( $^O eq 'MSWin32' ){
    $encode_to = 'shift-jis';
}
else{
    $encode_to = 'utf8'; # お好みに応じて
}

# データすべてをエンコード
foreach my $holiday ( @{ $holidays } ){
    foreach my $key ( keys %{ $holiday } ){
        $holiday->{$key} = encode( $encode_to, $holiday->{$key} );
    }
}

use Data::Dumper;
print Data::Dumper->Dump( [ $holidays ], [ qw( *holidays ) ] );

__END__

祝日の問題

 祝日は国によってことなります。また祝日が決まっても振り替え休日で休日が変化することもあります。春分や秋分などのように政府が前年の2月に正式な祝日の日を決定するものもあります。

Google Calender Data API

 Google Calender Data API を使用すると、国民の休日を取得することができます。振替休日も考慮されています。信頼性はGoogleの信頼性によります。

コード解説

(1)Google Calender Data API

 以下のURLがGoogle Calender Data API の祝日取得のためのURLです。

my $feed_base = 'http://www.google.com/calendar/feeds/japanese@holiday.calendar.google.com/public/full';
(2)クエリ文字列の作成

 上記の元になるURLに条件を指定したクエリ文字列を連結します。指定する日付は開始日を含み、終了日を含まないものになります。

# フィードに与える情報
my $start_date = '2007-01-01'; # この日を含む開始日
my $end_date = '2008-01-01'; # この日を含まない終了日
my $max_results = 100; # フィードの最大取得件数

my $query = "start-min=${start_date}&start-max=${end_date}&max-results=${max_results}";

my $feed_url = $feed_base . '?' . $query;

 このサンプルでは以下のURLを作成しています。

  http://www.google.com/calendar/feeds/japanese@holiday.calendar.google.com/public/full?start-min=2007-01-01&start-max=2008-01-01&max-results=100

 あとは上記URLの情報をWebから取得して、XMLデータなのでこれを解析して使いやすい形にして、OSに応じて文字コードをエンコードします。

(3)WebからXMLデータを取得する。

 LWP::UserAgentは、HTTPクライアントを作成するためのモジュールです。Webブラウザがやっていることをプログラム上で行えます。

use LWP::UserAgent; # Web上から情報を取得するのでHTTPクライアントを使用する。
# WebからXMLを取得
my $http_client = LWP::UserAgent->new;
my $response = $http_client->get( $feed_url );

my $holidays_xml;
if( $response->is_success ){
    $holidays_xml = $response->content;
}
else{
    die "フィードの取得に失敗しました。";
}

(4)XMLを解析して使いやすいデータにする。

 XMLを解析するにはXML::Simpleを使用します。XML::SimpleはXMLデータをPerlのデータ(ハッシュ、配列)に変換するためのモジュールです。

use XML::Simple; #フィードがXMLで与えられるのでXML::Simpleで解析

# XMLを解析
my $xml_parser = XML::Simple->new();
$XML::Simple::PREFERRED_PARSER = 'XML::Parser'; # XML::SAXがない人のため
                                                # パーサを変更
my $holidays_data = $xml_parser->XMLin( $holidays_xml );

my $holidays = [];
foreach my $key ( keys %{ $holidays_data->{ 'entry' } } ){
    
    my $holiday_day = $holidays_data->{ entry }{ $key }{'gd:when'}{ startTime };
    my $holiday_name = $holidays_data->{ entry }{ $key }{ title }{ content };
    push @{ $holidays }, { day => $holiday_day, name => $holiday_name };
}

 XML変換後はデータに以下のようにしてアクセスできます。

# 祝日の日付
$holidays_data->{ 'entry' }{ 識別子となるURL }{'gd:when'}{ startTime };

# 祝日の名前
$holidays_data->{ entry }{ 識別子となるURL }{ title }{ content };

(5)文字コードを変換する。

 Webから受け取ったXMLの文字コードはUTF-8です。XML::SimpleがXMLをPerlのデータに変換した時点で、文字はPerlの内部表現( UTF-8 )に変換されます。

 これを正しく出力するためには、Perlの内部表現から適切な文字コードにエンコードする必要があります。

 (出力する文字列がたとえUTF-8であろうともエンコードは必要です。内部表現UTF-8から外部表現UTF-8に変換すると考えると理解しやすいです。公式には「UTF-8フラグを落とす」といいますがわかりにくい)

use Encode;
my $encode_to;
if( $^O eq 'MSWin32' ){
    $encode_to = 'shift-jis';
}
else{
    $encode_to = 'utf8'; # OSの標準の文字コードにおうじて変更
}

# データすべてをエンコード
foreach my $holiday ( @{ $holidays } ){
    foreach my $key ( keys %{ $holiday } ){
        $holiday->{$key} = encode( $encode_to, $holiday->{$key} );
    }
}

 

ひろじぃひろじぃ 2009/03/09 23:01 はじめまして。
参考にさせていただいてます。

今一箇所詰まってるところがあってコメントさせてもらいました。

ある期間の祝日リストを取得するのではなく、ある日が祝日なのかそうでないのかを判定するスクリプトを書こうとしています。
で、とりあえずコードは全く変えず

my $start_date = '2009-03-20'; # この日を含む開始日
my $end_date = '2008-03-21'; # この日を含まない終了日

として実行すると、本来取れる結果が取れません。
print $holidays_data するとフィードは1entry取れてることを確認しているんですが、
$key に識別子となるURLがうまく取れてないようで
$holiday_day も $holiday_name もイニシャライズ出来ないと言われてしまいます。

何かアドバイスいただけませんでしょうか。

perlcodesampleperlcodesample 2009/03/12 13:17 my $start_date = '2009-03-20'; # この日を含む開始日
my $end_date = '2008-03-21'; # この日を含まない終了日

終了日が2008年の3月21日になってますが、2009年が正しいのじゃないでしょうか?

なまえなまえ 2009/03/18 15:50 >perlcodesampleさん

レスありがとうございます。
すみません、記載ミスです。

誤)
my $end_date = '2008-03-21'; # この日を含まない終了日
正)
my $end_date = '2009-03-21'; # この日を含まない終了日

これでもうまく取得出来ません。

ただ、単一日を取得する場合

$holidays_data->{ 'entry' }{ 識別子となるURL }{'gd:when'}{ startTime };

$holidays_data->{ 'entry' }{'gd:when'}{ startTime };

としたら上手く出来ました。
既に一意になっている場合は識別子となるURLは邪魔なようです。

判断入れて使い分けるしかないみたいですね。。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証