🌤️ Weather App - Ứng dụng thời tiết thông minh
thông tin
Tạo một ứng dụng thời tiết như một nhà khí tượng học mini - lấy dữ liệu từ internet và hiển thị thông tin thời tiết một cách đẹp mắt!
🎯 Mục tiêu dự án
Xây dựng ứng dụng thời tiết với các tính năng:
- 🌡️ Hiển thị nhiệt độ hiện tại và cảm giác như
- 🌤️ Thông tin thời tiết chi tiết (độ ẩm, áp suất, gió)
- 📅 Dự báo 5 ngày tới
- 🏙️ Hỗ trợ nhiều thành phố
- 💾 Lưu danh sách thành phố yêu thích
- 🎨 Giao diện console đẹp mắt với màu sắc
🛠️ Chuẩn bị
Thư viện cần thiết
# Cài đặt thư viện
# pip install requests colorama
import requests
import json
from datetime import datetime, timedelta
from colorama import Fore, Back, Style, init
import os
# Khởi tạo colorama cho Windows
init(autoreset=True)
Đăng ký API Key
# Bước 1: Đăng ký tài khoản miễn phí tại: https://openweathermap.org/api
# Bước 2: Lấy API key từ tài khoản của bạn
# Bước 3: Thay thế API_KEY bên dưới
API_KEY = "your_api_key_here" # 🔑 Thay bằng API key thật
BASE_URL = "https://api.openweathermap.org/data/2.5"
# Lưu ý: Để demo, chúng ta sẽ tạo dữ liệu giả
DEMO_MODE = True # Chuyển thành False khi có API key thật
🏗️ Xây dựng ứng dụng
1. Class quản lý thời tiết
class WeatherAPI:
"""
Class để giao tiếp với API thời tiết
Giống như một người phiên dịch giữa app và internet!
"""
def __init__(self, api_key=None):
self.api_key = api_key
self.base_url = BASE_URL
self.demo_mode = DEMO_MODE or not api_key
def get_weather_data(self, city_name):
"""Lấy dữ liệu thời tiết hiện tại"""
if self.demo_mode:
return self._get_demo_weather(city_name)
try:
url = f"{self.base_url}/weather"
params = {
'q': city_name,
'appid': self.api_key,
'units': 'metric',
'lang': 'vi'
}
response = requests.get(url, params=params, timeout=10)
if response.status_code == 200:
return response.json()
elif response.status_code == 404:
return {"error": f"Không tìm thấy thành phố: {city_name}"}
else:
return {"error": f"Lỗi API: {response.status_code}"}
except requests.exceptions.RequestException as e:
return {"error": f"Lỗi kết nối: {str(e)}"}
def get_forecast_data(self, city_name, days=5):
"""Lấy dự báo thời tiết 5 ngày"""
if self.demo_mode:
return self._get_demo_forecast(city_name, days)
try:
url = f"{self.base_url}/forecast"
params = {
'q': city_name,
'appid': self.api_key,
'units': 'metric',
'lang': 'vi'
}
response = requests.get(url, params=params, timeout=10)
if response.status_code == 200:
return response.json()
else:
return {"error": f"Lỗi lấy dự báo: {response.status_code}"}
except requests.exceptions.RequestException as e:
return {"error": f"Lỗi kết nối: {str(e)}"}
def _get_demo_weather(self, city_name):
"""Tạo dữ liệu thời tiết demo"""
import random
weather_conditions = [
("clear sky", "☀️", "trời quang"),
("few clouds", "🌤️", "ít mây"),
("scattered clouds", "⛅", "mây rải rác"),
("broken clouds", "☁️", "nhiều mây"),
("light rain", "🌦️", "mưa nhỏ"),
("moderate rain", "🌧️", "mưa vừa")
]
condition = random.choice(weather_conditions)
return {
"name": city_name.title(),
"main": {
"temp": round(random.uniform(20, 35), 1),
"feels_like": round(random.uniform(20, 35), 1),
"humidity": random.randint(40, 80),
"pressure": random.randint(1000, 1020)
},
"weather": [{
"main": condition[0],
"description": condition[2],
"icon": condition[1]
}],
"wind": {
"speed": round(random.uniform(2, 15), 1)
},
"sys": {
"country": "VN"
}
}
def _get_demo_forecast(self, city_name, days):
"""Tạo dữ liệu dự báo demo"""
import random
forecast_list = []
base_date = datetime.now()
for i in range(days):
date = base_date + timedelta(days=i)
temp_min = random.randint(20, 28)
temp_max = temp_min + random.randint(5, 10)
forecast_list.append({
"dt": date.timestamp(),
"main": {
"temp_min": temp_min,
"temp_max": temp_max
},
"weather": [{
"main": random.choice(["Clear", "Clouds", "Rain"]),
"description": random.choice(["trời quang", "có mây", "mưa nhỏ"])
}]
})
return {
"city": {"name": city_name.title()},
"list": forecast_list
}
2. Class hiển thị giao diện
class WeatherUI:
"""
Class quản lý giao diện người dùng
Giống như một nghệ sĩ vẽ tranh thời tiết!
"""
def __init__(self):
self.weather_api = WeatherAPI(API_KEY)
def clear_screen(self):
"""Xóa màn hình console"""
os.system('cls' if os.name == 'nt' else 'clear')
def print_header(self):
"""In header ứng dụng"""
print(f"{Fore.CYAN}{Style.BRIGHT}")
print("=" * 60)
print("🌤️ WEATHER APP - ỨNG DỤNG THỜI TIẾT THÔNG MINH 🌤️")
print("=" * 60)
print(f"{Style.RESET_ALL}")
def print_current_weather(self, weather_data):
"""Hiển thị thời tiết hiện tại"""
if "error" in weather_data:
print(f"{Fore.RED}❌ {weather_data['error']}")
return
# Thông tin cơ bản
city = weather_data["name"]
country = weather_data["sys"].get("country", "")
temp = weather_data["main"]["temp"]
feels_like = weather_data["main"]["feels_like"]
humidity = weather_data["main"]["humidity"]
pressure = weather_data["main"]["pressure"]
# Thông tin thời tiết
weather = weather_data["weather"][0]
description = weather["description"]
icon = weather.get("icon", "🌤️")
# Gió
wind_speed = weather_data["wind"]["speed"]
print(f"\n{Fore.GREEN}{Style.BRIGHT}📍 THỜI TIẾT HIỆN TẠI")
print("=" * 40)
# Thành phố
print(f"{Fore.YELLOW}🏙️ Thành phố: {Style.BRIGHT}{city}")
if country:
print(f"🌍 Quốc gia: {country}")
# Nhiệt độ
temp_color = self._get_temp_color(temp)
print(f"\n{temp_color}🌡️ Nhiệt độ: {Style.BRIGHT}{temp}°C")
print(f"{temp_color}🤒 Cảm giác như: {feels_like}°C")
# Thời tiết
print(f"\n{Fore.CYAN}{icon} Thời tiết: {Style.BRIGHT}{description.title()}")
# Chi tiết
print(f"\n{Fore.MAGENTA}💧 Độ ẩm: {humidity}%")
print(f"🌪️ Áp suất: {pressure} hPa")
print(f"💨 Tốc độ gió: {wind_speed} m/s")
print(f"{Style.RESET_ALL}")
def print_forecast(self, forecast_data):
"""Hiển thị dự báo thời tiết"""
if "error" in forecast_data:
print(f"{Fore.RED}❌ {forecast_data['error']}")
return
city = forecast_data["city"]["name"]
forecast_list = forecast_data["list"]
print(f"\n{Fore.GREEN}{Style.BRIGHT}📅 DỰ BÁO 5 NGÀY - {city.upper()}")
print("=" * 60)
for item in forecast_list:
# Chuyển đổi timestamp thành ngày
date = datetime.fromtimestamp(item["dt"])
day_name = self._get_day_name(date)
date_str = date.strftime("%d/%m/%Y")
# Nhiệt độ
temp_min = item["main"]["temp_min"]
temp_max = item["main"]["temp_max"]
# Thời tiết
weather_desc = item["weather"][0]["description"]
weather_icon = self._get_weather_icon(item["weather"][0]["main"])
# Màu sắc theo nhiệt độ
temp_color = self._get_temp_color(temp_max)
print(f"{Fore.YELLOW}📅 {day_name} ({date_str})")
print(f"{temp_color} 🌡️ {temp_min}°C - {temp_max}°C")
print(f"{Fore.CYAN} {weather_icon} {weather_desc.title()}")
print()
def _get_temp_color(self, temp):
"""Chọn màu theo nhiệt độ"""
if temp >= 35:
return f"{Fore.RED}{Style.BRIGHT}"
elif temp >= 30:
return f"{Fore.YELLOW}{Style.BRIGHT}"
elif temp >= 20:
return f"{Fore.GREEN}{Style.BRIGHT}"
else:
return f"{Fore.CYAN}{Style.BRIGHT}"
def _get_day_name(self, date):
"""Lấy tên thứ tiếng Việt"""
days = ["Thứ Hai", "Thứ Ba", "Thứ Tư", "Thứ Năm", "Thứ Sáu", "Thứ Bảy", "Chủ Nhật"]
return days[date.weekday()]
def _get_weather_icon(self, weather_main):
"""Lấy icon thời tiết"""
icons = {
"Clear": "☀️",
"Clouds": "☁️",
"Rain": "🌧️",
"Drizzle": "🌦️",
"Thunderstorm": "⛈️",
"Snow": "❄️",
"Mist": "🌫️"
}
return icons.get(weather_main, "🌤️")
3. Class quản lý dữ liệu
class WeatherDataManager:
"""
Quản lý dữ liệu ứng dụng
Giống như một thư ký ghi chép mọi thứ!
"""
def __init__(self, data_file="weather_favorites.json"):
self.data_file = data_file
self.favorites = self.load_favorites()
self.history = []
def load_favorites(self):
"""Tải danh sách thành phố yêu thích"""
try:
if os.path.exists(self.data_file):
with open(self.data_file, 'r', encoding='utf-8') as f:
data = json.load(f)
return data.get('favorites', [])
return []
except Exception as e:
print(f"⚠️ Lỗi tải dữ liệu: {e}")
return []
def save_favorites(self):
"""Lưu danh sách yêu thích"""
try:
data = {
'favorites': self.favorites,
'last_updated': datetime.now().isoformat()
}
with open(self.data_file, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
return True
except Exception as e:
print(f"⚠️ Lỗi lưu dữ liệu: {e}")
return False
def add_favorite(self, city_name):
"""Thêm thành phố yêu thích"""
city_name = city_name.title()
if city_name not in self.favorites:
self.favorites.append(city_name)
if self.save_favorites():
return True
return False
def remove_favorite(self, city_name):
"""Xóa thành phố yêu thích"""
city_name = city_name.title()
if city_name in self.favorites:
self.favorites.remove(city_name)
self.save_favorites()
return True
return False
def get_favorites(self):
"""Lấy danh sách yêu thích"""
return self.favorites.copy()
def add_to_history(self, city_name, weather_data):
"""Thêm vào lịch sử tìm kiếm"""
self.history.append({
'city': city_name,
'timestamp': datetime.now().isoformat(),
'temp': weather_data.get('main', {}).get('temp', 0)
})
# Giữ tối đa 10 lần tìm kiếm gần nhất
if len(self.history) > 10:
self.history = self.history[-10:]
def get_history(self):
"""Lấy lịch sử tìm kiếm"""
return self.history.copy()
4. Ứng dụng chính
class WeatherApp:
"""
Ứng dụng thời tiết chính
Giống như một chỉ huy điều phối tất cả!
"""
def __init__(self):
self.ui = WeatherUI()
self.data_manager = WeatherDataManager()
self.running = True
def show_menu(self):
"""Hiển thị menu chính"""
print(f"\n{Fore.CYAN}{Style.BRIGHT}📋 MENU CHÍNH")
print("=" * 30)
print(f"{Fore.WHITE}1. 🔍 Xem thời tiết thành phố")
print("2. 📅 Xem dự báo 5 ngày")
print("3. ⭐ Quản lý yêu thích")
print("4. 📊 Xem lịch sử")
print("5. ℹ️ Thông tin ứng dụng")
print("0. 🚪 Thoát")
print("=" * 30)
def get_city_weather(self):
"""Xem thời tiết thành phố"""
print(f"\n{Fore.YELLOW}🔍 XEM THỜI TIẾT")
print("-" * 20)
city = input("🏙️ Nhập tên thành phố: ").strip()
if not city:
print(f"{Fore.RED}❌ Vui lòng nhập tên thành phố!")
return
print(f"\n{Fore.CYAN}⏳ Đang lấy dữ liệu thời tiết...")
weather_data = self.ui.weather_api.get_weather_data(city)
self.ui.print_current_weather(weather_data)
if "error" not in weather_data:
self.data_manager.add_to_history(city, weather_data)
# Hỏi có muốn thêm vào yêu thích không
if city.title() not in self.data_manager.favorites:
choice = input(f"\n💝 Thêm {city} vào yêu thích? (y/n): ")
if choice.lower() == 'y':
if self.data_manager.add_favorite(city):
print(f"{Fore.GREEN}✅ Đã thêm vào yêu thích!")
def get_city_forecast(self):
"""Xem dự báo thời tiết"""
print(f"\n{Fore.YELLOW}📅 DỰ BÁO THỜI TIẾT")
print("-" * 25)
city = input("🏙️ Nhập tên thành phố: ").strip()
if not city:
print(f"{Fore.RED}❌ Vui lòng nhập tên thành phố!")
return
print(f"\n{Fore.CYAN}⏳ Đang lấy dữ liệu dự báo...")
forecast_data = self.ui.weather_api.get_forecast_data(city)
self.ui.print_forecast(forecast_data)
def manage_favorites(self):
"""Quản lý thành phố yêu thích"""
while True:
print(f"\n{Fore.MAGENTA}{Style.BRIGHT}⭐ QUẢN LÝ YÊU THÍCH")
print("=" * 30)
favorites = self.data_manager.get_favorites()
if favorites:
print("📍 Thành phố yêu thích:")
for i, city in enumerate(favorites, 1):
print(f" {i}. {city}")
else:
print("📭 Chưa có thành phố yêu thích nào.")
print(f"\n{Fore.WHITE}1. 📍 Xem thời tiết yêu thích")
print("2. ➕ Thêm thành phố")
print("3. ➖ Xóa thành phố")
print("0. 🔙 Quay lại")
choice = input(f"\n{Fore.CYAN}Chọn: ").strip()
if choice == "1":
self.view_favorite_weather(favorites)
elif choice == "2":
self.add_favorite_city()
elif choice == "3":
self.remove_favorite_city(favorites)
elif choice == "0":
break
else:
print(f"{Fore.RED}❌ Lựa chọn không hợp lệ!")
def view_favorite_weather(self, favorites):
"""Xem thời tiết các thành phố yêu thích"""
if not favorites:
print(f"{Fore.YELLOW}📭 Chưa có thành phố yêu thích!")
return
print(f"\n{Fore.CYAN}⏳ Đang lấy thời tiết tất cả thành phố...")
for city in favorites:
print(f"\n{'-' * 50}")
weather_data = self.ui.weather_api.get_weather_data(city)
self.ui.print_current_weather(weather_data)
def add_favorite_city(self):
"""Thêm thành phố yêu thích"""
city = input(f"\n🏙️ Nhập tên thành phố muốn thêm: ").strip()
if city:
if self.data_manager.add_favorite(city):
print(f"{Fore.GREEN}✅ Đã thêm {city} vào yêu thích!")
else:
print(f"{Fore.YELLOW}⚠️ {city} đã có trong danh sách!")
def remove_favorite_city(self, favorites):
"""Xóa thành phố yêu thích"""
if not favorites:
print(f"{Fore.YELLOW}📭 Chưa có thành phố nào để xóa!")
return
try:
choice = int(input(f"\n🗑️ Chọn số thứ tự để xóa (0 để hủy): "))
if choice == 0:
return
elif 1 <= choice <= len(favorites):
city = favorites[choice - 1]
if self.data_manager.remove_favorite(city):
print(f"{Fore.GREEN}✅ Đã xóa {city} khỏi yêu thích!")
else:
print(f"{Fore.RED}❌ Số không hợp lệ!")
except ValueError:
print(f"{Fore.RED}❌ Vui lòng nhập số!")
def show_history(self):
"""Hiển thị lịch sử tìm kiếm"""
print(f"\n{Fore.BLUE}{Style.BRIGHT}📊 LỊCH SỬ TÌM KIẾM")
print("=" * 40)
history = self.data_manager.get_history()
if not history:
print("📭 Chưa có lịch sử tìm kiếm.")
return
for i, record in enumerate(reversed(history), 1):
timestamp = datetime.fromisoformat(record['timestamp'])
time_str = timestamp.strftime("%d/%m/%Y %H:%M")
print(f"{i:2d}. 🏙️ {record['city']}")
print(f" ⏰ {time_str}")
print(f" 🌡️ {record['temp']}°C")
print()
def show_app_info(self):
"""Hiển thị thông tin ứng dụng"""
print(f"\n{Fore.GREEN}{Style.BRIGHT}ℹ️ THÔNG TIN ỨNG DỤNG")
print("=" * 35)
print(f"{Fore.WHITE}📱 Tên: Weather App")
print("🏷️ Phiên bản: 1.0.0")
print("👨💻 Phát triển bởi: Behitek")
print("🎯 Mục đích: Học lập trình Python")
print("\n🌟 Tính năng:")
print(" ✅ Xem thời tiết hiện tại")
print(" ✅ Dự báo 5 ngày")
print(" ✅ Quản lý yêu thích")
print(" ✅ Lịch sử tìm kiếm")
print(" ✅ Giao diện màu sắc")
if DEMO_MODE:
print(f"\n{Fore.YELLOW}⚠️ Đang chạy ở chế độ DEMO")
print("🔑 Để sử dụng dữ liệu thật, hãy:")
print(" 1. Đăng ký tại: https://openweathermap.org/api")
print(" 2. Lấy API key miễn phí")
print(" 3. Thay đổi API_KEY trong code")
print(" 4. Đặt DEMO_MODE = False")
def run(self):
"""Chạy ứng dụng chính"""
try:
while self.running:
self.ui.clear_screen()
self.ui.print_header()
self.show_menu()
choice = input(f"\n{Fore.CYAN}👉 Chọn chức năng (0-5): ").strip()
if choice == "1":
self.get_city_weather()
elif choice == "2":
self.get_city_forecast()
elif choice == "3":
self.manage_favorites()
elif choice == "4":
self.show_history()
elif choice == "5":
self.show_app_info()
elif choice == "0":
print(f"\n{Fore.GREEN}👋 Cảm ơn bạn đã sử dụng Weather App!")
print("🌤️ Chúc bạn một ngày tốt lành!")
self.running = False
else:
print(f"{Fore.RED}❌ Lựa chọn không hợp lệ!")
if self.running:
input(f"\n{Fore.YELLOW}📱 Nhấn Enter để tiếp tục...")
except KeyboardInterrupt:
print(f"\n\n{Fore.YELLOW}⚠️ Ứng dụng bị gián đoạn bởi người dùng.")
print(f"{Fore.GREEN}👋 Tạm biệt!")
except Exception as e:
print(f"\n{Fore.RED}❌ Lỗi không mong muốn: {e}")
print(f"{Fore.GREEN}👋 Ứng dụng sẽ thoát an toàn.")
# Chạy ứng dụng
if __name__ == "__main__":
print("🌤️ Khởi động Weather App...")
print("⚠️ Lưu ý: Đang chạy ở chế độ DEMO với dữ liệu giả")
print("🔑 Để dùng dữ liệu thật, vui lòng đăng ký API key tại OpenWeatherMap")
app = WeatherApp()
app.run()
🎯 Kết quả và Demo
# Output mẫu khi chạy ứng dụng:
"""
============================================================
🌤️ WEATHER APP - ỨNG DỤNG THỜI TIẾT THÔNG MINH 🌤️
============================================================
📋 MENU CHÍNH
==============================
1. 🔍 Xem thời tiết thành phố
2. 📅 Xem dự báo 5 ngày
3. ⭐ Quản lý yêu thích
4. 📊 Xem lịch sử
5. ℹ️ Thông tin ứng dụng
0. 🚪 Thoát
==============================
👉 Chọn chức năng (0-5): 1
🔍 XEM THỜI TIẾT
--------------------
🏙️ Nhập tên thành phố: Ho Chi Minh
⏳ Đang lấy dữ liệu thời tiết...
📍 THỜI TIẾT HIỆN TẠI
========================================
🏙️ Thành phố: Ho Chi Minh
🌍 Quốc gia: VN
🌡️ Nhiệt độ: 32.5°C
🤒 Cảm giác như: 35.2°C
🌤️ Thời tiết: Ít Mây
💧 Độ ẩm: 65%
🌪️ Áp suất: 1015 hPa
💨 Tốc độ gió: 8.5 m/s
💝 Thêm Ho Chi Minh vào yêu thích? (y/n): y
✅ Đã thêm vào yêu thích!
"""
🚀 Mở rộng và cải tiến
1. Thêm tính năng mới
# Có thể thêm các tính năng:
class WeatherAppAdvanced(WeatherApp):
"""Phiên bản nâng cao với nhiều tính năng"""
def get_weather_alerts(self, city):
"""Cảnh báo thời tiết nguy hiểm"""
# Cảnh báo nhiệt độ cao, bão, mưa lớn...
pass
def compare_cities(self):
"""So sánh thời tiết giữa các thành phố"""
pass
def export_data(self):
"""Xuất dữ liệu ra file Excel/PDF"""
pass
def send_notifications(self):
"""Gửi thông báo email/SMS"""
pass
2. Cải thiện giao diện
# Có thể sử dụng:
# - tkinter cho GUI desktop
# - Flask/Django cho web app
# - Kivy cho mobile app
🏆 Điểm nổi bật của dự án
Kỹ thuật được sử dụng:
- 🌐 API Integration - Gọi API thời tiết
- 💾 File I/O - Lưu trữ dữ liệu JSON
- 🎨 Console UI - Giao diện màu sắc với colorama
- 🏗️ OOP Design - Thiết kế hướng đối tượng
- 🔄 Exception Handling - Xử lý lỗi toàn diện
- 📊 Data Processing - Xử lý và hiển thị dữ liệu
- ⏰ DateTime Handling - Làm việc với thời gian
Tính năng nổi bật:
- ✅ Giao diện đẹp mắt với màu sắc
- ✅ Hỗ trợ chế độ demo và thật
- ✅ Quản lý danh sách yêu thích
- ✅ Lịch sử tìm kiếm
- ✅ Dự báo nhiều ngày
- ✅ Xử lý lỗi thông minh
- ✅ Dữ liệu persistent
🎯 Bài học rút ra
Nh ững điều học được:
- API Integration - Cách gọi và xử lý API
- Error Handling - Xử lý lỗi mạng và dữ liệu
- Data Persistence - Lưu trữ dữ liệu người dùng
- User Experience - Thiết kế giao diện thân thiện
- Code Organization - Tổ chức code theo OOP
Bước tiếp theo: Thử tạo Quiz Game để học cách xây dựng game đơn giản!
💡 Tip: Weather App là dự án tuyệt vời để học API, JSON, và UI design!
Behitek - Học lập trình Python một cách dễ hiểu nhất! 🚀