python 基於AioHttp 異步抓取火星圖片
翻譯:大江狗
原文鏈接:https://pfertyk.me/2017/06/getting-mars-photos-from-nasa-using-aiohttp/
小編註:aiohttp是基於asyncio實現的異步http框架。
本文案例也可以使用異步django實現。
我是Andy Weir寫的《火星人》一書的忠實粉絲。閱讀時,我想知道馬克·沃特尼(Mark Watney)繞著紅色星球走的感覺如何。最近,多虧瞭 Twilio的這篇博文, 我發現NASA提供瞭一個公共API,可以提供火星漫遊者拍攝的照片。但是,由於不是MMS的忠實擁護者,我決定編寫自己的應用程序,以將具有啟發性的圖像直接傳遞到我的瀏覽器中。
創建aiohttp應用程序
讓我們從一個簡單的應用程序開始,隻是為瞭啟動和運行aiohttp。首先,創建一個新的virtualenv。建議使用Python 3.5以後版本,因為我們將使用asyncio提供的async def和await語法。如果您想進一步開發該項目並利用異步理解的優勢,則可以使用Python 3.6(本例使用python版本)。接下來,安裝aiohttp:
pip install aiohttp
現在創建一個python文件(稱為nasa.py),並將一些代碼放入其中:
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')
如果您不熟悉aiohttp,則可能需要說明以下幾點:
- get_mars_photo協程是一個請求處理程序;它以HTTP請求作為唯一參數,並負責返回HTTP響應(或引發異常)
- app是高級服務器;它支持路由器,中間件和信號(對於該程序,我們將僅使用路由器)
- app.router.add_get 在HTTP GET方法和’/’路徑上註冊請求處理程序
註意:請求處理程序不必一定是協程,它們可以是常規函數。但是我們將使用asyncio的功能,因此程序中的大多數函數都將使用進行定義async def。
運行應用程序
要運行您的應用程序,您可以在文件末尾添加以下行:
web.run_app(app, host='127.0.0.1', port=8080)
然後像運行其他任何Python腳本一樣運行它:
python nasa.py
但是有更好的方法。在許多第三方庫中,您可以找到aiohttp-devtools。它提供瞭一個很好的runserver命令,可以自動檢測您的應用並支持實時重載:
pip install aiohttp-devtools adev runserver -p 8080 nasa.py
現在如果您訪問localhost:8080,則應該在瀏覽器中看到”A photo of mars”的字樣。
使用NASA API
當然,這還沒有結束。如果您是一位敏銳的觀察者,您會註意到我們沒有得到實際的圖像,而是一些文本。現在讓我們解決這個問題。
要從火星獲取照片,我們將使用NASA API。每個火星探路者(rover)都有自己的URL(對於好奇號,它url是https://api.nasa.gov/mars-photos/api/v1/rovers/curiosity/photos)。我們必須為每個請求至少提供2個參數:
- sol:火星輪轉或拍攝照片的日期,從探路者的著陸日期開始算起(最大值可以rover/max_sol在響應的一部分中找到 )
- API_KEY:由美國航天局提供的API密鑰(你可以使用默認的:DEMO_KEY)
響應數據裡我們將獲得一張照片列表,每張照片均帶有URL,相機信息和探路者信息。
修改nasa.py文件,如下所示:
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)
到底發生瞭什麼事?
- 我們選擇一個隨機拍攝日期(對於“好奇心” max_sol,在撰寫本文時,其值為1722)
- ClientSession 創建一個會話,我們可以使用該會話從NASA API獲取響應
- 我們使用獲取JSON響應 resp.json()
- 我們檢查響應中是否存在“照片”鍵;如果沒有,我們已經達到瞭每小時請求數量的上限,我們需要稍等片刻
- 如果當天沒有照片,我們會再次檢查是否有其他拍攝時間
- 然後,我們使用HTTPFound響應重定向到找到的照片
獲取NASA API密鑰
DEMO_KEYNASA提供的默認設置可以正常工作,但是您很快就會達到每小時API調用的限制。我建議您獲取自己的API密鑰。您可以在此處進行操作 (註冊過程非常簡單快捷)。
現在,當您運行該應用程序時,您將直接從火星重定向到一個漂亮的圖像:
好吧,這不完全是我的意思…
驗證圖像
您剛剛看到的圖像並不讓人受到啟發。事實證明,漫遊者拍攝瞭很多非常無聊的照片。我想看看馬克·沃特尼(Mark Watney)在他不可思議的旅程中所看到的,但這還不夠好。讓我們找到一種解決方法。
我們將需要對圖像進行某種形式的驗證。在指定篩選條件前,我們可以修改代碼:
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')
這裡發生瞭一些新的事情:
- 我們使用先前定義的函數獲取URL,然後使用讀取圖像中的原始字節 resp.read()
- 我們檢查我們的圖片是否足夠好;如果沒有,我們一直在尋找
- 一旦有瞭令人滿意的照片,我們會將其放入響應中(註意,我們仍然使用與web.Response以前相同的照片,但是這次我們指定body 而不是text,同時瞭定義content_type
註意:在此代碼中,我們刪除瞭重定向(HTTPFound),因此現在我們可以輕松地刷新頁面以獲取另一個圖像。
現在我們需要弄清楚如何驗證照片。我們可以很容易做到的一件事就是檢查圖像尺寸否足夠大。這不是一個完美的驗證,但現在應該這樣做。要處理圖像,我們將需要python的圖片庫Pillow。
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 >= 1024
現在刷新瀏覽器,應該可以看到火星大圖瞭。
現在我們可以更進一步,拒絕灰度圖像:
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'
現在我們的程序開始返回更多鼓舞人心的照片:
偶爾還能看到機器人自拍:
總結
我們整個程序如下所示:
import random import io from aiohttp import web, ClientSession 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 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')
我們還可以改善很多事情(例如max_sol從API中獲取價值,傳遞流動站的名稱,緩存URL),但是現在它已經完成瞭工作:我們可以得到一張隨機的,鼓舞人心的火星照片,並覺得我們確實在那裡。
我希望您喜歡這個簡短的教程。如果您發現錯誤或有任何疑問,請告訴我。
以上就是python 基於AioHttp 異步抓取火星圖片的詳細內容,更多關於python AioHttp 抓取火星圖片的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- Python Http發送請求淺析
- Python使用signal定時結束AsyncIOScheduler任務的問題
- Python異步爬蟲requests和aiohttp中代理IP的使用
- 熱門問題python爬蟲的效率如何提高
- python 常用的異步框架匯總整理