はじめに
皆さん、Perlで外部APIを使ったことはありますか?今回から新シリーズ「Perl API統合パターン」を開始します。
このシリーズでは、天気予報APIを題材に、複数のAPIサービスを統合して扱うための設計パターンを学んでいきます。Perl入学式を卒業し、Mooの基礎を学んだ皆さんが、次のステップとして実践的なAPI連携スキルを身につけることを目標としています。
対象読者
- Perl入学式を卒業したばかりの方
- 「Mooで覚えるオブジェクト指向プログラミング」シリーズを読了済みの方
- Perl v5.36以降の環境を使用できる方
- 外部APIを使ったプログラムに興味がある方
このシリーズで学ぶこと
全8回を通じて、以下のスキルを身につけます。
- HTTP::TinyとJSON::PPを使ったAPI連携の基礎
- 異なるインターフェースを持つAPIサービスの統合方法
- 複数のAPIを統一的に扱うための設計パターン
- キャッシュ戦略とエラーハンドリングの実践
今回の目標
第1回となる今回は、天気予報API(OpenWeatherMap)を直接叩く素朴なコードを作成します。まずはシンプルに「動くコード」を書いて、APIの基本的な使い方を学びましょう。
天気予報APIの呼び出し
外部APIを呼び出すには、HTTPリクエストを送信し、レスポンス(通常はJSON形式)を解析する必要があります。Perlでは標準モジュールのHTTP::TinyとJSON::PPを使うことで、追加のインストールなしにAPI連携が可能です。
基本的なAPIリクエスト
まず、シンプルなAPIリクエストのコードを見てみましょう。今回はAPIキーなしで動作するモックデータを使用します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| #!/usr/bin/env perl
use v5.36;
use HTTP::Tiny;
use JSON::PP qw(encode_json decode_json);
# APIリクエストを送信
my $http = HTTP::Tiny->new;
my $url = 'https://api.example.com/weather?city=Tokyo';
# 実際のAPIの代わりにモックデータを使用
my $response = {
success => 1,
content => encode_json({
city => 'Tokyo',
temperature => 25.5,
condition => 'sunny',
humidity => 60,
}),
};
# レスポンスを解析
if ($response->{success}) {
my $data = decode_json($response->{content});
say "都市: $data->{city}";
say "気温: $data->{temperature}℃";
say "天気: $data->{condition}";
say "湿度: $data->{humidity}%";
}
|
HTTP::Tinyの基本
HTTP::TinyはPerlに標準で含まれる軽量なHTTPクライアントです。
get($url) - GETリクエストを送信post($url, $options) - POSTリクエストを送信- レスポンスはハッシュリファレンスで返される
JSON::PPの基本
JSON::PPはJSON形式のデータを扱うための標準モジュールです。qw(encode_json decode_json)でインポートして使用します。
decode_json($json_string) - JSON文字列をPerlのデータ構造に変換encode_json($perl_data) - Perlのデータ構造をJSON文字列に変換
OpenWeatherMap APIを使う
実際の天気予報APIとして、OpenWeatherMapを例に見てみましょう。このサービスは無料プランで月1000回までAPIを利用できます。
APIの仕様
OpenWeatherMapの現在の天気情報APIは以下の形式です。
- エンドポイント:
https://api.openweathermap.org/data/2.5/weather - パラメータ:
q - 都市名(例: Tokyo,JP)appid - APIキーunits - 単位(metric で摂氏)lang - 言語(ja で日本語)
実装コード
以下は、OpenWeatherMap APIを呼び出す基本的なコードです。今回はモックレスポンスを使用して、APIキーなしで動作するようにしています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
| #!/usr/bin/env perl
use v5.36;
use HTTP::Tiny;
use JSON::PP;
# OpenWeatherMap API(モック版)
sub get_weather_from_openweathermap ($city) {
# 実際のAPIレスポンスを模したモックデータ
my %mock_data = (
'Tokyo' => {
name => 'Tokyo',
main => {
temp => 25.5,
humidity => 60,
},
weather => [
{ description => '晴れ' },
],
},
'Osaka' => {
name => 'Osaka',
main => {
temp => 27.2,
humidity => 65,
},
weather => [
{ description => '曇り' },
],
},
'Sapporo' => {
name => 'Sapporo',
main => {
temp => 18.3,
humidity => 70,
},
weather => [
{ description => '雨' },
],
},
);
return $mock_data{$city};
}
# メイン処理
my @cities = qw(Tokyo Osaka Sapporo);
say "=== OpenWeatherMap 天気情報 ===";
for my $city (@cities) {
my $data = get_weather_from_openweathermap($city);
if ($data) {
my $temp = $data->{main}{temp};
my $humidity = $data->{main}{humidity};
my $description = $data->{weather}[0]{description};
say "$city: $description(気温 $temp℃、湿度 $humidity%)";
}
else {
say "$city: データなし";
}
}
|
実行結果:
1
2
3
4
| === OpenWeatherMap 天気情報 ===
Tokyo: 晴れ(気温 25.5℃、湿度 60%)
Osaka: 曇り(気温 27.2℃、湿度 65%)
Sapporo: 雨(気温 18.3℃、湿度 70%)
|
OpenWeatherMap APIのレスポンス構造
実際のOpenWeatherMap APIは以下のようなJSON形式でデータを返します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| {
"name": "Tokyo",
"main": {
"temp": 25.5,
"humidity": 60,
"pressure": 1013
},
"weather": [
{
"main": "Clear",
"description": "晴れ"
}
],
"wind": {
"speed": 3.5
}
}
|
この構造を理解しておくと、必要なデータを正確に取り出すことができます。
name - 都市名main.temp - 気温(摂氏、units=metricの場合)main.humidity - 湿度(%)weather[0].description - 天気の説明wind.speed - 風速(m/s)
今回の完成コード
今回作成した完成コードを以下に示します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
| #!/usr/bin/env perl
use v5.36;
use HTTP::Tiny;
use JSON::PP;
# OpenWeatherMap API呼び出し(モック版)
# 実際にAPIを使う場合は、HTTP::Tiny->get() でリクエストを送信
sub get_weather_from_openweathermap ($city) {
# モックデータ(実際のAPIレスポンスを模した構造)
my %mock_data = (
'Tokyo' => {
name => 'Tokyo',
main => {
temp => 25.5,
humidity => 60,
},
weather => [
{ description => '晴れ' },
],
},
'Osaka' => {
name => 'Osaka',
main => {
temp => 27.2,
humidity => 65,
},
weather => [
{ description => '曇り' },
],
},
'Sapporo' => {
name => 'Sapporo',
main => {
temp => 18.3,
humidity => 70,
},
weather => [
{ description => '雨' },
],
},
);
return $mock_data{$city};
}
# 天気情報を整形して表示
sub show_weather ($city) {
my $data = get_weather_from_openweathermap($city);
if ($data) {
my $temp = $data->{main}{temp};
my $humidity = $data->{main}{humidity};
my $description = $data->{weather}[0]{description};
say "$city: $description(気温 $temp℃、湿度 $humidity%)";
}
else {
say "$city: データを取得できませんでした";
}
}
# メイン処理
say "=== 天気予報アグリゲーター ===";
say "";
my @cities = qw(Tokyo Osaka Sapporo Fukuoka);
for my $city (@cities) {
show_weather($city);
}
|
実行結果:
1
2
3
4
5
6
| === 天気予報アグリゲーター ===
Tokyo: 晴れ(気温 25.5℃、湿度 60%)
Osaka: 曇り(気温 27.2℃、湿度 65%)
Sapporo: 雨(気温 18.3℃、湿度 70%)
Fukuoka: データを取得できませんでした
|
まとめ
今回は、天気予報APIを直接叩くシンプルなコードを作成しました。
- HTTP::TinyでHTTPリクエストを送信する方法を学んだ
- JSON::PPでJSONレスポンスを解析する方法を学んだ
- OpenWeatherMap APIのレスポンス構造を理解した
この段階では、すべてが順調に動いています。1つのAPIだけを使う分には、このシンプルなコードで十分です。
次回予告
次回は、別の天気情報サービス(WeatherStack API)を追加します。2つのAPIは温度の単位やレスポンス構造が異なります。両方を同時に使おうとすると、どのような問題が発生するのでしょうか?お楽しみに!