接入外部api
api是啥
api(Application Programming Interface)指应用程序编程接口,你可以通过调用由其他人提供的api服务实现特定功能,而无需编写具体功能的业务实现逻辑。
打个比方,你是顾客,api是服务员,只要你把要吃的菜告诉服务员,然后等待服务员把菜呈上来就行了,而无需其他操作。
接下来我们将以 随机柴郡表情包 为例,展示如何调用外部api
定义一个函数
选择接口
在api站点找到你想用的接口
观察得知,这个api的地址为http://api.yujn.cn/api/chaijun.php
,同时,它是直接返回图片的,不用传递其他参数。
定义函数
我们现在知道,只要向http://api.yujn.cn/api/chaijun.php
发送get请求,就可以获取到一张柴郡表情包的二进制数据,所以我们可以这样写。
新建一个python文件,写入下面的内容(假定这里创建的文件名叫做chaijun.py
)
import httpx
async def chaijun(): #async def是固定前缀,不能变
url = "http://api.yujn.cn/api/chaijun.php?" #柴郡图片的api地址
async with httpx.AsyncClient() as client: #使用httpx库发送get请求
r = await client.get(url)
path="pic.png" #图片保存路径
with open(path, "wb") as f: #将图片保存到本地
f.write(r.content)
return path #返回图片路径
if __name__ == '__main__':
import asyncio #调用测试
asyncio.run(chaijun())
好的,我们顺利地写出了第一个功能函数,运行这个函数吧,你应该会得到一张柴郡表情包的图片。
调用函数
记得上一节我们创建的主函数main.py吗?现在我们可以通过它使用刚才做的柴郡表情包功能了。
from Eridanus.adapters.websocket_adapter import WebSocketBot
from Eridanus.event.events import GroupMessageEvent
from Eridanus.message.message_components import Image, Text
from chaijun import chaijun
bot = WebSocketBot('ws://127.0.0.1:3001')
@bot.on(GroupMessageEvent)
async def _(event: GroupMessageEvent):
if event.pure_text=="柴郡":
bot.logger.info("找一张柴郡表情包!")
path=await chaijun()
await bot.send(event,Image(file=path))
#如果你想图文一起发,可以这样写👇
#await bot.send(event,[Text("柴郡表情包"),Image(file=path)])
bot.run()
这样,当你在群里发送 柴郡 的时候,bot就会下载一张柴郡表情包发给你。
恭喜你完成本章节学习。
后记:为什么不是requests?
同步和异步
在上面柴郡表情包功能实现中,其实还有另一种写法
import requests
def chaijun():
url = "http://api.yujn.cn/api/chaijun.php?" #柴郡图片的api地址
r = requests.get(url)
path="pic.png" #图片保存路径
with open(path, "wb") as f: #将图片保存到本地
f.write(r.content)
return path #返回图片路径
if __name__ == '__main__':
chaijun()
为什么我们不这样写?这样看起来似乎是更加简单且符合直觉的写法。
这是因为requests库的请求是【同步】的,如果你使用这种写法,当bot在执行chaijun指令时,bot将无法执行任何其他指令,直到 柴郡 任务执行完毕。
在 柴郡 任务中,如果你使用【同步】写法,并不会感觉到明显【阻塞】,这是因为因为图片本身体积很小,同步任务执行的很快以至于看起来似乎没有阻塞,一旦换成其他耗时的请求操作,这样写的弊端就会暴露无遗。
httpx是一个【异步】库,异步编程允许程序在等待I/O操作完成时执行其他任务,从而避免了程序在I/O操作期间的空闲等待,因此不会出现阻塞情况。
伪异步
#伪异步示例
import requests
async def chaijun():
url = "http://api.yujn.cn/api/chaijun.php?" #柴郡图片的api地址
r = requests.get(url)
path="pic.png" #图片保存路径
with open(path, "wb") as f: #将图片保存到本地
f.write(r.content)
return path
尽管函数前加上 async 可以使其成为协程函数,但这并不意味着函数内部的操作会自动变为异步。async 只是标记了函数是异步的,函数内部如果调用的是同步操作(例如 requests.get()),那么这些操作依然会阻塞当前线程。因此,async 本身并不会改变函数执行的同步本质,只有使用支持异步的库(如 httpx)才能实现非阻塞的异步操作。