iT邦幫忙

2025 iThome 鐵人賽

DAY 16
0
Software Development

軟體開發養成計畫:以小程式實作深化開發能力系列 第 16

[Day16]查詢今天要不要帶雨傘:Python 串接天氣 API

  • 分享至 

  • xImage
  •  

開場

昨天我們一起探討了 API 串接的核心概念,並學會了如何處理從 API 伺服器返回的 JSON 資料。這些知識將成為今天的基礎,帶領我們邁向一個更實際的挑戰──打造天氣查詢小程式
這個小工具不僅能讓我們在出門前快速掌握天氣狀況,更是個練習整合能力的絕佳專案。我將把過去所學的理論串連起來,從發送請求、解析資料,到最後在介面上呈現結果,完整體驗 Python 串接 API 的流程。
準備好了嗎?那就一起動手寫程式吧!

天氣查詢程式開發步驟

1.引入模組 & API Key

import tkinter as tk
from tkinter import ttk, messagebox
import requests
from io import BytesIO
from PIL import Image, ImageTk
import datetime

API_KEY = "個人的API_KEY"
  • tkinter → 製作 GUI 視窗。
  • requests → 呼叫 API 抓天氣資料。
  • PIL.ImageTk → 顯示天氣圖示用。
  • datetime → 把日出日落的時間戳轉換成正常時間。

小提醒:API_KEY 需要自己在 OpenWeatherMap 申請金鑰,沒有它就無法呼叫 API。

2.與 API 溝通的函式

def get_current_weather(city, units="metric"):
    url = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={API_KEY}&units={units}&lang=zh_tw"
    r = requests.get(url)
    if r.status_code == 200:
        return r.json()
    return None
  • 傳入城市名稱 city 和單位(默認攝氏)。
  • 呼叫 OpenWeather API 的即時天氣,如果成功回應 (status_code == 200),就回傳 JSON 資料,否則回傳 None。

此外,有個功能類似的get_forecast則是查 5 天預測。

3.查詢邏輯

def search_weather():
    city = entry.get().strip()
    if not city:
        messagebox.showwarning("警告", "請輸入城市名稱!")
        return

    units = "metric" if unit_combo.get() == "攝氏 (°C)" else "imperial"
  • 使用者輸入城市名稱,如果是空白就跳警告。
  • 判斷單位是攝氏還是華氏

依照模式不同也有區分。

1.即時天氣模式:

 if mode_combo.get() == "即時天氣":
        data = get_current_weather(city, units)
        if not data:
            result_text.set("警告!找不到城市,請重新輸入!")
            return
            
        weather = data["weather"][0]["description"]
        temp = data["main"]["temp"]
        feels_like = data["main"]["feels_like"]
        humidity = data["main"]["humidity"]
        wind = data["wind"]["speed"]
        sunrise = datetime.datetime.fromtimestamp(data["sys"]["sunrise"])
        sunset = datetime.datetime.fromtimestamp(data["sys"]["sunset"])

        result_text.set(
            f"城市:{data['name']}\n"
            f"天氣:{weather}\n"
            f"溫度:{temp}°\n"
            f"體感溫度:{feels_like}°\n"
            f"濕度:{humidity}%\n"
            f"風速:{wind}\n"
            f"日出:{sunrise.strftime('%H:%M:%S')}\n"
            f"日落:{sunset.strftime('%H:%M:%S')}\n"
        )

會顯示溫度、體感、濕度、風速、日出日落時間。

2.5 天預測模式:

elif mode_combo.get() == "5天預測":
        data = get_forecast(city, units)
        if not data:
            result_text.set("警告!找不到城市,請重新輸入!")
            return

        result = f"城市:{data['city']['name']}\n 5 天預測:\n"
        for i in range(0, len(data["list"]), 8):
            forecast = data["list"][i]
            date = forecast["dt_txt"].split(" ")[0]
            desc = forecast["weather"][0]["description"]
            temp = forecast["main"]["temp"]
            result += f"{date} → {desc}, {temp}°\n"
  • 每 8 筆(一天)抓一筆 → 顯示每天的天氣描述 + 溫度。
  • 最後還會把輸入的城市存進 history_list,方便下次直接選。

4.歷史查詢功能

def search_from_history(event):
    city = history_combo.get()
    entry.delete(0, tk.END)
    entry.insert(0, city)
    search_weather()
  • 使用者可以從查詢紀錄下拉選單選擇城市。
  • 自動把城市填回輸入框,並立即查詢。

5.GUI 介面設計

(1)建立主視窗,設定大小和背景色

root = tk.Tk()
root.title("天氣查詢程式")
root.geometry("500x600")
root.configure(bg="lightblue")

(2)下拉選單,讓使用者切換「模式」和「單位」

mode_combo = ttk.Combobox(frame, values=["即時天氣", "5天預測"], state="readonly", width=10)
unit_combo = ttk.Combobox(frame, values=["攝氏 (°C)", "華氏 (°F)"], state="readonly", width=12)

(3)StringVar 綁定 Label,讓程式更新文字時,自動更新介面。

result_text = tk.StringVar()
result_label = tk.Label(root, textvariable=result_text, justify="left", font=("Arial", 11))

(4)查詢紀錄的下拉選單,選擇後會觸發 search_from_history。

history_combo = ttk.Combobox(root, values=history_list, state="readonly", width=20)
history_combo.bind("<<ComboboxSelected>>", search_from_history)

天氣查詢程式成果呈現

27
28


一開始,我的天氣程式碼只能在終端機上跑,雖然資料能正常顯示,但總覺得少了點什麼。為了讓它更符合我們平日需求,我決定動手改造一番!我加入了未來七天預報、即時資料刷新,甚至還能顯示溫度、濕度和風速等豐富資訊。當我完成這些小細節,才真正體會到,一個好的程式不只求「能用」,更要讓使用者覺得「好用」。
API 真的超神奇,期待下次能用它玩出更多有趣的小東西!


上一篇
[Day15]讓程式與世界連線:API 串接與 JSON 處理攻略
下一篇
[Day17]JSON、XML、YAML:資料交換三兄弟
系列文
軟體開發養成計畫:以小程式實作深化開發能力17
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言