I'm a big fan of Andy Weier's The Martian. Reading it, I was wondering what Mark Watney felt while walking on the red planet. Recently I came across a post on
Twillo , in which it was mentioned that NASA has a public API for accessing photos from rovers. So I decided to write my own application for viewing images directly in the browser.
Creating aiohttp application
Let's start with a simple one - install and run aiohttp. First, create a virtual environment. I suggest using Python 3.5, in which the new syntax async def and await appeared. If you want to develop this application in the future to better understand asynchronous programming, you can install Python 3.6 right away. Finally, install aiohttp:
pip install aiohttp
Add a file to the project (let's call it nasa.py) with the code:
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')
If you have not worked with aiohttp, then I will explain a few points:
')
- Korutina get_mars_photo - request handler; accepts an HTTP request as an argument and prepares content for an HTTP response (well, or throws an exception)
- app - high-level server; it supports routing, middleware and signals (in the example only routing will be shown)
- app.router.add_get - registers an HTTP handler for the GET method on the '/' path
Note: a request handler can also be a regular function, and not just a quorutine. But in order to understand the full power of asyncio, most functions will be defined as async def.
Application launch
To start the application, add a line to the end of the file:
web.run_app(app, host='127.0.0.1', port=8080)
And run it as usual python script:
python nasa.py
However, there is a better way. Among the many third-party libraries I found
aiohttp-devtools . It provides a great runserver command that launches your application, and also supports live reloading.
pip install aiohttp-devtools adev runserver -p 8080 nasa.py
Now, at localhost: 8080, you should see the text “A photo of Mars”.
Using the NASA API
Let's go directly to getting photos. For this we will use
the NASA API . Each rover has its own URL (for Curiosity, it's
api.nasa.gov/mars-photos/api/v1/rovers/curiosity/photos [1]). We must also pass at least 2 parameters on each call:
- sol - the Martian day on which the photo was taken, starting with the landing of the rover (you can find the maximum value in the section rover / max_sol)
- API KEY - a key provided by NASA (for now you can use the test DEMO_KEY)
In response, we will receive a list of photos from the URL, information about the camera and the rover. A little tweak the nasa.py file:
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) 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 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)
This is what happens here:
- we choose random sol (for Curiosity max_sol at the time of this writing was 1722)
- ClientSession creates a new session that we use to get a response from the NASA API
- we parse JSON with resp.json ()
- check the presence of the key 'photos' in the response; if it's not there, then we have reached the limit of calls, you need to wait a bit
- if there are no pictures in the current day, we request another random day
- use HTTPFound to redirect to the found photo
Getting a key for NASA API
The DEMO_KEY public key, proposed by NASA by default, works fine, but very soon you will be confronted with a call limit [2]. I recommend getting your own key here, which will be available after registration.
After launching the application, you can be redirected to the following image from Mars:

This is not what I expected ...
Image validation
The image above is not very inspiring. It turns out, rovers take a bunch of very boring photos. I want to see the same as Mark Watney in his incredible adventure. I'll try to fix it.
We need some sort of validation mechanism for the resulting images. Adapt the code:
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')
Here is what has changed:
- we get the raw stream of bytes by url of the picture using resp.read ()
- check whether the image looks good enough
- if everything is in order, then we put the bytes in the web.Response. Please note - they are passed to the body instead of text, and also set to content_type
In addition, we removed the redirection (HTTPFound), so that we can get the following random image by simply reloading the page.
It remains to describe the validation of the photo. The simplest is to determine the minimum size.
Install Pillow:
pip install pillow
Our validation function will turn into:
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 >= 1024

Already something! Go ahead and drop all black and white 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'
Now our program starts to produce more interesting photos:

And even sometimes selfies:

Conclusion
Source code of the program There are many things to improve (for example, getting the max_sol value through the API, viewing images from several rovers, caching the URL), but while the program is doing its job: we can get a random photo from Mars and present ourselves at the site of future settlers.
Hope you enjoyed this short tutorial. If you notice a mistake or have any questions, let me know.
Translator's Notes:
[1] In fact, there are only 3 rovers, you can see a list of them with the request
api.nasa.gov/mars-photos/api/v1/rovers/?API_KEY=DEMO_KEY .
[2] I would also add that the program works for a very long time due to the random image search.