Building a Command-Line Weather Application in Python Using OpenWeatherMap API

Noah Weston
7 min read6 days ago

--

Introduction

In today’s world, weather applications are ubiquitous. From planning your day to preparing for extreme weather conditions, having access to up-to-date weather information is crucial. Building a weather application not only serves practical needs but also provides an excellent opportunity to delve into Python programming, API integration, and data parsing.

In this comprehensive guide, we’ll walk through the process of creating a command-line weather application using Python and the OpenWeatherMap API. By the end of this tutorial, you’ll have a fully functional program that fetches and displays current weather data for any city in the world.

Project Overview

Objective

  • Develop a command-line application that retrieves and displays current weather data for a specified city.

Tools and Technologies

  • Python 3.x: A versatile programming language known for its simplicity and readability.
  • Requests Library: A powerful HTTP library for making API calls.
  • OpenWeatherMap API: A popular API that provides weather data globally.

Key Learning Outcomes

  • API Interaction: Learn how to interact with RESTful APIs using Python.
  • JSON Data Handling: Understand how to parse and manipulate JSON data structures.
  • Error Handling: Implement robust error handling for network requests and user inputs.
  • Code Organization: Practice writing clean, maintainable, and well-structured code.

Prerequisites

Before diving into the code, ensure you have the following set up on your system.

Installing Python

If you haven’t installed Python yet, download the latest version from the official website. Follow the installation instructions specific to your operating system.

Check Python Installation:

python --version

Setting Up a Virtual Environment

It’s good practice to use a virtual environment to manage project-specific dependencies.

Create a Virtual Environment:

python -m venv weather_env

Activate the Virtual Environment:

Windows:

weather_env\Scripts\activate

macOS/Linux:

source weather_env/bin/activate

Installing Required Libraries

Install the requests library, which we'll use to make HTTP requests.

pip install requests

Obtaining an OpenWeatherMap API Key

  1. Sign Up: Go to the OpenWeatherMap website and create a free account.
  2. Generate API Key: After verifying your email, navigate to the API keys section in your profile and generate a new API key.
  3. Note the Key: Keep your API key safe; you’ll need it to authenticate your API requests.

Understanding APIs and JSON

Before we start coding, let’s understand some fundamental concepts.

What is an API?

An Application Programming Interface (API) allows different software applications to communicate with each other. In this project, we’ll use the OpenWeatherMap API to retrieve weather data.

Introduction to JSON

JSON (JavaScript Object Notation) is a lightweight data-interchange format that’s easy for humans to read and write. The OpenWeatherMap API returns data in JSON format, which we’ll parse in our application.

Sample JSON Response:

{
"weather": [{
"description": "light rain"
}],
"main": {
"temp": 15.0,
"humidity": 82
},
"wind": {
"speed": 4.6
},
"sys": {
"country": "GB"
},
"name": "London"
}

Step-by-Step Guide

1. Project Structure

Create a new directory for your project and navigate into it.

mkdir weather_app
cd weather_app

Create a Python script file.

touch weather_app.py

2. Importing Necessary Libraries

At the top of your weather_app.py file, import the required libraries.

import requests
import sys
  • requests: To make HTTP requests to the API.
  • sys: To handle command-line arguments and system exits.

3. Defining the Main Function

The main function serves as the entry point of the program.

def main():
if len(sys.argv) != 2:
print("Usage: python weather_app.py <city_name>")
sys.exit(1)
city = sys.argv[1]
get_weather(city)
  • Argument Checking: Ensures the user provides exactly one argument (the city name).
  • Function Call: Passes the city name to the get_weather function.

4. Building the API Request

Construct the URL for the API call.

def get_weather(city):
api_key = 'YOUR_API_KEY' # Replace with your actual API key
base_url = 'http://api.openweathermap.org/data/2.5/weather?'
units = 'metric' # Use 'imperial' for Fahrenheit
complete_url = f"{base_url}appid={api_key}&q={city}&units={units}"

Parameters:

  • appid: Your API key.
  • q: The city name.
  • units: Measurement units (metric or imperial).

5. Fetching Weather Data

Make the API request and parse the JSON response.

try:
response = requests.get(complete_url)
data = response.json()
  • requests.get(): Sends a GET request to the specified URL.
  • response.json(): Parses the response as JSON.

6. Handling API Responses

Check if the API call was successful.

if data['cod'] != 200:
print(f"City '{city}' not found. Please check the city name.")
sys.exit(1)
else:
show_weather(data)

data[‘cod’]:

  • The HTTP status code returned by the API.
  • 200 indicates success.
  • Other codes indicate various errors.

7. Parsing and Displaying Weather Information

Extract relevant information from the JSON data.

def show_weather(data):
city = data['name']
country = data['sys']['country']
temp = data['main']['temp']
feels_like = data['main']['feels_like']
weather_desc = data['weather'][0]['description'].title()
humidity = data['main']['humidity']
wind_speed = data['wind']['speed']
print(f"Weather in {city}, {country}:")
print(f"Temperature : {temp}°C")
print(f"Feels Like : {feels_like}°C")
print(f"Condition : {weather_desc}")
print(f"Humidity : {humidity}%")
print(f"Wind Speed : {wind_speed} m/s")
  • Data Points:
  • City and Country: data['name'], data['sys']['country']
  • Temperature: data['main']['temp']
  • Feels Like: data['main']['feels_like']
  • Weather Description: data['weather'][0]['description']
  • Humidity: data['main']['humidity']
  • Wind Speed: data['wind']['speed']

8. Enhancing User Experience

Format the output for better readability.

print("\n==============================")
print(f" Weather in {city}, {country}")
print("==============================\n")
print(f"Temperature : {temp}°C")
print(f"Feels Like : {feels_like}°C")
print(f"Condition : {weather_desc}")
print(f"Humidity : {humidity}%")
print(f"Wind Speed : {wind_speed} m/s\n")
  • Visual Separation: Adds separators for clarity.
  • Alignment: Ensures the output is neatly aligned.

9. Error Handling and Exceptions

Handle network-related errors.

except requests.exceptions.RequestException as e:
print("Error connecting to the weather service.")
print(f"Details: {e}")
sys.exit(1)

Exceptions Covered:

  • Network connectivity issues.
  • Timeouts.
  • DNS failures.

10. Running and Testing the Application

Ensure the script runs when executed directly.

if __name__ == '__main__':
main()
  • Entry Point: Allows the script to be imported without executing the main function.

Complete Code

Here’s the complete script with all the enhancements:

import requests
import sys

def main():
if len(sys.argv) != 2:
print("Usage: python weather_app.py <city_name>")
sys.exit(1)
city = sys.argv[1]
get_weather(city)

def get_weather(city):
api_key = 'YOUR_API_KEY' # Replace with your actual API key
base_url = 'http://api.openweathermap.org/data/2.5/weather?'
units = 'metric' # Use 'imperial' for Fahrenheit
complete_url = f"{base_url}appid={api_key}&q={city}&units={units}"

try:
response = requests.get(complete_url)
data = response.json()

if data['cod'] != 200:
print(f"City '{city}' not found. Please check the city name.")
sys.exit(1)
else:
show_weather(data)
except requests.exceptions.RequestException as e:
print("Error connecting to the weather service.")
print(f"Details: {e}")
sys.exit(1)

def show_weather(data):
city = data['name']
country = data['sys']['country']
temp = data['main']['temp']
feels_like = data['main']['feels_like']
weather_desc = data['weather'][0]['description'].title()
humidity = data['main']['humidity']
wind_speed = data['wind']['speed']

print("\n==============================")
print(f" Weather in {city}, {country}")
print("==============================\n")
print(f"Temperature : {temp}°C")
print(f"Feels Like : {feels_like}°C")
print(f"Condition : {weather_desc}")
print(f"Humidity : {humidity}%")
print(f"Wind Speed : {wind_speed} m/s\n")

if __name__ == '__main__':
main()

How the Code Works

Detailed Code Explanation

Let’s break down the code step by step.

Import Statements

import requests
import sys
  • requests: Used for making HTTP requests to the API.
  • sys: Provides access to variables and functions that interact with the Python interpreter.

Main Function

def main():
if len(sys.argv) != 2:
print("Usage: python weather_app.py <city_name>")
sys.exit(1)
city = sys.argv[1]
get_weather(city)
  • Argument Length Check: Ensures the user has provided exactly one argument.
  • Usage Message: Informs the user how to use the script if incorrect arguments are supplied.
  • Exit Code: sys.exit(1) exits the script with a status code of 1, indicating an error.

Get Weather Function

def get_weather(city):
api_key = 'YOUR_API_KEY'
base_url = 'http://api.openweathermap.org/data/2.5/weather?'
units = 'metric'
complete_url = f"{base_url}appid={api_key}&q={city}&units={units}"
try:
response = requests.get(complete_url)
data = response.json()
if data['cod'] != 200:
print(f"City '{city}' not found. Please check the city name.")
sys.exit(1)
else:
show_weather(data)
except requests.exceptions.RequestException as e:
print("Error connecting to the weather service.")
print(f"Details: {e}")
sys.exit(1)
  • API Key: Replace 'YOUR_API_KEY' with your actual API key.
  • Complete URL: Constructs the full API endpoint with parameters.
  • Try-Except Block: Handles exceptions that may occur during the HTTP request.
  • Response Check: Validates the HTTP status code returned by the API.

Show Weather Function

def show_weather(data):
city = data['name']
country = data['sys']['country']
temp = data['main']['temp']
feels_like = data['main']['feels_like']
weather_desc = data['weather'][0]['description'].title()
humidity = data['main']['humidity']
wind_speed = data['wind']['speed']
print("\n==============================")
print(f" Weather in {city}, {country}")
print("==============================\n")
print(f"Temperature : {temp}°C")
print(f"Feels Like : {feels_like}°C")
print(f"Condition : {weather_desc}")
print(f"Humidity : {humidity}%")
]print(f"Wind Speed : {wind_speed} m/s\n")
  • Data Extraction: Retrieves data from the JSON response.
  • Output Formatting: Prints the weather information in a readable format.

Entry Point Check

if __name__ == '__main__':
main()
  • Ensures that the main function is called only when the script is run directly.

Extending the Application

Now that we have a basic weather application, let’s explore ways to enhance it.

Adding Forecast Data

You can modify the application to display a weather forecast for the next few days.

  • API Endpoint: Use the /forecast endpoint from OpenWeatherMap.
  • Implementation:
def get_forecast(city):
# Similar structure to get_weather(), but use the forecast endpoint
  • Display: Loop through the forecast data and display it.

Supporting Multiple Units (Metric and Imperial)

Allow the user to choose between Celsius and Fahrenheit.

Command-Line Argument: Accept an optional parameter for units.

if len(sys.argv) < 2 or len(sys.argv) > 3:
print("Usage: python weather_app.py <city_name> [units]")
sys.exit(1)
city = sys.argv[1]
units = sys.argv[2] if len(sys.argv) == 3 else 'metric'

Validating Units:

if units not in ['metric', 'imperial']:
print("Units must be 'metric' or 'imperial'.")
sys.exit(1)

Creating a GUI Version

Transform the command-line application into a graphical user interface.

  • Libraries:
  • Tkinter: Python’s standard GUI library.
  • PyQt: A set of Python bindings for Qt application framework.
  • Implementation: Create input fields for the city name and display the weather data in labels or text areas.

Error Logging

Implement logging to keep track of errors and application flow.

Python Logging Module:

import logging

logging.basicConfig(filename='app.log', level=logging.ERROR)

Usage:

except requests.exceptions.RequestException as e:
logging.error(f"Network error: {e}")
print("Error connecting to the weather service.")
sys.exit(1)

Best Practices

API Key Security

  • Never Hard-Code API Keys: Avoid placing API keys directly in your code.
  • Use Environment Variables:
import os

api_key = os.getenv('OPENWEATHER_API_KEY')

Setting Environment Variable:

  • Windows:
set OPENWEATHER_API_KEY=your_api_key
  • macOS/Linux:
export OPENWEATHER_API_KEY=your_api_key

Code Readability and Maintenance

  • Comments and Documentation: Write comments to explain complex sections.
  • Function Decomposition: Break down code into smaller, reusable functions.
  • PEP 8 Compliance: Follow Python’s style guidelines for code formatting.

Handling Edge Cases

  • Invalid Inputs: Handle cases where the city name is misspelled or empty.
  • API Rate Limits: Be aware of the API’s rate limits to prevent service denial.
  • Internationalization: Support city names in different languages.

Conclusion

In this tutorial, we’ve built a functional command-line weather application using Python and the OpenWeatherMap API. This project serves as a practical example of how to interact with web APIs, parse JSON data, and handle exceptions.

We’ve also explored ways to extend the application, improve user experience, and adhere to best coding practices. Whether you’re a beginner or an experienced developer, projects like this are valuable for honing your skills and understanding real-world programming challenges.

Additional Resources

--

--

Noah Weston
Noah Weston

No responses yet