Download and deserialize JSON

openweathermap.py

For the meanings of the fields of the URL and the fields of the output, see Call current weather data for one location.

The data downloaded from the URL is a sequence of bytes, not a string of characters. That’s why we have to feed the data to decode. The current weather is one long line line of output.

{"coord":{"lon":-74.02,"lat":40.69},"weather":[{"id":521,"main":"Rain","description":"shower rain","icon":"09n"},{"id":701,"main":"Mist","description":"mist","icon":"50n"},{"id":711,"main":"Smoke","description":"smoke","icon":"50n"}],"base":"stations","main":{"temp":77.95,"pressure":1009,"humidity":88,"temp_min":73.99,"temp_max":84.2},"visibility":6437,"wind":{"speed":4.7,"deg":320,"gust":23.04},"rain":{"1h":4.91},"clouds":{"all":90},"dt":1570056296,"sys":{"type":1,"id":4026,"message":0.0127,"country":"US","sunrise":1570013592,"sunset":1570055855},"timezone":-14400,"id":5128581,"name":"New York","cod":200}

{'coord': {'lon': -74.02, 'lat': 40.69}, 'weather': [{'id': 521, 'main': 'Rain', 'description': 'shower rain', 'icon': '09n'}, {'id': 701, 'main': 'Mist', 'description': 'mist', 'icon': '50n'}, {'id': 711, 'main': 'Smoke', 'description': 'smoke', 'icon': '50n'}], 'base': 'stations', 'main': {'temp': 77.95, 'pressure': 1009, 'humidity': 88, 'temp_min': 73.99, 'temp_max': 84.2}, 'visibility': 6437, 'wind': {'speed': 4.7, 'deg': 320, 'gust': 23.04}, 'rain': {'1h': 4.91}, 'clouds': {'all': 90}, 'dt': 1570056296, 'sys': {'type': 1, 'id': 4026, 'message': 0.0127, 'country': 'US', 'sunrise': 1570013592, 'sunset': 1570055855}, 'timezone': -14400, 'id': 5128581, 'name': 'New York', 'cod': 200}

{
    "base": "stations",
    "clouds": {
        "all": 90
    },
    "cod": 200,
    "coord": {
        "lat": 40.69,
        "lon": -74.02
    },
    "dt": 1570056296,
    "id": 5128581,
    "main": {
        "humidity": 88,
        "pressure": 1009,
        "temp": 77.95,
        "temp_max": 84.2,
        "temp_min": 73.99
    },
    "name": "New York",
    "rain": {
        "1h": 4.91
    },
    "sys": {
        "country": "US",
        "id": 4026,
        "message": 0.0127,
        "sunrise": 1570013592,
        "sunset": 1570055855,
        "type": 1
    },
    "timezone": -14400,
    "visibility": 6437,
    "weather": [
        {
            "description": "shower rain",
            "icon": "09n",
            "id": 521,
            "main": "Rain"
        },
        {
            "description": "mist",
            "icon": "50n",
            "id": 701,
            "main": "Mist"
        },
        {
            "description": "smoke",
            "icon": "50n",
            "id": 711,
            "main": "Smoke"
        }
    ],
    "wind": {
        "deg": 320,
        "gust": 23.04,
        "speed": 4.7
    }
}

temperature = 77.95° Fahrenheit
temperature = 77.95° Fahrenheit
description = shower rain

Things to try

  1. Instead of lines 16–20, assemble the URL like this:
    import urllib.parse
    
    query = {                              #query is a dictionary
        "q":     "10004,US",
        "units": "imperial",
        "mode":  "json",
        "APPID": "532d313d6a9ec4ea93eb89696983e369"
    }
    
    params = urllib.parse.urlencode(query) #params is a string
    url = f"http://api.openweathermap.org/data/2.5/weather?{params}"
    
  2. The dates and times are in POSIX format. Print them in a format that make sense to human beings.
    import datetime
    
    try:
        dt = bigDictionary["dt"]                           #dt is an int
    except KeyError:
        print("bigDictionary has no key named dt", file = sys.stderr)
        sys.exit(1)
    
    print(f"dt = {dt:,} seconds after midnight on January 1, 1970")
    
    localDateAndTime = datetime.datetime.fromtimestamp(dt) #localDateAndTime is a datetime.datetime
    print(f"localDateAndTime = {localDateAndTime}")
    print(localDateAndTime.strftime("%c"))
    print(localDateAndTime.strftime("%A, %B %-d, %Y %-I:%M:%S %p"))
    
    dt = 1,570,056,296 seconds after midnight on January 1, 1970
    localDateAndTime = 2019-10-02 18:44:56
    Wed Oct  2 18:44:56 2019
    Wednesday, October 2, 2019 6:44:56 PM
    

    Print the sunrise and sunset times too.

    Ask the macOS and Linux “binary calculator” for answers with up to five digits to the right of the decimal point.

    bc -l
    scale = 5
    60 * 60 * 24 * 365.25 * (2019 - 1970)
    1546322400.00
    control-d
    
  3. Display the current weather icon. See Binary.
    import tkinter
    
    weather = bigDictionary["weather"] #weather is a list.
    d = weather[0]                     #d is a dictionary.
    icon = d["icon"]                   #icon is a string
    url = f"http://openweathermap.org/img/wn/{icon}@2x.png" #also try it without the @2x
    
    infile = urllib.request.urlopen(url) #error checking omitted for brevity
    sequenceOfBytes = infile.read()
    infile.close()
    
    root = tkinter.Tk()
    
    try:
        myImage = tkinter.PhotoImage(data = sequenceOfBytes)
    except tkinter.TclError as error:
        print(error, file = sys.stderr)
        sys.exit(1)
    
    label = tkinter.Label(root, image = myImage)
    label.pack()
    root.mainloop()
    
  4. Here’s the whole program with a tkinter interface.
    """
    weather.py
    
    Display the current weather (including icon) in different cities.
    """
    
    import sys
    import urllib.parse
    import urllib.request
    import json
    import tkinter
    
    zipcodes = {
        "Atlanta":       "30301",
        "Boston":        "02109",
        "Chicago":       "60007",
        "Denver":        "80202",
        "Houston":       "77001",
        "Los Angeles":   "90001",
        "Miami":         "33101",
        "New York":      "10004",
        "San Francisco": "94102",
        "Seattle":       "98101"
    }
    
    defaultCity = "New York"
    citynames = zipcodes.keys()
    maxLen = len(max(citynames, key = len))   #length of longest name
    
    #data is a tuple containing 5 smaller tuples.
    #The second half of the last 4 of them is a function.
    
    data = (
        ("Select your city", None),
        ("Temperature",      lambda d: f'{int(d["main"]["temp"])}° F'),
        ("Wind speed",       lambda d: f'{int(d["wind"]["speed"])} mph'),
        ("Humidity",         lambda d: f'{d["main"]["humidity"]}%'),
        ("Skies",            lambda d: f'{d["weather"][0]["description"]}')
    )
    
    backgroundColor = {      #of the tkinter.Label that displays the icon
        "d": "sky blue",     #day
        "n": "midnight blue" #night
    }
    
    #This function is called when the program is launched,
    #and when the user selects a city.
    
    def command(cityname):
        query = {                               #query is a dictionary
            "q":     f"{zipcodes[cityname]},US",
            "units": "imperial",
            "mode":  "json",
            "APPID": "532d313d6a9ec4ea93eb89696983e369"
        }
    
        params = urllib.parse.urlencode(query)  #params is a string
        url = f"http://api.openweathermap.org/data/2.5/weather?{params}"
    
        try:
            infile = urllib.request.urlopen(url)
        except urllib.error.URLError as error:
            print(error, url, file = sys.stderr)
            sys.exit(1)
    
        sequenceOfBytes = infile.read()         #Read the entire input file.
        infile.close()
    
        try:
            s = sequenceOfBytes.decode("utf-8") #s is a string.
        except UnicodeError as error:
            print(error, file = sys.stderr)
            sys.exit(1)
    
        try:
            bigDict = json.loads(s)
        except json.JSONDecodeError as error:
            print(error, file = sys.stderr)
            sys.exit(1)
    
        assert isinstance(bigDict, dict)
    
        for label, datum in zip(labels, data[1:]):
            label["text"] = datum[1](bigDict)
    
        weather = bigDict["weather"][0] #bigDict["weather"] is a list.
        icon = weather["icon"]          #first characters of name of icon file
        url = f'http://openweathermap.org/img/wn/{icon}@2x.png'
    
        try:
            infile = urllib.request.urlopen(url)
        except urllib.error.URLError as error:
            print(error, file = sys.stderr)
            sys.exit(1)
    
        sequenceOfBytes = infile.read()
        infile.close()
    
        try:
            image = tkinter.PhotoImage(data = sequenceOfBytes)
        except tkinter.TclError as error:
            print(error, file = sys.stderr)
            sys.exit(1)
    
        try:
            bg = backgroundColor[icon[-1]]
        except KeyError:
            iconLabel.configure(image = image)
        else:
            iconLabel.configure(image = image, bg = bg)
    
        iconLabel.image = image   #Strange that you need this too.
    
    #Create the tkinter interface when the program is launched.
    
    root = tkinter.Tk()
    root.title("Current weather")
    padx = 5
    
    #dropdown menu
    dropVariable = tkinter.StringVar(root)
    dropVariable.set(defaultCity)
    menu = tkinter.OptionMenu(root, dropVariable, *citynames, command = command)
    menu.config(width = maxLen)
    menu.grid(row = 0, column = 1, sticky = "ew")
    
    #Create the labels that are captions.
    
    for row, datum in enumerate(data):
        label = tkinter.Label(text = f"{datum[0]}:", anchor = "e", padx = padx)
        label.grid(row = row, column = 0, sticky = "ew")
    
    #Create the labels that display information.
    labels = []
    
    for row in range(1, len(data)):
        label = tkinter.Label(anchor = "w", padx = padx, fg = "blue")
        label.grid(row = row, column = 1, sticky = "ew")
        labels.append(label)
    
    iconLabel = tkinter.Label(root)
    iconLabel.grid(row = len(data), column = 0, columnspan = 2, sticky = "ew")
    
    command(defaultCity)   #Begin by displaying the data for the default city.
    tkinter.mainloop()
    

    Daytime in New York, nighttime in Seattle:

  5. Point your browser at
    https://maps.googleapis.com/maps/api/geocode/json?address=11+West+42nd+Street,+New+York,+NY+10036
    See Get Started.
    {
       "results" : [
          {
             "address_components" : [
                {
                   "long_name" : "11",
                   "short_name" : "11",
                   "types" : [ "street_number" ]
                },
                {
                   "long_name" : "West 42nd Street",
                   "short_name" : "W 42nd St",
                   "types" : [ "route" ]
                },
                {
                   "long_name" : "Manhattan",
                   "short_name" : "Manhattan",
                   "types" : [ "political", "sublocality", "sublocality_level_1" ]
                },
                {
                   "long_name" : "New York",
                   "short_name" : "New York",
                   "types" : [ "locality", "political" ]
                },
                {
                   "long_name" : "New York County",
                   "short_name" : "New York County",
                   "types" : [ "administrative_area_level_2", "political" ]
                },
                {
                   "long_name" : "New York",
                   "short_name" : "NY",
                   "types" : [ "administrative_area_level_1", "political" ]
                },
                {
                   "long_name" : "United States",
                   "short_name" : "US",
                   "types" : [ "country", "political" ]
                },
                {
                   "long_name" : "10036",
                   "short_name" : "10036",
                   "types" : [ "postal_code" ]
                }
             ],
             "formatted_address" : "11 W 42nd St, New York, NY 10036, USA",
             "geometry" : {
                "location" : {
                   "lat" : 40.7541476,
                   "lng" : -73.9818586
                },
                "location_type" : "ROOFTOP",
                "viewport" : {
                   "northeast" : {
                      "lat" : 40.75549658029149,
                      "lng" : -73.98050961970849
                   },
                   "southwest" : {
                      "lat" : 40.75279861970849,
                      "lng" : -73.98320758029151
                   }
                }
             },
             "place_id" : "ChIJuzzySglZwokRuuz-0i_egSU",
             "types" : [ "street_address" ]
          }
       ],
       "status" : "OK"
    }
    
    import sys
    import urllib.request
    import json
    
    url = "https://maps.googleapis.com/maps/api/geocode/json" \
        "?address=11+West+42nd+Street,+New+York,+NY+10036"
    
    infile = urllib.request.urlopen(url) #error checking omitted for brevity
    sequenceOfBytes = infile.read()
    infile.close()
    s = sequenceOfBytes.decode("utf-8")  #error checking omitted for brevity
    bigDictionary = json.loads(s)        #error checking omitted for brevity
    
    print(f'status = {bigDictionary["status"]}')
    if bigDictionary["status"] != "OK":
        sys.exit(1)
    
    results = bigDictionary["results"] #results is a list containing 1 item
    result = results[0]                #result is a dictionary
    
    formatted_address = result["formatted_address"] #formatted_address is a string
    print(f"formatted_address = {formatted_address}")
    
    geometry = result["geometry"]   #geometry is a dictionary containing 3 items
    location = geometry["location"] #location is a dictionary containing 2 items
    print(f'latitude = {location["lat"]}')
    print(f'longitude = {location["lng"]}')
    
    sys.exit(0)
    

    Positive latitude is north of the equator. Negative longitude is west of Greenwich.

    status = OK
    formatted_address = 11 W 42nd St, New York, NY 10036, USA
    latitude = 40.7541476
    longitude = -73.9818586
    
    Add a tkinter GUI to this Python script.
  6. githubusers.py
    {
        "incomplete_results": false,
        "items": [
            {
                "avatar_url": "https://avatars3.githubusercontent.com/u/54085829?v=4",
                "events_url": "https://api.github.com/users/sf19pb1-k1chan/events{/privacy}",
                "followers_url": "https://api.github.com/users/sf19pb1-k1chan/followers",
                "following_url": "https://api.github.com/users/sf19pb1-k1chan/following{/other_user}",
                "gists_url": "https://api.github.com/users/sf19pb1-k1chan/gists{/gist_id}",
                "gravatar_id": "",
                "html_url": "https://github.com/sf19pb1-k1chan",
                "id": 54085829,
                "login": "sf19pb1-k1chan",
                "node_id": "MDEyOk9yZ2FuaXphdGlvbjU0MDg1ODI5",
                "organizations_url": "https://api.github.com/users/sf19pb1-k1chan/orgs",
                "received_events_url": "https://api.github.com/users/sf19pb1-k1chan/received_events",
                "repos_url": "https://api.github.com/users/sf19pb1-k1chan/repos",
                "score": 41.05616,
                "site_admin": false,
                "starred_url": "https://api.github.com/users/sf19pb1-k1chan/starred{/owner}{/repo}",
                "subscriptions_url": "https://api.github.com/users/sf19pb1-k1chan/subscriptions",
                "type": "Organization",
                "url": "https://api.github.com/users/sf19pb1-k1chan"
            },
            {
                "avatar_url": "https://avatars2.githubusercontent.com/u/54280012?v=4",
                "events_url": "https://api.github.com/users/sf19pb1-hardeep-leyl/events{/privacy}",
                "followers_url": "https://api.github.com/users/sf19pb1-hardeep-leyl/followers",
                "following_url": "https://api.github.com/users/sf19pb1-hardeep-leyl/following{/other_user}",
                "gists_url": "https://api.github.com/users/sf19pb1-hardeep-leyl/gists{/gist_id}",
                "gravatar_id": "",
                "html_url": "https://github.com/sf19pb1-hardeep-leyl",
                "id": 54280012,
                "login": "sf19pb1-hardeep-leyl",
                "node_id": "MDEyOk9yZ2FuaXphdGlvbjU0MjgwMDEy",
                "organizations_url": "https://api.github.com/users/sf19pb1-hardeep-leyl/orgs",
                "received_events_url": "https://api.github.com/users/sf19pb1-hardeep-leyl/received_events",
                "repos_url": "https://api.github.com/users/sf19pb1-hardeep-leyl/repos",
                "score": 37.516575,
                "site_admin": false,
                "starred_url": "https://api.github.com/users/sf19pb1-hardeep-leyl/starred{/owner}{/repo}",
                "subscriptions_url": "https://api.github.com/users/sf19pb1-hardeep-leyl/subscriptions",
                "type": "Organization",
                "url": "https://api.github.com/users/sf19pb1-hardeep-leyl"
            },
            {
                "avatar_url": "https://avatars0.githubusercontent.com/u/54124365?v=4",
                "events_url": "https://api.github.com/users/sf19pb1-petercooper/events{/privacy}",
                "followers_url": "https://api.github.com/users/sf19pb1-petercooper/followers",
                "following_url": "https://api.github.com/users/sf19pb1-petercooper/following{/other_user}",
                "gists_url": "https://api.github.com/users/sf19pb1-petercooper/gists{/gist_id}",
                "gravatar_id": "",
                "html_url": "https://github.com/sf19pb1-petercooper",
                "id": 54124365,
                "login": "sf19pb1-petercooper",
                "node_id": "MDEyOk9yZ2FuaXphdGlvbjU0MTI0MzY1",
                "organizations_url": "https://api.github.com/users/sf19pb1-petercooper/orgs",
                "received_events_url": "https://api.github.com/users/sf19pb1-petercooper/received_events",
                "repos_url": "https://api.github.com/users/sf19pb1-petercooper/repos",
                "score": 33.525013,
                "site_admin": false,
                "starred_url": "https://api.github.com/users/sf19pb1-petercooper/starred{/owner}{/repo}",
                "subscriptions_url": "https://api.github.com/users/sf19pb1-petercooper/subscriptions",
                "type": "Organization",
                "url": "https://api.github.com/users/sf19pb1-petercooper"
            },
            {
                "avatar_url": "https://avatars3.githubusercontent.com/u/54157444?v=4",
                "events_url": "https://api.github.com/users/sf19pb1-dannyphu/events{/privacy}",
                "followers_url": "https://api.github.com/users/sf19pb1-dannyphu/followers",
                "following_url": "https://api.github.com/users/sf19pb1-dannyphu/following{/other_user}",
                "gists_url": "https://api.github.com/users/sf19pb1-dannyphu/gists{/gist_id}",
                "gravatar_id": "",
                "html_url": "https://github.com/sf19pb1-dannyphu",
                "id": 54157444,
                "login": "sf19pb1-dannyphu",
                "node_id": "MDEyOk9yZ2FuaXphdGlvbjU0MTU3NDQ0",
                "organizations_url": "https://api.github.com/users/sf19pb1-dannyphu/orgs",
                "received_events_url": "https://api.github.com/users/sf19pb1-dannyphu/received_events",
                "repos_url": "https://api.github.com/users/sf19pb1-dannyphu/repos",
                "score": 32.648464,
                "site_admin": false,
                "starred_url": "https://api.github.com/users/sf19pb1-dannyphu/starred{/owner}{/repo}",
                "subscriptions_url": "https://api.github.com/users/sf19pb1-dannyphu/subscriptions",
                "type": "Organization",
                "url": "https://api.github.com/users/sf19pb1-dannyphu"
            },
            {
                "avatar_url": "https://avatars3.githubusercontent.com/u/54147861?v=4",
                "events_url": "https://api.github.com/users/SF19PB1-MarkMeretzky/events{/privacy}",
                "followers_url": "https://api.github.com/users/SF19PB1-MarkMeretzky/followers",
                "following_url": "https://api.github.com/users/SF19PB1-MarkMeretzky/following{/other_user}",
                "gists_url": "https://api.github.com/users/SF19PB1-MarkMeretzky/gists{/gist_id}",
                "gravatar_id": "",
                "html_url": "https://github.com/SF19PB1-MarkMeretzky",
                "id": 54147861,
                "login": "SF19PB1-MarkMeretzky",
                "node_id": "MDEyOk9yZ2FuaXphdGlvbjU0MTQ3ODYx",
                "organizations_url": "https://api.github.com/users/SF19PB1-MarkMeretzky/orgs",
                "received_events_url": "https://api.github.com/users/SF19PB1-MarkMeretzky/received_events",
                "repos_url": "https://api.github.com/users/SF19PB1-MarkMeretzky/repos",
                "score": 24.493538,
                "site_admin": false,
                "starred_url": "https://api.github.com/users/SF19PB1-MarkMeretzky/starred{/owner}{/repo}",
                "subscriptions_url": "https://api.github.com/users/SF19PB1-MarkMeretzky/subscriptions",
                "type": "Organization",
                "url": "https://api.github.com/users/SF19PB1-MarkMeretzky"
            },
            {
                "avatar_url": "https://avatars2.githubusercontent.com/u/54218787?v=4",
                "events_url": "https://api.github.com/users/sf19pb1-ffranklin3/events{/privacy}",
                "followers_url": "https://api.github.com/users/sf19pb1-ffranklin3/followers",
                "following_url": "https://api.github.com/users/sf19pb1-ffranklin3/following{/other_user}",
                "gists_url": "https://api.github.com/users/sf19pb1-ffranklin3/gists{/gist_id}",
                "gravatar_id": "",
                "html_url": "https://github.com/sf19pb1-ffranklin3",
                "id": 54218787,
                "login": "sf19pb1-ffranklin3",
                "node_id": "MDEyOk9yZ2FuaXphdGlvbjU0MjE4Nzg3",
                "organizations_url": "https://api.github.com/users/sf19pb1-ffranklin3/orgs",
                "received_events_url": "https://api.github.com/users/sf19pb1-ffranklin3/received_events",
                "repos_url": "https://api.github.com/users/sf19pb1-ffranklin3/repos",
                "score": 24.107796,
                "site_admin": false,
                "starred_url": "https://api.github.com/users/sf19pb1-ffranklin3/starred{/owner}{/repo}",
                "subscriptions_url": "https://api.github.com/users/sf19pb1-ffranklin3/subscriptions",
                "type": "Organization",
                "url": "https://api.github.com/users/sf19pb1-ffranklin3"
            },
            {
                "avatar_url": "https://avatars1.githubusercontent.com/u/54335653?v=4",
                "events_url": "https://api.github.com/users/SF19PB1-ructr/events{/privacy}",
                "followers_url": "https://api.github.com/users/SF19PB1-ructr/followers",
                "following_url": "https://api.github.com/users/SF19PB1-ructr/following{/other_user}",
                "gists_url": "https://api.github.com/users/SF19PB1-ructr/gists{/gist_id}",
                "gravatar_id": "",
                "html_url": "https://github.com/SF19PB1-ructr",
                "id": 54335653,
                "login": "SF19PB1-ructr",
                "node_id": "MDEyOk9yZ2FuaXphdGlvbjU0MzM1NjUz",
                "organizations_url": "https://api.github.com/users/SF19PB1-ructr/orgs",
                "received_events_url": "https://api.github.com/users/SF19PB1-ructr/received_events",
                "repos_url": "https://api.github.com/users/SF19PB1-ructr/repos",
                "score": 18.845026,
                "site_admin": false,
                "starred_url": "https://api.github.com/users/SF19PB1-ructr/starred{/owner}{/repo}",
                "subscriptions_url": "https://api.github.com/users/SF19PB1-ructr/subscriptions",
                "type": "Organization",
                "url": "https://api.github.com/users/SF19PB1-ructr"
            },
            {
                "avatar_url": "https://avatars0.githubusercontent.com/u/54126431?v=4",
                "events_url": "https://api.github.com/users/sf19pb1-jhjhjhsu/events{/privacy}",
                "followers_url": "https://api.github.com/users/sf19pb1-jhjhjhsu/followers",
                "following_url": "https://api.github.com/users/sf19pb1-jhjhjhsu/following{/other_user}",
                "gists_url": "https://api.github.com/users/sf19pb1-jhjhjhsu/gists{/gist_id}",
                "gravatar_id": "",
                "html_url": "https://github.com/sf19pb1-jhjhjhsu",
                "id": 54126431,
                "login": "sf19pb1-jhjhjhsu",
                "node_id": "MDEyOk9yZ2FuaXphdGlvbjU0MTI2NDMx",
                "organizations_url": "https://api.github.com/users/sf19pb1-jhjhjhsu/orgs",
                "received_events_url": "https://api.github.com/users/sf19pb1-jhjhjhsu/received_events",
                "repos_url": "https://api.github.com/users/sf19pb1-jhjhjhsu/repos",
                "score": 17.24035,
                "site_admin": false,
                "starred_url": "https://api.github.com/users/sf19pb1-jhjhjhsu/starred{/owner}{/repo}",
                "subscriptions_url": "https://api.github.com/users/sf19pb1-jhjhjhsu/subscriptions",
                "type": "Organization",
                "url": "https://api.github.com/users/sf19pb1-jhjhjhsu"
            }
        ],
        "total_count": 8
    }
    
    bigDictionary["incomplete_results"] = False
    bigDictionary["total_count"] = 8
    type(bigDictionary["items"]) = <class 'list'>
    len(bigDictionary["items"]) = 8
    
    1 https://github.com/sf19pb1-dannyphu
    2 https://github.com/sf19pb1-ffranklin3
    3 https://github.com/sf19pb1-hardeep-leyl
    4 https://github.com/sf19pb1-jhjhjhsu
    5 https://github.com/sf19pb1-k1chan
    6 https://github.com/SF19PB1-MarkMeretzky
    7 https://github.com/sf19pb1-petercooper
    8 https://github.com/SF19PB1-ructr
    
  7. We have seen three websites that deliver output in JSON:
    http://api.openweathermap.org/data/2.5/weather
    https://maps.googleapis.com/maps/api/geocode/json
    https://api.github.com/search/users
    Find several more. Find the address that’s closest to a give latitude and longitude? Stock prices?

    When all else fails, there’s always http://www.jsontest.com/, e.g.

    1. http://date.jsontest.com/
      (UTC)
    2. http://ip.jsontest.com/
    3. http://echo.jsontest.com/hoy/today/manana/tomorrow/Sabado/Saturday
    4. http://headers.jsontest.com/
      (the HTTP headers)