Get the "Applied Data Science Edge"!

The ViralML School

Fundamental Market Analysis with Python - Find Your Own Answers On What Is Going on in the Financial Markets

Web Work

Python Web Work - Prototyping Guide for Maker

Use HTML5 Templates, Serve Dynamic Content, Build Machine Learning Web Apps, Grow Audiences & Conquer the World!

Hot off the Press!

The Little Book of Fundamental Market Indicators

My New Book: "The Little Book of Fundamental Analysis: Hands-On Market Analysis with Python" is Out!

Worldwide Weather Forecast Web App - Starting a New Business - Part 8

Introduction

Let's build a web app to forecast weather anywhere around the world. Our app will take a city name and you will return the 5-day forecast in icons! If you follow the video and use PythonAnywere, you will be able to use it on your mobile phone.



If you liked it, please share it:

Code

ViralML-Minimal-Weather-Forecast-Web-Application-Strating-New-Business
In [17]:
from IPython.display import Image
Image(filename='viralml-book.png', width=600)
Out[17]:

Starting a Business? Part 8

Let's build a simple weather forecasting web application using the MVP Light Stack!

In [18]:
Image(filename='five-day-forecast-web-app.png', width=500)
Out[18]:

Starting a Business? Part 8

Let's build a simple weather forecasting web application using the MVP Light Stack!

You will need two free accounts to follow this walkthrough:

  • PythonAnywhere.com
  • OpenWeatherMap.org

Signup for a "Free" account at OpenWeatherMap (https://openweathermap.org/price) and create an API key to follow along.

Singup for a "Beginner Free" account at PythonAnywhere (https://www.pythonanywhere.com/pricing/).

Once you have both accounts confirmed, you are ready to follow along and create a simple weather forecasting tool that anybody around the world can use!!

In [2]:
YOUR_OPENWEATHERMAP_API_KEY = 'ADD-YOUR-API-KEY-HERE'

Weather rest API function call

In [3]:
# bring in real weather data using openweathermap and json.load
from urllib.request import urlopen
import json
weather_json = json.load(
    urlopen("http://api.openweathermap.org/data/2.5/weather?q=Barcelona&appid=" + YOUR_OPENWEATHERMAP_API_KEY))
weather_json
Out[3]:
{'coord': {'lon': 2.18, 'lat': 41.38},
 'weather': [{'id': 802,
   'main': 'Clouds',
   'description': 'scattered clouds',
   'icon': '03d'}],
 'base': 'stations',
 'main': {'temp': 295.08,
  'pressure': 1018,
  'humidity': 73,
  'temp_min': 293.15,
  'temp_max': 297.59},
 'visibility': 10000,
 'wind': {'speed': 5.1, 'deg': 120},
 'clouds': {'all': 40},
 'dt': 1570365806,
 'sys': {'type': 1,
  'id': 6398,
  'message': 0.0081,
  'country': 'ES',
  'sunrise': 1570341158,
  'sunset': 1570382769},
 'timezone': 7200,
 'id': 3128760,
 'name': 'Barcelona',
 'cod': 200}

Forecast rest API function call

In [4]:
selected_location = "Seattle"
openweathermap_url = "http://api.openweathermap.org/data/2.5/forecast?q=" + selected_location + "&APPID=" + YOUR_OPENWEATHERMAP_API_KEY

weather_forecast_json = json.load(urlopen(openweathermap_url))

weather_forecast_json
Out[4]:
{'cod': '200',
 'message': 0.0115,
 'cnt': 40,
 'list': [{'dt': 1570374000,
   'main': {'temp': 279.74,
    'temp_min': 279.74,
    'temp_max': 281.173,
    'pressure': 1027.37,
    'sea_level': 1027.37,
    'grnd_level': 1019.25,
    'humidity': 94,
    'temp_kf': -1.44},
   'weather': [{'id': 803,
     'main': 'Clouds',
     'description': 'broken clouds',
     'icon': '04d'}],
   'clouds': {'all': 58},
   'wind': {'speed': 1.78, 'deg': 21.873},
   'sys': {'pod': 'd'},
   'dt_txt': '2019-10-06 15:00:00'},
  {'dt': 1570384800,
   'main': {'temp': 286.14,
    'temp_min': 286.14,
    'temp_max': 287.221,
    'pressure': 1025.73,
    'sea_level': 1025.73,
    'grnd_level': 1018.36,
    'humidity': 59,
    'temp_kf': -1.08},
   'weather': [{'id': 803,
     'main': 'Clouds',
     'description': 'broken clouds',
     'icon': '04d'}],
   'clouds': {'all': 70},
   'wind': {'speed': 2.16, 'deg': 8.055},
   'sys': {'pod': 'd'},
   'dt_txt': '2019-10-06 18:00:00'},
  {'dt': 1570395600,
   'main': {'temp': 289.74,
    'temp_min': 289.74,
    'temp_max': 290.458,
    'pressure': 1023.09,
    'sea_level': 1023.09,
    'grnd_level': 1015.82,
    'humidity': 46,
    'temp_kf': -0.72},
   'weather': [{'id': 803,
     'main': 'Clouds',
     'description': 'broken clouds',
     'icon': '04d'}],
   'clouds': {'all': 52},
   'wind': {'speed': 3.02, 'deg': 10.332},
   'sys': {'pod': 'd'},
   'dt_txt': '2019-10-06 21:00:00'},
  {'dt': 1570406400,
   'main': {'temp': 287.69,
    'temp_min': 287.69,
    'temp_max': 288.047,
    'pressure': 1021.47,
    'sea_level': 1021.47,
    'grnd_level': 1014.22,
    'humidity': 55,
    'temp_kf': -0.36},
   'weather': [{'id': 803,
     'main': 'Clouds',
     'description': 'broken clouds',
     'icon': '04n'}],
   'clouds': {'all': 56},
   'wind': {'speed': 2.39, 'deg': 1.584},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-07 00:00:00'},
  {'dt': 1570417200,
   'main': {'temp': 283.366,
    'temp_min': 283.366,
    'temp_max': 283.366,
    'pressure': 1021.42,
    'sea_level': 1021.42,
    'grnd_level': 1013.74,
    'humidity': 75,
    'temp_kf': 0},
   'weather': [{'id': 804,
     'main': 'Clouds',
     'description': 'overcast clouds',
     'icon': '04n'}],
   'clouds': {'all': 100},
   'wind': {'speed': 1.75, 'deg': 4.069},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-07 03:00:00'},
  {'dt': 1570428000,
   'main': {'temp': 282.598,
    'temp_min': 282.598,
    'temp_max': 282.598,
    'pressure': 1021.01,
    'sea_level': 1021.01,
    'grnd_level': 1013.42,
    'humidity': 77,
    'temp_kf': 0},
   'weather': [{'id': 804,
     'main': 'Clouds',
     'description': 'overcast clouds',
     'icon': '04n'}],
   'clouds': {'all': 88},
   'wind': {'speed': 0.91, 'deg': 3.956},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-07 06:00:00'},
  {'dt': 1570438800,
   'main': {'temp': 282.31,
    'temp_min': 282.31,
    'temp_max': 282.31,
    'pressure': 1019.15,
    'sea_level': 1019.15,
    'grnd_level': 1011.52,
    'humidity': 77,
    'temp_kf': 0},
   'weather': [{'id': 802,
     'main': 'Clouds',
     'description': 'scattered clouds',
     'icon': '03n'}],
   'clouds': {'all': 48},
   'wind': {'speed': 0.95, 'deg': 178.497},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-07 09:00:00'},
  {'dt': 1570449600,
   'main': {'temp': 282.8,
    'temp_min': 282.8,
    'temp_max': 282.8,
    'pressure': 1018.24,
    'sea_level': 1018.24,
    'grnd_level': 1010.3,
    'humidity': 72,
    'temp_kf': 0},
   'weather': [{'id': 803,
     'main': 'Clouds',
     'description': 'broken clouds',
     'icon': '04n'}],
   'clouds': {'all': 74},
   'wind': {'speed': 2.46, 'deg': 184.977},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-07 12:00:00'},
  {'dt': 1570460400,
   'main': {'temp': 282.824,
    'temp_min': 282.824,
    'temp_max': 282.824,
    'pressure': 1016.39,
    'sea_level': 1016.39,
    'grnd_level': 1008.38,
    'humidity': 77,
    'temp_kf': 0},
   'weather': [{'id': 500,
     'main': 'Rain',
     'description': 'light rain',
     'icon': '10d'}],
   'clouds': {'all': 100},
   'wind': {'speed': 4.47, 'deg': 190.339},
   'rain': {'3h': 0.625},
   'sys': {'pod': 'd'},
   'dt_txt': '2019-10-07 15:00:00'},
  {'dt': 1570471200,
   'main': {'temp': 285.276,
    'temp_min': 285.276,
    'temp_max': 285.276,
    'pressure': 1015.15,
    'sea_level': 1015.15,
    'grnd_level': 1007.31,
    'humidity': 70,
    'temp_kf': 0},
   'weather': [{'id': 804,
     'main': 'Clouds',
     'description': 'overcast clouds',
     'icon': '04d'}],
   'clouds': {'all': 99},
   'wind': {'speed': 5.58, 'deg': 202.686},
   'rain': {},
   'sys': {'pod': 'd'},
   'dt_txt': '2019-10-07 18:00:00'},
  {'dt': 1570482000,
   'main': {'temp': 286.459,
    'temp_min': 286.459,
    'temp_max': 286.459,
    'pressure': 1012.82,
    'sea_level': 1012.82,
    'grnd_level': 1005.54,
    'humidity': 88,
    'temp_kf': 0},
   'weather': [{'id': 500,
     'main': 'Rain',
     'description': 'light rain',
     'icon': '10d'}],
   'clouds': {'all': 100},
   'wind': {'speed': 6.51, 'deg': 205.113},
   'rain': {'3h': 1.875},
   'sys': {'pod': 'd'},
   'dt_txt': '2019-10-07 21:00:00'},
  {'dt': 1570492800,
   'main': {'temp': 287.505,
    'temp_min': 287.505,
    'temp_max': 287.505,
    'pressure': 1010.86,
    'sea_level': 1010.86,
    'grnd_level': 1003.23,
    'humidity': 92,
    'temp_kf': 0},
   'weather': [{'id': 500,
     'main': 'Rain',
     'description': 'light rain',
     'icon': '10n'}],
   'clouds': {'all': 100},
   'wind': {'speed': 6.63, 'deg': 203.292},
   'rain': {'3h': 0.313},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-08 00:00:00'},
  {'dt': 1570503600,
   'main': {'temp': 286.788,
    'temp_min': 286.788,
    'temp_max': 286.788,
    'pressure': 1009.8,
    'sea_level': 1009.8,
    'grnd_level': 1001.9,
    'humidity': 86,
    'temp_kf': 0},
   'weather': [{'id': 804,
     'main': 'Clouds',
     'description': 'overcast clouds',
     'icon': '04n'}],
   'clouds': {'all': 100},
   'wind': {'speed': 5.77, 'deg': 209.005},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-08 03:00:00'},
  {'dt': 1570514400,
   'main': {'temp': 284.223,
    'temp_min': 284.223,
    'temp_max': 284.223,
    'pressure': 1011.3,
    'sea_level': 1011.3,
    'grnd_level': 1003.26,
    'humidity': 83,
    'temp_kf': 0},
   'weather': [{'id': 804,
     'main': 'Clouds',
     'description': 'overcast clouds',
     'icon': '04n'}],
   'clouds': {'all': 100},
   'wind': {'speed': 4, 'deg': 229.663},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-08 06:00:00'},
  {'dt': 1570525200,
   'main': {'temp': 281,
    'temp_min': 281,
    'temp_max': 281,
    'pressure': 1012.21,
    'sea_level': 1012.21,
    'grnd_level': 1004.39,
    'humidity': 80,
    'temp_kf': 0},
   'weather': [{'id': 800,
     'main': 'Clear',
     'description': 'clear sky',
     'icon': '01n'}],
   'clouds': {'all': 0},
   'wind': {'speed': 3.46, 'deg': 108.178},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-08 09:00:00'},
  {'dt': 1570536000,
   'main': {'temp': 280.4,
    'temp_min': 280.4,
    'temp_max': 280.4,
    'pressure': 1013.3,
    'sea_level': 1013.3,
    'grnd_level': 1004.25,
    'humidity': 81,
    'temp_kf': 0},
   'weather': [{'id': 500,
     'main': 'Rain',
     'description': 'light rain',
     'icon': '10n'}],
   'clouds': {'all': 35},
   'wind': {'speed': 0.19, 'deg': 189.919},
   'rain': {'3h': 0.25},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-08 12:00:00'},
  {'dt': 1570546800,
   'main': {'temp': 278.424,
    'temp_min': 278.424,
    'temp_max': 278.424,
    'pressure': 1013.55,
    'sea_level': 1013.55,
    'grnd_level': 1004.22,
    'humidity': 92,
    'temp_kf': 0},
   'weather': [{'id': 500,
     'main': 'Rain',
     'description': 'light rain',
     'icon': '10d'}],
   'clouds': {'all': 22},
   'wind': {'speed': 1.74, 'deg': 204.853},
   'rain': {'3h': 0.312},
   'sys': {'pod': 'd'},
   'dt_txt': '2019-10-08 15:00:00'},
  {'dt': 1570557600,
   'main': {'temp': 284.1,
    'temp_min': 284.1,
    'temp_max': 284.1,
    'pressure': 1013.48,
    'sea_level': 1013.48,
    'grnd_level': 1004.82,
    'humidity': 66,
    'temp_kf': 0},
   'weather': [{'id': 500,
     'main': 'Rain',
     'description': 'light rain',
     'icon': '10d'}],
   'clouds': {'all': 44},
   'wind': {'speed': 1.09, 'deg': 267.785},
   'rain': {'3h': 0.876},
   'sys': {'pod': 'd'},
   'dt_txt': '2019-10-08 18:00:00'},
  {'dt': 1570568400,
   'main': {'temp': 283.371,
    'temp_min': 283.371,
    'temp_max': 283.371,
    'pressure': 1013.26,
    'sea_level': 1013.26,
    'grnd_level': 1005.32,
    'humidity': 66,
    'temp_kf': 0},
   'weather': [{'id': 500,
     'main': 'Rain',
     'description': 'light rain',
     'icon': '10d'}],
   'clouds': {'all': 97},
   'wind': {'speed': 2.99, 'deg': 50.204},
   'rain': {'3h': 1.5},
   'sys': {'pod': 'd'},
   'dt_txt': '2019-10-08 21:00:00'},
  {'dt': 1570579200,
   'main': {'temp': 282.912,
    'temp_min': 282.912,
    'temp_max': 282.912,
    'pressure': 1014.24,
    'sea_level': 1014.24,
    'grnd_level': 1006.6,
    'humidity': 70,
    'temp_kf': 0},
   'weather': [{'id': 500,
     'main': 'Rain',
     'description': 'light rain',
     'icon': '10n'}],
   'clouds': {'all': 99},
   'wind': {'speed': 3.11, 'deg': 12.673},
   'rain': {'3h': 0.562},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-09 00:00:00'},
  {'dt': 1570590000,
   'main': {'temp': 279.42,
    'temp_min': 279.42,
    'temp_max': 279.42,
    'pressure': 1017.2,
    'sea_level': 1017.2,
    'grnd_level': 1009.04,
    'humidity': 81,
    'temp_kf': 0},
   'weather': [{'id': 500,
     'main': 'Rain',
     'description': 'light rain',
     'icon': '10n'}],
   'clouds': {'all': 0},
   'wind': {'speed': 2.22, 'deg': 354.436},
   'rain': {'3h': 0.188},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-09 03:00:00'},
  {'dt': 1570600800,
   'main': {'temp': 277.57,
    'temp_min': 277.57,
    'temp_max': 277.57,
    'pressure': 1020.63,
    'sea_level': 1020.63,
    'grnd_level': 1011.62,
    'humidity': 88,
    'temp_kf': 0},
   'weather': [{'id': 800,
     'main': 'Clear',
     'description': 'clear sky',
     'icon': '01n'}],
   'clouds': {'all': 0},
   'wind': {'speed': 0.91, 'deg': 233.23},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-09 06:00:00'},
  {'dt': 1570611600,
   'main': {'temp': 276.402,
    'temp_min': 276.402,
    'temp_max': 276.402,
    'pressure': 1022.55,
    'sea_level': 1022.55,
    'grnd_level': 1013.12,
    'humidity': 93,
    'temp_kf': 0},
   'weather': [{'id': 800,
     'main': 'Clear',
     'description': 'clear sky',
     'icon': '01n'}],
   'clouds': {'all': 0},
   'wind': {'speed': 1.01, 'deg': 225.444},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-09 09:00:00'},
  {'dt': 1570622400,
   'main': {'temp': 276.3,
    'temp_min': 276.3,
    'temp_max': 276.3,
    'pressure': 1025.02,
    'sea_level': 1025.02,
    'grnd_level': 1015.89,
    'humidity': 86,
    'temp_kf': 0},
   'weather': [{'id': 800,
     'main': 'Clear',
     'description': 'clear sky',
     'icon': '01n'}],
   'clouds': {'all': 0},
   'wind': {'speed': 1.32, 'deg': 4.002},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-09 12:00:00'},
  {'dt': 1570633200,
   'main': {'temp': 276.3,
    'temp_min': 276.3,
    'temp_max': 276.3,
    'pressure': 1026.95,
    'sea_level': 1026.95,
    'grnd_level': 1018.07,
    'humidity': 70,
    'temp_kf': 0},
   'weather': [{'id': 800,
     'main': 'Clear',
     'description': 'clear sky',
     'icon': '01d'}],
   'clouds': {'all': 0},
   'wind': {'speed': 3.13, 'deg': 13.209},
   'sys': {'pod': 'd'},
   'dt_txt': '2019-10-09 15:00:00'},
  {'dt': 1570644000,
   'main': {'temp': 281.531,
    'temp_min': 281.531,
    'temp_max': 281.531,
    'pressure': 1027.51,
    'sea_level': 1027.51,
    'grnd_level': 1019.41,
    'humidity': 48,
    'temp_kf': 0},
   'weather': [{'id': 800,
     'main': 'Clear',
     'description': 'clear sky',
     'icon': '01d'}],
   'clouds': {'all': 0},
   'wind': {'speed': 4.06, 'deg': 8.166},
   'sys': {'pod': 'd'},
   'dt_txt': '2019-10-09 18:00:00'},
  {'dt': 1570654800,
   'main': {'temp': 283.956,
    'temp_min': 283.956,
    'temp_max': 283.956,
    'pressure': 1027.35,
    'sea_level': 1027.35,
    'grnd_level': 1019.56,
    'humidity': 33,
    'temp_kf': 0},
   'weather': [{'id': 800,
     'main': 'Clear',
     'description': 'clear sky',
     'icon': '01d'}],
   'clouds': {'all': 0},
   'wind': {'speed': 3.22, 'deg': 11.837},
   'sys': {'pod': 'd'},
   'dt_txt': '2019-10-09 21:00:00'},
  {'dt': 1570665600,
   'main': {'temp': 282.869,
    'temp_min': 282.869,
    'temp_max': 282.869,
    'pressure': 1027.18,
    'sea_level': 1027.18,
    'grnd_level': 1019.37,
    'humidity': 39,
    'temp_kf': 0},
   'weather': [{'id': 800,
     'main': 'Clear',
     'description': 'clear sky',
     'icon': '01n'}],
   'clouds': {'all': 0},
   'wind': {'speed': 3.27, 'deg': 9.212},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-10 00:00:00'},
  {'dt': 1570676400,
   'main': {'temp': 277.568,
    'temp_min': 277.568,
    'temp_max': 277.568,
    'pressure': 1028.94,
    'sea_level': 1028.94,
    'grnd_level': 1020.24,
    'humidity': 55,
    'temp_kf': 0},
   'weather': [{'id': 800,
     'main': 'Clear',
     'description': 'clear sky',
     'icon': '01n'}],
   'clouds': {'all': 0},
   'wind': {'speed': 2.25, 'deg': 14.655},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-10 03:00:00'},
  {'dt': 1570687200,
   'main': {'temp': 276.281,
    'temp_min': 276.281,
    'temp_max': 276.281,
    'pressure': 1030.42,
    'sea_level': 1030.42,
    'grnd_level': 1021.52,
    'humidity': 61,
    'temp_kf': 0},
   'weather': [{'id': 800,
     'main': 'Clear',
     'description': 'clear sky',
     'icon': '01n'}],
   'clouds': {'all': 0},
   'wind': {'speed': 1.56, 'deg': 351.396},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-10 06:00:00'},
  {'dt': 1570698000,
   'main': {'temp': 275.386,
    'temp_min': 275.386,
    'temp_max': 275.386,
    'pressure': 1030.14,
    'sea_level': 1030.14,
    'grnd_level': 1021.29,
    'humidity': 67,
    'temp_kf': 0},
   'weather': [{'id': 800,
     'main': 'Clear',
     'description': 'clear sky',
     'icon': '01n'}],
   'clouds': {'all': 0},
   'wind': {'speed': 1.12, 'deg': 9.563},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-10 09:00:00'},
  {'dt': 1570708800,
   'main': {'temp': 274.756,
    'temp_min': 274.756,
    'temp_max': 274.756,
    'pressure': 1030.12,
    'sea_level': 1030.12,
    'grnd_level': 1021.28,
    'humidity': 71,
    'temp_kf': 0},
   'weather': [{'id': 800,
     'main': 'Clear',
     'description': 'clear sky',
     'icon': '01n'}],
   'clouds': {'all': 0},
   'wind': {'speed': 1.08, 'deg': 359.788},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-10 12:00:00'},
  {'dt': 1570719600,
   'main': {'temp': 275.1,
    'temp_min': 275.1,
    'temp_max': 275.1,
    'pressure': 1029.58,
    'sea_level': 1029.58,
    'grnd_level': 1021.08,
    'humidity': 70,
    'temp_kf': 0},
   'weather': [{'id': 800,
     'main': 'Clear',
     'description': 'clear sky',
     'icon': '01d'}],
   'clouds': {'all': 0},
   'wind': {'speed': 0.9, 'deg': 0.832},
   'sys': {'pod': 'd'},
   'dt_txt': '2019-10-10 15:00:00'},
  {'dt': 1570730400,
   'main': {'temp': 282.963,
    'temp_min': 282.963,
    'temp_max': 282.963,
    'pressure': 1027.84,
    'sea_level': 1027.84,
    'grnd_level': 1020.24,
    'humidity': 46,
    'temp_kf': 0},
   'weather': [{'id': 800,
     'main': 'Clear',
     'description': 'clear sky',
     'icon': '01d'}],
   'clouds': {'all': 0},
   'wind': {'speed': 1.21, 'deg': 22.776},
   'sys': {'pod': 'd'},
   'dt_txt': '2019-10-10 18:00:00'},
  {'dt': 1570741200,
   'main': {'temp': 287.118,
    'temp_min': 287.118,
    'temp_max': 287.118,
    'pressure': 1026.11,
    'sea_level': 1026.11,
    'grnd_level': 1018.51,
    'humidity': 32,
    'temp_kf': 0},
   'weather': [{'id': 800,
     'main': 'Clear',
     'description': 'clear sky',
     'icon': '01d'}],
   'clouds': {'all': 0},
   'wind': {'speed': 1.47, 'deg': 20.931},
   'sys': {'pod': 'd'},
   'dt_txt': '2019-10-10 21:00:00'},
  {'dt': 1570752000,
   'main': {'temp': 286.27,
    'temp_min': 286.27,
    'temp_max': 286.27,
    'pressure': 1024.11,
    'sea_level': 1024.11,
    'grnd_level': 1016.09,
    'humidity': 37,
    'temp_kf': 0},
   'weather': [{'id': 800,
     'main': 'Clear',
     'description': 'clear sky',
     'icon': '01n'}],
   'clouds': {'all': 0},
   'wind': {'speed': 2.68, 'deg': 1.113},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-11 00:00:00'},
  {'dt': 1570762800,
   'main': {'temp': 279.174,
    'temp_min': 279.174,
    'temp_max': 279.174,
    'pressure': 1024.67,
    'sea_level': 1024.67,
    'grnd_level': 1015.73,
    'humidity': 62,
    'temp_kf': 0},
   'weather': [{'id': 800,
     'main': 'Clear',
     'description': 'clear sky',
     'icon': '01n'}],
   'clouds': {'all': 6},
   'wind': {'speed': 2.53, 'deg': 3.062},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-11 03:00:00'},
  {'dt': 1570773600,
   'main': {'temp': 277.923,
    'temp_min': 277.923,
    'temp_max': 277.923,
    'pressure': 1024.1,
    'sea_level': 1024.1,
    'grnd_level': 1014.91,
    'humidity': 68,
    'temp_kf': 0},
   'weather': [{'id': 801,
     'main': 'Clouds',
     'description': 'few clouds',
     'icon': '02n'}],
   'clouds': {'all': 11},
   'wind': {'speed': 2.37, 'deg': 10.309},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-11 06:00:00'},
  {'dt': 1570784400,
   'main': {'temp': 277.497,
    'temp_min': 277.497,
    'temp_max': 277.497,
    'pressure': 1023.34,
    'sea_level': 1023.34,
    'grnd_level': 1014.21,
    'humidity': 73,
    'temp_kf': 0},
   'weather': [{'id': 803,
     'main': 'Clouds',
     'description': 'broken clouds',
     'icon': '04n'}],
   'clouds': {'all': 58},
   'wind': {'speed': 2.05, 'deg': 19.22},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-11 09:00:00'},
  {'dt': 1570795200,
   'main': {'temp': 277.008,
    'temp_min': 277.008,
    'temp_max': 277.008,
    'pressure': 1022.57,
    'sea_level': 1022.57,
    'grnd_level': 1013.62,
    'humidity': 75,
    'temp_kf': 0},
   'weather': [{'id': 803,
     'main': 'Clouds',
     'description': 'broken clouds',
     'icon': '04n'}],
   'clouds': {'all': 79},
   'wind': {'speed': 1.64, 'deg': 17.172},
   'sys': {'pod': 'n'},
   'dt_txt': '2019-10-11 12:00:00'}],
 'city': {'id': 5809844,
  'name': 'Seattle',
  'coord': {'lat': 47.6038, 'lon': -122.3301},
  'country': 'US',
  'population': 608660,
  'timezone': -25200,
  'sunrise': 1570371298,
  'sunset': 1570412382}}
In [5]:
# https://openweathermap.org/api
[w['dt_txt'] for w in weather_forecast_json['list']]
Out[5]:
['2019-10-06 15:00:00',
 '2019-10-06 18:00:00',
 '2019-10-06 21:00:00',
 '2019-10-07 00:00:00',
 '2019-10-07 03:00:00',
 '2019-10-07 06:00:00',
 '2019-10-07 09:00:00',
 '2019-10-07 12:00:00',
 '2019-10-07 15:00:00',
 '2019-10-07 18:00:00',
 '2019-10-07 21:00:00',
 '2019-10-08 00:00:00',
 '2019-10-08 03:00:00',
 '2019-10-08 06:00:00',
 '2019-10-08 09:00:00',
 '2019-10-08 12:00:00',
 '2019-10-08 15:00:00',
 '2019-10-08 18:00:00',
 '2019-10-08 21:00:00',
 '2019-10-09 00:00:00',
 '2019-10-09 03:00:00',
 '2019-10-09 06:00:00',
 '2019-10-09 09:00:00',
 '2019-10-09 12:00:00',
 '2019-10-09 15:00:00',
 '2019-10-09 18:00:00',
 '2019-10-09 21:00:00',
 '2019-10-10 00:00:00',
 '2019-10-10 03:00:00',
 '2019-10-10 06:00:00',
 '2019-10-10 09:00:00',
 '2019-10-10 12:00:00',
 '2019-10-10 15:00:00',
 '2019-10-10 18:00:00',
 '2019-10-10 21:00:00',
 '2019-10-11 00:00:00',
 '2019-10-11 03:00:00',
 '2019-10-11 06:00:00',
 '2019-10-11 09:00:00',
 '2019-10-11 12:00:00']
In [6]:
len([w['dt_txt'] for w in weather_forecast_json['list']])
Out[6]:
40

Pull weather icons

In [7]:
### Pull weather icons
for w in weather_forecast_json['list']:
    print(w['dt_txt'])
    print(w['weather'])
    print(w['weather'][0]['icon'])
2019-10-06 15:00:00
[{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04d'}]
04d
2019-10-06 18:00:00
[{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04d'}]
04d
2019-10-06 21:00:00
[{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04d'}]
04d
2019-10-07 00:00:00
[{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04n'}]
04n
2019-10-07 03:00:00
[{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04n'}]
04n
2019-10-07 06:00:00
[{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04n'}]
04n
2019-10-07 09:00:00
[{'id': 802, 'main': 'Clouds', 'description': 'scattered clouds', 'icon': '03n'}]
03n
2019-10-07 12:00:00
[{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04n'}]
04n
2019-10-07 15:00:00
[{'id': 500, 'main': 'Rain', 'description': 'light rain', 'icon': '10d'}]
10d
2019-10-07 18:00:00
[{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04d'}]
04d
2019-10-07 21:00:00
[{'id': 500, 'main': 'Rain', 'description': 'light rain', 'icon': '10d'}]
10d
2019-10-08 00:00:00
[{'id': 500, 'main': 'Rain', 'description': 'light rain', 'icon': '10n'}]
10n
2019-10-08 03:00:00
[{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04n'}]
04n
2019-10-08 06:00:00
[{'id': 804, 'main': 'Clouds', 'description': 'overcast clouds', 'icon': '04n'}]
04n
2019-10-08 09:00:00
[{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}]
01n
2019-10-08 12:00:00
[{'id': 500, 'main': 'Rain', 'description': 'light rain', 'icon': '10n'}]
10n
2019-10-08 15:00:00
[{'id': 500, 'main': 'Rain', 'description': 'light rain', 'icon': '10d'}]
10d
2019-10-08 18:00:00
[{'id': 500, 'main': 'Rain', 'description': 'light rain', 'icon': '10d'}]
10d
2019-10-08 21:00:00
[{'id': 500, 'main': 'Rain', 'description': 'light rain', 'icon': '10d'}]
10d
2019-10-09 00:00:00
[{'id': 500, 'main': 'Rain', 'description': 'light rain', 'icon': '10n'}]
10n
2019-10-09 03:00:00
[{'id': 500, 'main': 'Rain', 'description': 'light rain', 'icon': '10n'}]
10n
2019-10-09 06:00:00
[{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}]
01n
2019-10-09 09:00:00
[{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}]
01n
2019-10-09 12:00:00
[{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}]
01n
2019-10-09 15:00:00
[{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}]
01d
2019-10-09 18:00:00
[{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}]
01d
2019-10-09 21:00:00
[{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}]
01d
2019-10-10 00:00:00
[{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}]
01n
2019-10-10 03:00:00
[{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}]
01n
2019-10-10 06:00:00
[{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}]
01n
2019-10-10 09:00:00
[{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}]
01n
2019-10-10 12:00:00
[{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}]
01n
2019-10-10 15:00:00
[{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}]
01d
2019-10-10 18:00:00
[{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}]
01d
2019-10-10 21:00:00
[{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01d'}]
01d
2019-10-11 00:00:00
[{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}]
01n
2019-10-11 03:00:00
[{'id': 800, 'main': 'Clear', 'description': 'clear sky', 'icon': '01n'}]
01n
2019-10-11 06:00:00
[{'id': 801, 'main': 'Clouds', 'description': 'few clouds', 'icon': '02n'}]
02n
2019-10-11 09:00:00
[{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04n'}]
04n
2019-10-11 12:00:00
[{'id': 803, 'main': 'Clouds', 'description': 'broken clouds', 'icon': '04n'}]
04n
In [8]:
from IPython.core.display import Image, display
import time

for w in weather_forecast_json['list']:
    # slow it down, be courteous 
    time.sleep(0.1)
    print(w['dt_txt'])
    outlook_icon = w['weather'][0]['icon']  + ".png"
    display(Image("http://openweathermap.org/img/w/" + outlook_icon, unconfined=True))
    
2019-10-06 15:00:00
2019-10-06 18:00:00
2019-10-06 21:00:00
2019-10-07 00:00:00
2019-10-07 03:00:00
2019-10-07 06:00:00
2019-10-07 09:00:00
2019-10-07 12:00:00
2019-10-07 15:00:00
2019-10-07 18:00:00
2019-10-07 21:00:00
2019-10-08 00:00:00
2019-10-08 03:00:00
2019-10-08 06:00:00
2019-10-08 09:00:00
2019-10-08 12:00:00
2019-10-08 15:00:00
2019-10-08 18:00:00
2019-10-08 21:00:00
2019-10-09 00:00:00
2019-10-09 03:00:00
2019-10-09 06:00:00
2019-10-09 09:00:00
2019-10-09 12:00:00
2019-10-09 15:00:00
2019-10-09 18:00:00
2019-10-09 21:00:00
2019-10-10 00:00:00
2019-10-10 03:00:00
2019-10-10 06:00:00
2019-10-10 09:00:00
2019-10-10 12:00:00
2019-10-10 15:00:00
2019-10-10 18:00:00
2019-10-10 21:00:00
2019-10-11 00:00:00
2019-10-11 03:00:00
2019-10-11 06:00:00
2019-10-11 09:00:00
2019-10-11 12:00:00

Let's fix the time to be in the local time zone of the forecast and not UTC

First call the weather function for your location to get the offset in seconds then use that offset on the OpenWeatherMap provided UTC time.

https://www.epochconverter.com/timezones

In [9]:
location = 'seattle'
  
weather_json = json.load(
    urlopen("http://api.openweathermap.org/data/2.5/weather?q=" + location + "&appid=" + YOUR_OPENWEATHERMAP_API_KEY))
weather_json
Out[9]:
{'coord': {'lon': -122.33, 'lat': 47.6},
 'weather': [{'id': 800,
   'main': 'Clear',
   'description': 'clear sky',
   'icon': '01n'}],
 'base': 'stations',
 'main': {'temp': 279.66,
  'pressure': 1029,
  'humidity': 87,
  'temp_min': 277.04,
  'temp_max': 282.04},
 'visibility': 16093,
 'wind': {'speed': 1.22, 'deg': 350.817},
 'clouds': {'all': 1},
 'dt': 1570366461,
 'sys': {'type': 1,
  'id': 3417,
  'message': 0.0128,
  'country': 'US',
  'sunrise': 1570371297,
  'sunset': 1570412382},
 'timezone': -25200,
 'id': 5809844,
 'name': 'Seattle',
 'cod': 200}
In [10]:
tz_offset = float(weather_json['timezone'])
print(tz_offset)
-25200.0
In [11]:
import datetime

date_str = '2019-10-01 15:00:00' 
datetime_object = datetime.datetime.strptime (date_str, '%Y-%m-%d %H:%M:%S')
datetime_object + datetime.timedelta(seconds=tz_offset)
Out[11]:
datetime.datetime(2019, 10, 1, 8, 0)
In [13]:
from IPython.core.display import Image, display
import time

for w in weather_forecast_json['list']:
    # slow it down, be courteous 
    time.sleep(0.1)
    date_str = w['dt_txt']
    print('UTC:', date_str)
    datetime_object = datetime.datetime.strptime (date_str, '%Y-%m-%d %H:%M:%S')
    datetime_object = datetime_object + datetime.timedelta(seconds=tz_offset)
    print('Local time in', location.capitalize(), str(datetime_object))
    

    outlook_icon = w['weather'][0]['icon']  + ".png"
  
    display(Image("http://openweathermap.org/img/w/" + outlook_icon, unconfined=True))
    break;
    
UTC: 2019-10-06 15:00:00
Local time in Seattle 2019-10-06 08:00:00

Let's Build Our Flask Application!

Inputing a location. OK, this is a bit of a hack but will simplify our implemenation tremendously. We will use the URl get input method. This means you will need to call your weather forecasting app the following way:

http://<<YOUR_PYTHONANYWHERE_ACCOUNT_NAME>>.pythonanywhere.com/?l=London

A word on encoding and decoding text when passing them through URL address links:

In [73]:
# Making text Internet/URL friendly
import urllib.parse
rez = urllib.parse.quote_plus('New York')
rez
Out[73]:
'New+York'
In [75]:
str(urllib.parse.unquote_plus(rez)).title()
Out[75]:
'New York'
In [ ]:
def forecaster():
    message = "Sorry, I cannot find that location..."

    if request.method == 'GET':
        selected_location = request.args.get('l')

        # deal with spaces and special characters
        selected_location = urllib.parse.quote_plus(selected_location)

        openweathermap_url = "https://api.openweathermap.org/data/2.5/weather?q=" + selected_location + "&mode=json&APPID=" + YOUR_OPENWEATHERMAP_API_KEY
        # call the openweathermap api
        weather_json = []
        try:
            weather_json = requests.get(openweathermap_url).json()
        except:
            # couldn't find location
            e = sys.exc_info()[0]

        if (len(weather_json) > 3):
            message = ''
            tz_offset = float(weather_json['timezone'])

            openweathermap_url = "http://api.openweathermap.org/data/2.5/forecast?q=" + selected_location + "&mode=json&APPID=" + YOUR_OPENWEATHERMAP_API_KEY
            weather_forecast_json = json.load(urlopen(openweathermap_url))

            message = '<HTML><BODY><H1>5 Day / 3 Hour Forecast For ' + str(urllib.parse.unquote_plus(selected_location)).title() + '</H1><BR><BR>'
            for w in weather_forecast_json['list']:
                # slow it down, be courteous
                time.sleep(0.1)
                # get local time
                date_str = w['dt_txt']
                datetime_object = datetime.datetime.strptime (date_str, '%Y-%m-%d %H:%M:%S')
                datetime_object = datetime_object + datetime.timedelta(seconds=tz_offset)
                message += str(datetime_object) + '<BR>'

                outlook_icon = w['weather'][0]['icon']  + ".png"
                message += "<img src='http://openweathermap.org/img/w/" + outlook_icon + "' /><BR><BR>"

            message += '</BODY></HTML>'

Get A PythonAnywhere Account - It's Free

Sign up for a free account on PythonAnywhere.com - no credit cards required, only a valid email address.

Setting Up Flask Web Framework

Next, let's setup a Flask web-serving platform. It is super easy to do. Under the 'Web' tab, click the 'Add a new web app' blue button. And accept the defaults until you get to the 'Select a Python Web framework' and click on 'Flask' and then the latest Python framework.

Full flask implementation:

In [ ]:
from flask import Flask, Markup, request
import datetime, time, json, sys, requests
from urllib.request import urlopen
import urllib.parse


YOUR_OPENWEATHERMAP_API_KEY = '4d87794afdcfe073ca9d7fc4c5da86e3'


app = Flask(__name__)

@app.route("/", methods=['GET'])
def forecaster():
    message = "Sorry, I cannot find that location..."

    if request.method == 'GET':
        selected_location = request.args.get('l')

        if selected_location is not None:

            # deal with spaces and special characters
            selected_location = urllib.parse.quote_plus(selected_location)

            openweathermap_url = "https://api.openweathermap.org/data/2.5/weather?q=" + selected_location + "&mode=json&APPID=" + YOUR_OPENWEATHERMAP_API_KEY
            # call the openweathermap api
            weather_json = []
            try:
                weather_json = requests.get(openweathermap_url).json()
            except:
                # couldn't find location
                e = sys.exc_info()[0]

            if (len(weather_json) > 3):
                message = ''
                tz_offset = float(weather_json['timezone'])

                openweathermap_url = "http://api.openweathermap.org/data/2.5/forecast?q=" + selected_location + "&mode=json&APPID=" + YOUR_OPENWEATHERMAP_API_KEY
                weather_forecast_json = json.load(urlopen(openweathermap_url))

                message = '<HTML><BODY><H1>5 Day / 3 Hour Forecast For ' + str(urllib.parse.unquote_plus(selected_location)).title() + '</H1><BR><BR>'
                for w in weather_forecast_json['list']:
                    # slow it down, be courteous
                    time.sleep(0.1)
                    # get local time
                    date_str = w['dt_txt']
                    datetime_object = datetime.datetime.strptime (date_str, '%Y-%m-%d %H:%M:%S')
                    datetime_object = datetime_object + datetime.timedelta(seconds=tz_offset)
                    message += str(datetime_object) + '<BR>'

                    outlook_icon = w['weather'][0]['icon']  + ".png"
                    message += "<img src='http://openweathermap.org/img/w/" + outlook_icon + "' /><BR><BR>"

                message += '</BODY></HTML>'
                # tell Flask to interpret HTML tags as actual HTML
                message = Markup(message)

    return message

Show Notes

(pardon typos and formatting -
these are the notes I use to make the videos)

Let's build a web app to forecast weather anywhere around the world. Our app will take a city name and you will return the 5-day forecast in icons! If you follow the video and use PythonAnywere, you will be able to use it on your mobile phone.