Build a Mars Photo Viewer with aiohttp and NASA API – Step-by-Step Guide
This tutorial walks you through creating a simple aiohttp web application that fetches random Mars rover photos from the NASA API, validates image quality using Pillow, and serves the images directly, covering environment setup, async code, and deployment options.
Creating an aiohttp Application
Start by creating a new virtual environment (Python 3.5+ recommended) and install aiohttp: pip install aiohttp Create a file nasa.py with a basic handler that returns a text response:
from aiohttp import web
async def get_mars_photo(request):
return web.Response(text='A photo of Mars')
app = web.Application()
app.router.add_get('/', get_mars_photo, name='mars_photo')Run the app: web.run_app(app, host='127.0.0.1', port=8080) Alternatively, use aiohttp-devtools for automatic reload:
pip install aiohttp-devtools
adev runserver -p 8080 nasa.pyUsing the NASA API
Replace the placeholder response with a call to the NASA Mars Rover Photos API. Each rover has a URL (Curiosity example shown) and requires at least sol (Martian day) and an API_KEY (use DEMO_KEY for testing).
import random
from aiohttp import web, ClientSession
from aiohttp.web import HTTPFound
NASA_API_KEY = 'DEMO_KEY'
ROVER_URL = 'https://api.nasa.gov/mars-photos/api/v1/rovers/curiosity/photos'
async def get_mars_image_url_from_nasa():
while True:
sol = random.randint(0, 1722) # max_sol at time of writing
params = {'sol': sol, 'api_key': NASA_API_KEY}
async with ClientSession() as session:
async with session.get(ROVER_URL, params=params) as resp:
resp_dict = await resp.json()
if 'photos' not in resp_dict:
raise Exception('API limit reached or error')
photos = resp_dict['photos']
if not photos:
continue
return random.choice(photos)['img_src']
async def get_mars_photo(request):
url = await get_mars_image_url_from_nasa()
return HTTPFound(url)Running the Application
Execute the script with: python nasa.py Visiting http://localhost:8080 will display the text "A photo of Mars" initially.
Validating Images
To serve actual images, fetch the image bytes, validate size (and later color mode), and return the binary data.
pip install pillow import io
from PIL import Image
async def validate_image(image_bytes):
image = Image.open(io.BytesIO(image_bytes))
return image.width >= 1024 and image.height >= 1024Extended validation to reject grayscale images:
async def validate_image(image_bytes):
image = Image.open(io.BytesIO(image_bytes))
return (
image.width >= 1024 and
image.height >= 1024 and
image.mode != 'L'
)Fetch image bytes and apply validation:
async def get_mars_photo_bytes():
while True:
image_url = await get_mars_image_url_from_nasa()
async with ClientSession() as session:
async with session.get(image_url) as resp:
image_bytes = await resp.read()
if await validate_image(image_bytes):
break
return image_bytes
async def get_mars_photo(request):
image = await get_mars_photo_bytes()
return web.Response(body=image, content_type='image/jpeg')Complete Program
import random
import io
from aiohttp import web, ClientSession
from aiohttp.web import HTTPFound
from PIL import Image
NASA_API_KEY = 'DEMO_KEY'
ROVER_URL = 'https://api.nasa.gov/mars-photos/api/v1/rovers/curiosity/photos'
async def validate_image(image_bytes):
image = Image.open(io.BytesIO(image_bytes))
return image.width >= 1024 and image.height >= 1024 and image.mode != 'L'
async def get_mars_image_url_from_nasa():
while True:
sol = random.randint(0, 1722)
params = {'sol': sol, 'api_key': NASA_API_KEY}
async with ClientSession() as session:
async with session.get(ROVER_URL, params=params) as resp:
resp_dict = await resp.json()
if 'photos' not in resp_dict:
raise Exception('API error')
photos = resp_dict['photos']
if not photos:
continue
return random.choice(photos)['img_src']
async def get_mars_photo_bytes():
while True:
image_url = await get_mars_image_url_from_nasa()
async with ClientSession() as session:
async with session.get(image_url) as resp:
image_bytes = await resp.read()
if await validate_image(image_bytes):
break
return image_bytes
async def get_mars_photo(request):
image = await get_mars_photo_bytes()
return web.Response(body=image, content_type='image/jpeg')
app = web.Application()
app.router.add_get('/', get_mars_photo, name='mars_photo')Running this script will serve a random high‑resolution, non‑grayscale Mars photo each time the page is refreshed.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
MaGe Linux Operations
Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
