python 基于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 抓取火星图片的资料请关注其它相关文章!