Of course! Using urllib.request.urlopen to get JSON data is a fundamental task in Python. Here’s a complete guide, from the basic steps to best practices and error handling.

The Short & Sweet Answer
Here is the most common and straightforward way to do it.
import urllib.request
import json
# The URL of the public JSON API we want to query
url = "https://jsonplaceholder.typicode.com/todos/1"
try:
# 1. Open the URL and read the response
with urllib.request.urlopen(url) as response:
# 2. Read the content and decode it from bytes to a string
data_string = response.read().decode('utf-8')
# 3. Parse the JSON string into a Python dictionary
data_dict = json.loads(data_string)
# 4. Now you can work with the data as a Python dictionary
print(f"User ID: {data_dict['userId']}")
print(f"Title: {data_dict['title']}")
print(f"Completed: {data_dict['completed']}")
except urllib.error.URLError as e:
print(f"Error opening URL: {e.reason}")
except json.JSONDecodeError:
print("Error: Could not decode JSON from the response.")
Step-by-Step Breakdown
Let's break down the code above to understand what each part does.
Step 1: Import Necessary Modules
import urllib.request import json
urllib.request: This is Python's built-in library for opening URLs (making web requests).json: This is Python's built-in library for working with JSON data.
Step 2: Make the HTTP Request and Read the Response
with urllib.request.urlopen(url) as response:
data_string = response.read().decode('utf-8')
urllib.request.urlopen(url): This function opens the given URL. It returns a file-like object, which we callresponse.with ... as response:: This is the recommended way to handle resources like network connections. It automatically closes the connection when the block is exited, even if an error occurs.response.read(): This reads the entire content of the response. The result is a sequence of bytes (e.g.,b'{"userId": 1, ...}')..decode('utf-8'): We need to convert these bytes into a regular string so that thejsonlibrary can understand it. UTF-8 is the standard encoding for web content.
Step 3: Parse the JSON String into a Python Object
data_dict = json.loads(data_string)
json.loads()(which stands for "load string") is the core function for this step. It takes a JSON formatted string and converts it into the equivalent Python object.- JSON object → Python dictionary
- JSON array
[...]→ Python list[] - JSON string → Python string
- JSON number
123or3→ Python numberintorfloat - JSON
true/false→ PythonTrue/False - JSON
null→ PythonNone
Now, data_dict is a Python dictionary, and you can access its values using keys, like data_dict['userId'].
Complete, Robust Example (with Error Handling)
In a real-world application, you should always handle potential errors. Things can go wrong: the URL might be invalid, the server might be down, the network might fail, or the response might not be valid JSON.

This example includes robust error handling.
import urllib.request
import json
import urllib.error
# A URL that is known to be reliable for testing
url = "https://jsonplaceholder.typicode.com/todos/1"
# A URL that will produce a 404 error (Not Found)
# url = "https://jsonplaceholder.typicode.com/todos/999999"
try:
print(f"Attempting to fetch data from: {url}")
# Make the request
with urllib.request.urlopen(url) as response:
# Check if the response status code is 200 (OK)
if response.status == 200:
# Read and decode the response
data_string = response.read().decode('utf-8')
# Parse the JSON string
data_dict = json.loads(data_string)
# --- Process the data ---
print("\nSuccessfully fetched and parsed JSON data:")
print("-" * 30)
# Pretty-print the dictionary
print(json.dumps(data_dict, indent=4))
print("-" * 30)
# Access a specific value
print(f"\nTitle of the todo: {data_dict['title']}")
else:
print(f"Request failed with status code: {response.status}")
except urllib.error.HTTPError as e:
# This handles HTTP errors like 404 (Not Found) or 500 (Server Error)
print(f"HTTP Error: {e.code} - {e.reason}")
except urllib.error.URLError as e:
# This handles other URL-related errors, like network down or invalid domain
print(f"URL Error: {e.reason}")
except json.JSONDecodeError:
# This handles the case where the response is not valid JSON
print("Error: Failed to decode JSON from the response body.")
except Exception as e:
# A catch-all for any other unexpected errors
print(f"An unexpected error occurred: {e}")
The Modern Alternative: requests Library
While urllib is built-in, the requests library is extremely popular because it simplifies the process significantly. It's not part of the standard library, so you need to install it first.
Installation:
pip install requests
Code with requests:
import requests
import json
url = "https://jsonplaceholder.typicode.com/todos/1"
try:
# The .json() method does the decoding and parsing in one step!
response = requests.get(url)
response.raise_for_status() # Raises an HTTPError for bad responses (4xx or 5xx)
data_dict = response.json() # This is the magic line!
print("Successfully fetched data using the 'requests' library:")
print(f"User ID: {data_dict['userId']}")
print(f"Title: {data_dict['title']}")
except requests.exceptions.HTTPError as errh:
print(f"Http Error: {errh}")
except requests.exceptions.ConnectionError as errc:
print(f"Error Connecting: {errc}")
except requests.exceptions.Timeout as errt:
print(f"Timeout Error: {errt}")
except requests.exceptions.RequestException as err:
print(f"An error occurred: {err}")
As you can see, requests is more concise and readable, especially with the response.json() method that handles decoding and parsing automatically. For any serious project, requests is the recommended choice. However, understanding urllib is valuable because it's always available.
