Create a Weather App Using a Public API and Flask

Welcome, budding developers! Have you ever wondered how websites show you the current weather for your city? It’s not magic, but rather a clever combination of web technologies talking to each other. In this blog post, we’re going to embark on an exciting journey to build our very own simple weather application using Flask, a lightweight web framework for Python, and a public API to fetch real-time weather data.

Don’t worry if these terms sound a bit daunting; we’ll break down everything into easy-to-understand steps. By the end of this guide, you’ll have a functional web app that can tell you the weather for any city you search for!

What You’ll Learn

  • How to set up a basic Flask web application.
  • What an API is and how to use it to get data.
  • How to make web requests in Python to fetch external data.
  • How to display dynamic (changing) data on a web page.
  • The basics of JSON, a common format for sending data.

Prerequisites

Before we start coding, please make sure you have the following installed on your computer:

  • Python 3: You can download it from the official Python website.
  • pip: This is Python’s package installer, and it usually comes with Python.

Once Python is ready, open your terminal (on macOS/Linux) or Command Prompt/PowerShell (on Windows) and install the necessary libraries:

  • Flask: Our web framework.
  • Requests: A wonderful library for making web requests (like asking a server for data).
pip install Flask requests

Understanding APIs: Your Data Doorway

Before we dive into Flask, let’s understand the “API” part.

What is an API?

API stands for Application Programming Interface. Think of it like a menu at a restaurant. You don’t go into the kitchen to cook your food; you tell the waiter what you want from the menu, and the kitchen prepares it and sends it back to you.

Similarly, an API allows different software applications to talk to each other. In our case, our Flask app will “talk” to a weather service’s API, asking for weather information for a specific city. The weather service will then send that information back to our app.

Why use a Weather API?

Instead of trying to collect weather data ourselves (which would be incredibly complicated and require sensors and lots of complex calculations!), we can simply ask a specialized service that already collects and organizes this data. They provide an API for us to easily access it.

Choosing a Weather API: OpenWeatherMap

For this project, we’ll use OpenWeatherMap. It’s a popular and free-to-use (with limitations) service that provides current weather data.

Getting Your API Key

To use the OpenWeatherMap API, you’ll need a unique identifier called an API key. This key tells OpenWeatherMap who is asking for the data.

  1. Go to the OpenWeatherMap website.
  2. Sign up for a free account.
  3. Once logged in, go to your profile (usually found by clicking your username) and then navigate to the “API keys” tab.
  4. You’ll see a default API key, or you can create a new one. Copy this key; we’ll need it soon!
    • API Key (Supplementary Explanation): Think of an API key as your unique password or ID card that grants you access to use a specific service’s API. It helps the service know who is making requests and manage usage.

Setting Up Your Flask Project

Let’s organize our project files. Create a new folder for your project, say weather_app, and inside it, create the following structure:

weather_app/
├── app.py
└── templates/
    └── index.html
  • app.py: This will be our main Python file where our Flask application lives.
  • templates/: Flask looks for HTML files (our web page designs) inside this folder by default.
  • index.html: Our single web page where users will enter a city and see the weather.

Fetching Weather Data with Python’s requests Library

First, let’s see how we can get weather data from OpenWeatherMap using Python.

The API Endpoint

Every API has specific web addresses, called endpoints, that you send your requests to. For current weather data from OpenWeatherMap, the endpoint looks something like this:

https://api.openweathermap.org/data/2.5/weather?q={city_name}&appid={your_api_key}&units=metric

Let’s break down the parts:

  • https://api.openweathermap.org/data/2.5/weather: The base URL for current weather data.
  • ?: Separates the base URL from the parameters (extra information) we’re sending.
  • q={city_name}: This is where we tell the API which city we want weather for.
  • appid={your_api_key}: This is where you put the API key you copied earlier.
  • units=metric: This tells the API to give us temperatures in Celsius (use units=imperial for Fahrenheit).

Making the Request and Handling JSON

When the API sends back the weather data, it typically does so in a format called JSON.

  • JSON (Supplementary Explanation): Stands for JavaScript Object Notation. It’s a simple, human-readable way to store and exchange data, often looking like a dictionary or list in Python. For example: {"city": "London", "temperature": 15}.

Here’s how we’d make a request and print the JSON response using Python:

import requests # We need this to make web requests

API_KEY = "YOUR_OPENWEATHERMAP_API_KEY"
BASE_URL = "https://api.openweathermap.org/data/2.5/weather"

def get_weather(city):
    params = {
        'q': city,
        'appid': API_KEY,
        'units': 'metric' # Or 'imperial' for Fahrenheit
    }
    response = requests.get(BASE_URL, params=params)

    # Check if the request was successful (status code 200 means OK)
    if response.status_code == 200:
        data = response.json() # Convert the JSON response into a Python dictionary
        return data
    else:
        print(f"Error fetching data: {response.status_code} - {response.text}")
        return None

if __name__ == "__main__":
    city_name = input("Enter city name: ")
    weather_data = get_weather(city_name)
    if weather_data:
        # You can explore the 'data' dictionary to find specific info
        # For example, to get temperature:
        temperature = weather_data['main']['temp']
        description = weather_data['weather'][0]['description']
        print(f"Weather in {city_name}: {temperature}°C, {description}")

Try running this script! It should ask for a city and then print out some weather info.

Integrating with Flask: Building Our Web App

Now, let’s bring Flask into the picture to create a web interface.

Building app.py

This file will handle our web requests, call the get_weather function, and then show the results on our web page.

from flask import Flask, render_template, request
import requests

app = Flask(__name__)

API_KEY = "YOUR_OPENWEATHERMAP_API_KEY"
BASE_URL = "https://api.openweathermap.org/data/2.5/weather"

def get_weather_data(city):
    params = {
        'q': city,
        'appid': API_KEY,
        'units': 'metric'
    }
    response = requests.get(BASE_URL, params=params)

    if response.status_code == 200:
        data = response.json()
        return {
            'city': data['name'],
            'temperature': data['main']['temp'],
            'description': data['weather'][0]['description'],
            'humidity': data['main']['humidity'],
            'wind_speed': data['wind']['speed']
        }
    else:
        return None

@app.route('/', methods=['GET', 'POST'])
def index():
    weather_info = None
    error_message = None

    if request.method == 'POST':
        city = request.form['city'] # Get the city name from the form
        if city:
            weather_info = get_weather_data(city)
            if not weather_info:
                error_message = "Could not retrieve weather for that city. Please try again."
        else:
            error_message = "Please enter a city name."

    # Render the HTML template, passing weather_info and error_message
    return render_template('index.html', weather=weather_info, error=error_message)

if __name__ == '__main__':
    app.run(debug=True)

In this app.py file:

  • @app.route('/'): This tells Flask what to do when someone visits the main page (/) of our website.
  • methods=['GET', 'POST']: Our page will handle both GET requests (when you first visit) and POST requests (when you submit the form).
  • request.form['city']: This is how we get the data (the city name) that the user typed into the form on our web page.
  • render_template('index.html', weather=weather_info, error=error_message): This tells Flask to load our index.html file and pass it the weather_info (if available) and any error_message we might have. These pieces of data will be available inside our index.html file.

Creating the HTML Template (templates/index.html)

Now, let’s create the web page itself. This file will contain an input field for the city and display the weather data. We’ll use Jinja2 syntax (Flask’s templating engine) to show dynamic data.

  • Jinja2 (Supplementary Explanation): A templating engine helps you mix Python code (like variables and loops) directly into your HTML. It allows you to create dynamic web pages that change based on the data you pass to them.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Simple Weather App</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f4;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            margin: 0;
            flex-direction: column;
        }
        .container {
            background-color: #fff;
            padding: 20px 40px;
            border-radius: 8px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
            text-align: center;
            max-width: 400px;
            width: 100%;
        }
        h1 {
            color: #333;
            margin-bottom: 20px;
        }
        form {
            margin-bottom: 20px;
        }
        input[type="text"] {
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 4px;
            width: calc(100% - 22px);
            margin-right: 10px;
            font-size: 16px;
        }
        button {
            padding: 10px 15px;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
        }
        button:hover {
            background-color: #0056b3;
        }
        .weather-result {
            margin-top: 20px;
            border-top: 1px solid #eee;
            padding-top: 20px;
        }
        .weather-result h2 {
            color: #555;
            margin-bottom: 10px;
        }
        .weather-result p {
            font-size: 1.1em;
            color: #666;
            margin: 5px 0;
        }
        .error-message {
            color: red;
            margin-top: 15px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Weather Checker</h1>
        <form method="POST">
            <input type="text" name="city" placeholder="Enter city name" required>
            <button type="submit">Get Weather</button>
        </form>

        {% if error %}
            <p class="error-message">{{ error }}</p>
        {% endif %}

        {% if weather %}
        <div class="weather-result">
            <h2>{{ weather.city }}</h2>
            <p><strong>Temperature:</strong> {{ weather.temperature }}°C</p>
            <p><strong>Description:</strong> {{ weather.description.capitalize() }}</p>
            <p><strong>Humidity:</strong> {{ weather.humidity }}%</p>
            <p><strong>Wind Speed:</strong> {{ weather.wind_speed }} m/s</p>
        </div>
        {% endif %}
    </div>
</body>
</html>

Key things to note in index.html:

  • <form method="POST">: This form will send its data back to our Flask app using a POST request.
  • <input type="text" name="city">: The name="city" part is crucial! This is how Flask identifies the data when you submit the form (remember request.form['city'] in app.py).
  • {% if weather %}{% endif %}: This is Jinja2 syntax. It means “if the weather variable has data (i.e., we successfully got weather info), then display the content inside this block.”
  • {{ weather.city }}: This is also Jinja2. It means “display the city value from the weather variable that was passed from app.py.”

Running Your Application

  1. Save everything: Make sure app.py is in your weather_app folder and index.html is inside the weather_app/templates folder.
  2. Open your terminal/command prompt and navigate to your weather_app folder using the cd command.
    bash
    cd weather_app
  3. Run your Flask app:
    bash
    python app.py

    You should see output similar to:
    “`

    • Serving Flask app ‘app’
    • Debug mode: on
      INFO: This is a development server. Do not use it in a production deployment.
      Use a production WSGI server instead.
    • Running on http://127.0.0.1:5000
      Press CTRL+C to quit
      “`
  4. Open your web browser and go to http://127.0.0.1:5000.

You should now see your simple weather app! Enter a city name, click “Get Weather,” and behold the real-time weather information.

Conclusion

Congratulations! You’ve successfully built a basic weather application using Flask and integrated a public API to fetch dynamic data. You’ve touched upon core concepts like web frameworks, APIs, HTTP requests, JSON, and templating engines.

This is just the beginning! You can expand this app by:

  • Adding more styling with CSS.
  • Displaying additional weather details (like wind direction, sunrise/sunset times).
  • Implementing error handling for invalid city names more gracefully.
  • Adding a feature to save favorite cities.

Keep experimenting and happy coding!

Comments

Leave a Reply