PerlならサンプルコードPerl入門

2008-10-11

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

  1. Perl
  2. 日付・時刻

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

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

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

use strict;
use warnings;

# Web上から情報を取得するのでHTTPクライアントを使用する。
use LWP::UserAgent;

#フィードがXMLで与えられるのでXML::Simpleで解析。
use 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::SAXがない人のためパーサを変更
$XML::Simple::PREFERRED_PARSER = 'XML::Parser'; 
my $holidays_data = $xml_parser->XMLin($holidays_xml);

my $holidays = [];
for 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';
}

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

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

祝日の問題

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

Google Calender Data API

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

コード解説

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';
クエリ文字列の作成

 上記の元になる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に応じて文字コードをエンコードします。

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

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

use LWP::UserAgent;
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を解析して使いやすいデータにする。

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

use XML::Simple;

# XMLを解析
my $xml_parser = XML::Simple->new();
$XML::Simple::PREFERRED_PARSER = 'XML::Parser';
my $holidays_data = $xml_parser->XMLin( $holidays_xml );

my $holidays = [];
for 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};
文字コードを変換する

 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';
}

# データすべてをエンコード
for my $holiday (@$holidays) {
  for 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は邪魔なようです。

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

投稿したコメントは管理者が承認するまで公開されません。

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


画像認証