3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Goでつくる!クリスマス限定ディズニーフード簡易検索CLIを実装してみた🎄

Last updated at Posted at 2025-12-01

これは「imtakalab Advent Calendar 2025」の 2 日目の記事です。

はじめに

こんにちは! 最近忙しいのか忙しく見せてるのかわからない iitz-i13 です!
もうクリスマスシーズンですね🎅
実はディズニーがとても好きで、期間限定のものに目がないんです👀
そして今まさに、東京ディズニーリゾートでは現在 クリスマス限定フード が多数登場しています🎄

disneyland-christmas-food-2025-j160455.png

引用:https://castel.jp/item/160455/

ディズニーシー クリスマスフード 2025.png

引用:https://castel.jp/item/160456/

しかし、実際に公式サイトでフードを探そうとすると……

  • 画面遷移が多い
  • レストランごとにページを開き直す必要がある
  • パーク・エリア・店舗・フード種別でサクッと絞り込みたいのに、結構大変

と感じる場面が多いと思います。

「今いるエリアで食べられるクリスマス限定メニュー(限定メニューに関わらず)を、パッと一覧で見たい…!」

こう思ったことがある人も多いのではないでしょうか。

そこで本当は
パークの地図上に、検索したクリスマスフードをマッピング表示する Web ツール を作りたかったのですが、今回は時間が足りず……

まずは CLI 版としてシンプルに検索できるツール を Go (Golang) で作りました。

なぜ Go 言語を使用して作るの?

理由はほんとにシンプルで、

  • Go を一回触ってみたかった
  • コンパイラ言語であるため処理が速い
  • シンプルで書きやすいらしい

という3つだけです。

「とりあえず軽めのツールで Go を試してみよう!」
というノリで作り始めました。

今回作るもの

今回実装した CLI ツールを使うと、次のように 検索条件を指定してクリスマス限定フードを一発で一覧表示 できます。

例えば、TDS の「メディテレーニアンハーバー」にあるクリスマスメニューだけを見たい 場合は下記のコマンドを入力します。

go run ./cmd/xmas_cli --park tds --area ハーバー

そうすると以下のような出力が得られます。

一致するフードが 14 件見つかりました

1/14 | スペシャルセット (5800円) [meal]
  パーク  : TDS
  エリア  : メディテレーニアンハーバー
  店舗名  : リストランテ・ディ・カナレット
  店舗URL : https://www.tokyodisneyresort.jp/tds/restaurant/detail/408/
  詳細URL : https://www.tokyodisneyresort.jp/food/4222/

2/14 | チョコレートチュロス (600円) [food]
  パーク  : TDS
  エリア  : メディテレーニアンハーバー
  店舗名  : ザンビーニ・ブラザーズ・リストランテ
  店舗URL : https://www.tokyodisneyresort.jp/tds/restaurant/detail/403/
  詳細URL : https://www.tokyodisneyresort.jp/food/1549/
.
.
.

他にもレストラン名やジャンル(food, drink, meal)を分けてのフィルターもできます。

go run ./cmd/xmas_cli --restaurant ベイサイド --kind drink

実際に上記のコマンドを入力すると下記のような出力を得ることができます。

一致するフードが 1 件見つかりました

1/1 | ホットアップルジンジャードリンク (700円) [drink]
  パーク  : TDS
  エリア  : ポートディスカバリー
  店舗名  : ベイサイド・テイクアウト
  店舗URL : https://www.tokyodisneyresort.jp/tds/restaurant/detail/453/
  詳細URL : https://www.tokyodisneyresort.jp/food/2847/

前提

開発のディレクトリ環境は以下のようになっています

disney_xmas_foods
├── xmas_cli
│   └── main.go
├── data
│   └── foods_xmas.json
└── internal
    ├── data
    │   └── loader.go
    └── domain
        ├── food.go
        └── search.go
  • data/foods_xmas.json
    → クリスマスフードのデータ
  • internal/domain
    → ドメイン構造体 & 検索ロジック
  • internal/data
    → JSON 読み込み
  • xmas_cli/main.go
    → CLI 実行ファイル

また、今回は、クリスマスフードの情報が以下のような JSON にまとまっている前提で進めます。

[
  {
    "id": 1,
    "name": "スペシャルセット",
    "park": "TDS",
    "area": "メディテレーニアンハーバー",
    "restaurant": "リストランテ・ディ・カナレット",
    "price": 5800,
    "kind": "meal",
    "url_detail": "https://www.tokyodisneyresort.jp/food/4222/",
    "url_restaurant": "https://www.tokyodisneyresort.jp/tds/restaurant/detail/408/"
  },
  {
    "id": 2,
    "name": "チョコレートチュロス",
    "park": "TDS",
    "area": "メディテレーニアンハーバー",
    "restaurant": "ザンビーニ・ブラザーズ・リストランテ",
    "price": 600,
    "kind": "food",
    "url_detail": "https://www.tokyodisneyresort.jp/food/1549/",
    "url_restaurant": "https://www.tokyodisneyresort.jp/tds/restaurant/detail/403/"
  },
.
.
.
  {
    "id": 36,
    "name": "ロゼ・スパークリングワイン(グラス)",
    "park": "TDS",
    "area": "メディテレーニアンハーバー",
    "restaurant": "カフェ・ポルトフィーノ",
    "price": 1000,
    "kind": "drink",
    "url_detail": "https://www.tokyodisneyresort.jp/food/2255/",
    "url_restaurant": "https://www.tokyodisneyresort.jp/tds/restaurant/detail/400/"
  }
]

スクレイピングをしようとしたけど、ちょっとグレーかなって思ったので、テストデータを作成して実装しました〜

実装

フード情報のデータ構造(internal/domain/food.go

ここでは、クリスマスフード 1 件分のデータを表す Food 構造体と、検索に使用する条件をまとめた SearchCondition を定義しています。

Food 構造体には、パーク名・エリア名・店舗名・価格・メニューの種類など、検索に必要なすべての情報が含まれています。
また、公式サイトへのリンク(詳細ページ・店舗ページ)も保持しているため、検索結果からそのままアクセスできます。

検索条件をまとめる SearchCondition は、CLI からの入力を受け取り、
「どのパークを検索するか」「どの店舗名で絞るか」などの指定を扱うためのものです。

// internal/domain/food.go
package domain

type Food struct {
	ID		      int      `json:"id"`
	Name          string   `json:"name"`
	Park	      string   `json:"park"`
	Area          string   `json:"area"`
	Restaurant    string   `json:"restaurant"`
	Price         int      `json:"price"`
	Kind	      string   `json:"kind"`
	URLDetail     string   `json:"url_detail"`
	URLRestaurant string   `json:"url_restaurant"`
}

type SearchCondition struct {
	Park       string
	Area       string
	Restaurant string
	Kind       string
}

検索ロジック(internal/domain/search.go

フード情報を実際に検索するロジックがこちらです。

// internal/domain/search.go
package domain

import "strings"

func SearchFoods(foods []Food, cond SearchCondition) []Food {
	var result []Food

	for _, food := range foods {
		if !matchFood(food, cond) {
			continue
		}
		result = append(result, food)
	}

	return result
}

func matchFood(food Food, cond SearchCondition) bool {
	// 空文字は無視
	if cond.Park != "" && !strings.EqualFold(food.Park, cond.Park) {
		return false
	}
	if cond.Area != "" {
        // 部分一致 & 大文字小文字を無視
		condArea := strings.ToLower(cond.Area)
		foodArea := strings.ToLower(food.Area)
		if !strings.Contains(foodArea, condArea) {
			return false
		}
	}
	if cond.Kind != "" && food.Kind != cond.Kind {
		return false
	}
	if cond.Restaurant != "" {
		restLower := strings.ToLower(food.Restaurant)
		condLower := strings.ToLower(cond.Restaurant)
		if !strings.Contains(restLower, condLower) {
			return false
		}
	}

	return true
}

SearchFoods は、全フードデータを走査し、検索条件に一致したものだけを返すシンプルな関数です。

条件のチェック部分は matchFood に切り出されており、
「条件が空の場合は無視」 という仕様になっているため、
ユーザーは指定したい条件だけを CLI で入力すれば OK です。

特にエリア名や店舗名は、完全一致だとミスの影響を受けやすいため、
「部分一致」+「大文字小文字を無視する」 形で柔軟に検索できるようにしています。

ここで実装しているフィルタリングに加えて、
価格帯(例:--max-price--min-price)を追加したい場合は、
SearchCondition に項目を足して、matchFood に条件分岐を数行追加するだけで実装できます。

JSON ローダー(internal/data/loader.go

フード情報は JSON 形式で保存されており、それを読み込むのが LoadFoods です。

  • os.ReadFile で JSON ファイルを読み込み
  • json.Unmarshal[]Food 型にパース
// internal/data/loader.go
package data

import (
	"encoding/json"
	"os"

	"disney_xmas_foods/internal/domain"
)

func LoadFoods(path string) ([]domain.Food, error) {
	file, err := os.ReadFile(path)
	if err != nil {
		return nil, err
	}

	var foods []domain.Food
	if err := json.Unmarshal(file, &foods); err != nil {
		return nil, err
	}

	return foods, nil
}

実行

CLI から実行するときは、以下のようにフラグを指定して検索できます。

  • --park : TDL or TDS (tdl or tds)
  • --area : エリア名(部分一致 OK)
  • --restaurant : 店舗名(部分一致 OK)
  • --kind : food / drink / meal

例えば:

go run main.go --park TDS --area リストランテ

のように入力すれば、条件にマッチしたクリスマス限定フードだけが一覧表示されます!

一致するフードが 8 件見つかりました

1/8 | スペシャルセット (5800円) [meal]
  パーク  : TDS
  エリア  : メディテレーニアンハーバー
  店舗名  : リストランテ・ディ・カナレット
  店舗URL : https://www.tokyodisneyresort.jp/tds/restaurant/detail/408/
  詳細URL : https://www.tokyodisneyresort.jp/food/4222/

2/8 | チョコレートチュロス (600円) [food]
  パーク  : TDS
  エリア  : メディテレーニアンハーバー
  店舗名  : ザンビーニ・ブラザーズ・リストランテ
  店舗URL : https://www.tokyodisneyresort.jp/tds/restaurant/detail/403/
  詳細URL : https://www.tokyodisneyresort.jp/food/1549/

3/8 | スペシャルパスタセット (3800円) [meal]
  パーク  : TDS
  エリア  : メディテレーニアンハーバー
  店舗名  : リストランテ・ディ・カナレット
  店舗URL : https://www.tokyodisneyresort.jp/tds/restaurant/detail/408/
  詳細URL : https://www.tokyodisneyresort.jp/food/4223/
.
.
.

まとめ

いかがでしたでしょうか、前日書いたにしてはいい感じに発展性もりもりの初期段階の開発ができたと思います。なかなか上手くいかなくてたのしかったです🙃

今回の Go CLI ツールでは、

  • JSON データを読み込んで
  • パーク・エリア・店舗名・ジャンルで検索し
  • 目的のクリスマス限定メニューを一瞬で取得できる

という機能を実装しました。

本当は、
地図上にアイコンとしてフードをマッピングする Web アプリ
まで実装したかったのですが、まずは検索機能だけを CLI で先に実装しました。

この仕組みを使えば、今後は

  • Web API 化してアプリから使えるようにしたり
  • 地図上にフードを表示したり
  • iPhoneのショートカットと連携したり

といった形で、簡単に機能を広げていくこともできます。

本当は APEX Legends ユーザーのステータス可視化とかやってみたかったんですけど、それはまた今度気が向いたらやってみたいなぁなんておもてます。

最後までご覧いただき、ありがとうございました❕

参考文献

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?