How to Build a Python Weather Report by City App - Simple & Detailed Guide

How to Build a Python Weather Report by City App: Simple & Detailed Guide

Build a Python Weather Report by City App

Learn how to create a Python-based Weather Report by City app—first with a one-liner using wttr.in, then with a full-featured solution using OpenStreetMap’s Nominatim geocoding and the Open-Meteo API. Step-by-step code breakdown, error handling, and best practices included.

Introduction

Whether you’re building a CLI tool for quick forecasts or a full-blown weather dashboard, fetching and displaying weather by city is a classic Python project. In this post, you’ll first see a minimal one-liner solution using wttr.in, then dive into a robust implementation leveraging:

  • Nominatim (OpenStreetMap’s geocoding service)

  • Open-Meteo API for current weather data

  • Python’s requests library for HTTP calls

By the end, you’ll understand each section of the code and best practices for error handling, user-agent headers, and mapping weather codes to human-friendly descriptions.


 

Prerequisites

Make sure you have:

  • Python 3.7+ installed

  • The requests library:

pip install requests

  • A basic understanding of HTTP requests and JSON parsing

  • (Optional) An IDE or text editor like VS Code for syntax highlighting


 

Quick & Simple: wttr.in One-Liner

For an ultra-lightweight solution, you can use wttr.in—a free weather service for the terminal:

import requests

city = input(“City: “)
res = requests.get(f”http://wttr.in/{city}?format=3″)
print(res.text)

  • How it works:

    1. Prompts the user for a city name.

    2. Sends a GET request to wttr.in with ?format=3 (returns “City: +temp +condition”).

    3. Prints the concise result.

Pros: No API key required; one-liner.
Cons: Limited formatting, no fine-grained control, depends on an external service’s uptime.


 

The Detailed Approach

For production-grade projects, you’ll often need more control, reliability, and clarity. Let’s explore a two-step version:

1. Geocoding with Nominatim

def get_weather(city):
geocode_url = f”https://nominatim.openstreetmap.org/search?city={city}&format=json”
headers = {
‘User-Agent’: ‘WeatherApp (contact@example.com)’
}
response = requests.get(geocode_url, headers=headers)
location_data = response.json()
# Validate the response
if not location_data:
print(f”Error: City ‘{city}’ not found.”)
return
latitude = location_data[0][‘lat’]
longitude = location_data[0][‘lon’]

  • Why geocode? Most weather APIs require latitude & longitude.

  • User-Agent header: Nominatim requires a descriptive User-Agent to identify your application (OSM Usage Policy).

  • JSON parsing & validation: Always check for empty lists or malformed JSON to avoid crashes.

2. Fetching Weather Data from Open-Meteo

weather_url = (
f”https://api.open-meteo.com/v1/forecast?”
f”latitude={latitude}&longitude={longitude}&current=temperature_2m,weathercode”
)
weather_response = requests.get(weather_url)
weather_data = weather_response.json()

if ‘current’ in weather_data:
current = weather_data[‘current’]
temperature = current[‘temperature_2m’]
code = current[‘weathercode’]
description = get_weather_description(code)
print(f”Weather in {city.title()}:”)
print(f”Temperature: {temperature}°C”)
print(f”Condition: {description}”)
else:
print(f”Error: Weather data unavailable for ‘{city}’.”)

  • Open-Meteo API: Free, no API key required for basic usage.

  • Current weather: We request only temperature_2m and weathercode to minimize payload.

  • Data validation: Ensure the current key exists before accessing nested fields.

3. Mapping Weather Codes to Descriptions

def get_weather_description(code):
weather_descriptions = {
0: “Clear sky”,
1: “Mainly clear”,
2: “Partly cloudy”,
3: “Overcast”,
45: “Fog”,
# … include all codes up to 99 …
99: “Thunderstorm with heavy hail”
}
return weather_descriptions.get(code, “Unknown condition”)

  • Why map codes? Weather APIs use numeric codes for conditions; mapping makes output human-friendly.

  • Extensibility: You can extend this dictionary to include icons, humidity, wind speed, etc.

4. Error Handling & User Feedback

  • JSON decode errors: Wrap response.json() in a try/except to catch invalid JSON.

  • Network issues: Consider handling requests.exceptions.RequestException for timeouts or connection errors.

  • Invalid input: Prompt the user to re-enter if the city name yields no geocoding result.


 

Putting It All Together

import requests

def get_weather(city):
# Step 1: Geocode city → lat/lon
geocode_url = f”https://nominatim.openstreetmap.org/search?city={city}&format=json”
headers = {‘User-Agent’: ‘WeatherApp (contact@example.com)’}
try:
location_data = requests.get(geocode_url, headers=headers).json()
except requests.exceptions.JSONDecodeError:
print(“Error: Could not parse location data.”)
return

if not location_data:
print(f”Error: City ‘{city}’ not found.”)
return

latitude, longitude = location_data[0][‘lat’], location_data[0][‘lon’]

# Step 2: Fetch current weather
weather_url = (
f”https://api.open-meteo.com/v1/forecast?”
f”latitude={latitude}&longitude={longitude}&current=temperature_2m,weathercode”
)
weather_data = requests.get(weather_url).json()

if ‘current’ not in weather_data:
print(f”Error: No weather data for ‘{city}’.”)
return

current = weather_data[‘current’]
temp = current[‘temperature_2m’]
code = current[‘weathercode’]
desc = get_weather_description(code)

# Step 3: Display
print(f”\nWeather in {city.title()}:”)
print(f”🌡️ Temperature: {temp}°C”)
print(f”☁️ Condition: {desc}”)

def get_weather_description(code):
weather_descriptions = {
# … (same as previous mapping) …
}
return weather_descriptions.get(code, “Unknown condition”)

if __name__ == “__main__”:
city = input(“Enter city name: “)
get_weather(city)

Conclusion

You now have two approaches to fetch weather by city in Python:

  1. A one-line wttr.in solution for quick CLI checks.

  2. A detailed implementation using Nominatim geocoding and the Open-Meteo API for production-grade apps.

Experiment by extending the mapping dictionary, adding error logging, or integrating with a GUI/web framework like Flask or Tkinter. Happy coding!

 


💬 Got questions? Drop a comment or reach out!

Leave a Comment

Your email address will not be published. Required fields are marked *