使用Web API

本章节我们来学习一些网络的知识,为什么学习网络呢?因为用我们之前的知识虽然可以写出很复杂的程序了,但是只能处理自己电脑上存储的数据,还没有联网功能。但其实目前我们用到的大多数软件都是联网的软件,比如看视频的网站、聊天的微信等等。本章节我们就开学习一下基础的网络知识,当然完整的网络知识很复杂,需要专门的书籍来讲解,我们这里是带你入门丰富神奇的网络世界。最终我们会完成一个程序,实时显示太空中空间站的位置。话不多说,让我们开始吧!

空间站程序1-获取数据

丰富多彩的网络世界

首先让我们来看一个网站,这是国际空间站组织建立的一个网站,这个网页中可以获取3个信息:

  1. Current Location of the International Space Station:空间站当前的位置
  2. Overhead Pass Predictions for the International Space Station:空间站从当前位置头顶划过的预测:已经被移除了
  3. Number of People in Space:太空中的人数 如果我们要得到空间站当前的位置,是不是打开电脑浏览器找到这个网址就可以了,但如果要让我们的程序自动获取到这个信息就比较复杂了,要实现这个效果我们首先得知道这样一个网站是如何运行的。

Web API如何工作

当我们打开一个网站的时候,背后都发生了什么事情呢,其实有这样几个流程:

  1. 请求:浏览器是一个上网的工具,当我们在地址栏输入网址,或者通过百度这样的搜索引擎点击进入到某个网站的时候,其实都是向这个网址背后的服务器发送了一个请求:你好,我想要访问你的这个网页,这个过程我们叫“请求”;
  2. 响应:存储着这个网页的服务器会接收到刚才那个来自浏览器的请求,当然它不止会接收到1个这样的请求,会有很多人都来请求这个服务器,所以它会做一个判断,看看每个请求到底是具体要什么内容,然后再针对性的给每个请求返回特定的内容,这个过程我们叫“响应”; 对于普通的网页来说,响应的内容是HTML,通过查看网页源代码可以看到具体的HTML代码;有一些响应打开之后并不是这样美轮美奂的网页,而仅仅包含一些数据,这种我们一般称之为API,格式大多数为json,当然也还有其他格式,这里我们就不再深入了。

空间站API

以空间站API为例,能看到这种json格式是把数据以及数据的含义成对的放在了一起,比如第1个API返回的结果就是空间站目前的位置,也就是经纬度,第3个API返回的结果就是空间站中的人数以及具体的人名。那为什么不直接展示成美轮美奂的网页,而是只把数据展示出来呢,这是因为除了国际空间站管理组织可以做这样一个页面,对于天文爱好者来说,有了这些数据也可以自己编写程序来展示空间站的信息。接下来我们就会完成这样一个程序:从空间站API中获取到空间站的位置并且实时地展示在我们自己的电脑中。

requests模块

这里就要用到requests模块了,这是一个第3方模块,在前面模块化章节我们提到过,除了安装Python时自带的模块我们可以使用之外,我们还可以安装别人发布的优秀的模块来使用,这个requests模块就是一个优秀的关于网络请求的模块。要安装这种第3放模块,需要通过pip命令来安装一下,具体操作就是打开VSCode的终端或者直接用命令行也可以,输入pip install requests就可以了,如果python环境配置正常的话,这时候就会开始安装了,进度条走完之后就安装成功了。如何使用呢? 跟普通模块一样,要使用首先要import进来,然后通过get方法来向特定的网址发出一个请求,这个过程模拟的就是我们人类点击浏览器向服务器发送请求的行为。

import requests
url = 'http://api.open-notify.org/iss-now.json'
response = requests.get(url)
print(response.text)

当然,有时候服务器可能因为繁忙或者其他问题不一定能返回给我们正确的数据,所以这里我们可以加上一个状态码的判断,当没有正确的返回时我们做出相应的处理。

import requests
url = 'http://api.open-notify.org/iss-now.json'
response = requests.get(url)
if (response.status_code == 200):
    print(response.text)
else:
    print("Houston, we have a problem:", response.status_code)

空间站程序2-json处理

前面的程序中我们已经能够运用requests模块从空间站API中获取到我们想要的位置数据了,但是返回的数据是json格式,我们还不知道怎么把我们想要的经纬度信息提取出来。要想完成这个任务,首先需要对json格式有一定的了解。

json格式

我们以人物信息为例来演示一下json这种数据格式如何在Python中使用,下面这个是json数据的一部分,从这里能看出这个跟我们学过的Python中的什么类型比较相似吗?

"name": "侯林森", "address": "河南安阳", "career": "编程老师"

是的,字典!json跟字典是很像的,只不过字典是以大括号括起来的形式,但json本质上是一个字符串,它是有引号括起来的,但也有json特有的规则,通过大括号括起来。

'{"name": "侯林森", "address": "河南安阳", "career": "编程老师"}'

按照这个思路,我们把这样json格式的字符串变成字典是不是就可以啦。我们可以通过字符串操作来实现,不过Python自带的json模块已经可以很完美的解决这个问题了。

import json
json_string = '{"name": "侯林森", "address": "河南安阳", "career": "编程老师"}'
person= json.loads(json_string)
print(person['name'], person['address'], person['career'])

优化空间站程序

接下来我们把刚才学到的json模块知识应用到空间站程序当中,把从空间站API获取的json数据处理一下:

# P451
import requests, json
url = 'http://api.open-notify.org/iss-now.json'
response = requests.get(url)
if (response.status_code == 200):
    response_dictionary = json.loads(response.text)
    position = response_dictionary['iss_position']
    print('国际空间站在:' +      
        position['latitude'] + ', ' + position['longitude'])
else:
    print("我们遇到了一个问题:", response.status_code)

空间站程序3-地图显示

前面的课程中我们通过requests模块获取到了空间站的地理信息并使用json模块对数据进行了处理,但是还是冷冰冰的数据,如果能直接显示在地图上就更好了。那肯定是可以的,这里我们准备了一张世界地图,接下来我们来使用海龟模块来加载图片并将空间站展示到地图上。

认识screen对象

import requests, json, turtle
screen = turtle.Screen()
screen.setup(1000,500)
screen.bgpic('earth.gif')
screen.setworldcoordinates(-180, -90, 180, 90)
url = 'http://api.open-notify.org/iss-now.json'
response = requests.get(url)
if (response.status_code == 200):
    response_dictionary = json.loads(response.text)
    position = response_dictionary['iss_position']
    print('国际空间站在:' +      
        position['latitude'] + ', ' + position['longitude'])
else:
    print("我们遇到了一个问题:", response.status_code)
turtle.mainloop()

用乌龟表示国际空间站

import requests, json, turtle
screen = turtle.Screen()
screen.setup(1000,500)
screen.bgpic('earth.gif')
screen.setworldcoordinates(-180, -90, 180, 90)
iss = turtle.Turtle()
iss.shape('circle')
iss.color('red')
url = 'http://api.open-notify.org/iss-now.json'
response = requests.get(url)
if (response.status_code == 200):
    response_dictionary = json.loads(response.text)
    position = response_dictionary['iss_position']
    print('国际空间站在:' +      
        position['latitude'] + ', ' + position['longitude'])
else:
    print("我们遇到了一个问题:", response.status_code)
turtle.mainloop()

显示空间站图标

import requests, json, turtle
screen = turtle.Screen()
screen.setup(1000,500)
screen.bgpic('earth.gif')
screen.setworldcoordinates(-180, -90, 180, 90)
iss = turtle.Turtle()
turtle.register_shape("iss.gif")
iss.shape("iss.gif")
url = 'http://api.open-notify.org/iss-now.json'
response = requests.get(url)
if (response.status_code == 200):
    response_dictionary = json.loads(response.text)
    position = response_dictionary['iss_position']
    print('国际空间站在:' +      
        position['latitude'] + ', ' + position['longitude'])
else:
    print("我们遇到了一个问题:", response.status_code)
turtle.mainloop()

空间站程序4-实时位置

前面的程序中我们已经能够将空间站显示到地图上了,但其实只有在程序刚开始运行的一瞬间显示的位置是正确的,后面这个位置就不会更新了,那能不能做到让地图上的空间站能够实时更新位置呢?那肯定也是可以的。

优化代码

要实现实时显示空间站位置的功能,首先我们来优化一下代码结构,让程序的逻辑更加清晰:

#将移动的代码抽象成函数
import requests, json, turtle
def move_iss(lat, long):
    global iss
    iss.penup()
    iss.goto(long, lat)
    iss.pendown()
screen = turtle.Screen()
screen.setup(1000,500)
screen.bgpic('earth.gif')
screen.setworldcoordinates(-180, -90, 180, 90)
iss = turtle.Turtle()
turtle.register_shape("iss.gif")
iss.shape("iss.gif")
url = 'http://api.open-notify.org/iss-now.json'
response = requests.get(url)
if (response.status_code == 200):
    response_dictionary = json.loads(response.text)
    position = response_dictionary['iss_position']
    lat = float(position['latitude'])
    long = float(position['longitude'])
    move_iss(lat, long)
else:
    print("我们遇到了一个问题:", response.status_code)
turtle.mainloop()

将其他部分也抽象成函数:

import requests, json, turtle

iss = turtle.Turtle()

def setup(window):
    global iss

    window.setup(1000,500)
    window.bgpic('earth.gif')
    window.setworldcoordinates(-180, -90, 180, 90)
    turtle.register_shape("iss.gif")
    iss.shape("iss.gif")

def move_iss(lat, long):
    global iss

    iss.hideturtle()
    iss.penup()
    iss.goto(long, lat)
    iss.pendown()
    iss.showturtle()

def track_iss():
    url = 'http://api.open-notify.org/iss-now.json'
    response = requests.get(url)
    if (response.status_code == 200):
        response_dictionary = json.loads(response.text)
        position = response_dictionary['iss_position']
        lat = float(position['latitude'])
        long = float(position['longitude'])
        move_iss(lat, long)
    else:
        print("我们遇到了一个问题:", response.status_code)

def main():
    global iss
    screen = turtle.Screen()
    setup(screen)
    track_iss()

if __name__ == "__main__":
    main()
    turtle.mainloop()

定时运行

通过after函数实现定时运行:

# P461
import requests, json, turtle
iss = turtle.Turtle()
def setup(window):
    global iss
    window.setup(1000,500)
    window.bgpic('earth.gif')
    window.setworldcoordinates(-180, -90, 180, 90)
    turtle.register_shape("iss.gif")
    iss.shape("iss.gif")
def move_iss(lat, long):
    global iss
    iss.penup()
    iss.goto(long, lat)
    iss.pendown()
def track_iss():
    url = 'http://api.open-notify.org/iss-now.json'
    response = requests.get(url)
    if (response.status_code == 200):
        response_dictionary = json.loads(response.text)
        position = response_dictionary['iss_position']
        lat = float(position['latitude'])
        long = float(position['longitude'])
        move_iss(lat, long)
    else:
        print("我们遇到了一个问题:", response.status_code)
    widget = turtle.getcanvas()
    widget.after(5000, track_iss)
def main():
    global iss
    screen = turtle.Screen()
    setup(screen)
    track_iss()
if __name__ == "__main__":
    main()
    turtle.mainloop()                    

豆包AI机器人

前面我们已经完成了获取空间站API并显示到自己程序中的功能,本节课我们再来介绍一个有趣的API:豆包API,豆包是字节跳动公司退出的大语言模型聊天机器人服务,使用方法很简单,通过网页或者手机APP都可以直接跟机器人聊天,接下来我们会利用它的API来用Python实现一个对话机器人的程序并且可以进一步做一个让两个机器人互相对话的趣味程序。

智能对话机器人

官方API的写法:

from openai import OpenAI
client = OpenAI(
    api_key = "填写自己的API Key,
    base_url = "https://ark.cn-beijing.volces.com/api/v3",
)
while True:
    user_input = input("请输入:")
    if user_input in ['再见','退下']:
        print("AI:感谢您的使用,再见!")
        break
    response = client.chat.completions.create(
        model="ep-20241210181727-9fjv5",
        messages=[
            {
                "role": "user",
                "content": user_input,
            }
        ],
    )
    print("AI:"+response.choices[0].message.content)

基于requests模块的写法

import requests
import json
api_key = "6945a719-3ddc-4819-8a8b-3c944a0b2d44"
base_url = "https://ark.cn-beijing.volces.com/api/v3"
while True:
    user_input = input("请输入:")
    if user_input in ['再见', '退下']:
        print("AI:感谢您的使用,再见!")
        break
    # 请求的URL
    url = f"{base_url}/chat/completions"
   
    # 请求的headers
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }
   
    # 请求的payload
    payload = {
        "model": "ep-20241210181727-9fjv5",
        "messages": [
            {
                "role": "user",
                "content": user_input
            }
        ]
    }
   
    # 发送POST请求
    response = requests.post(url, headers=headers, data=json.dumps(payload))
   
    if response.status_code == 200:
        # 解析并打印AI的回应
        response_data = response.json()
        print("AI:" + response_data['choices'][0]['message']['content'])
    else:
        print(f"请求失败,状态码: {response.status_code}")

双人自动对话

import time
from openai import OpenAI
client = OpenAI(
    api_key="6945a719-3ddc-4819-8a8b-3c944a0b2d44",
    base_url="https://ark.cn-beijing.volces.com/api/v3",
)
# 开场白
initial_message = "我们来聊天吧"
limit_message = "限制在50字内,以纯文本输出,日常对话的口吻,避免重复和无意义的内容,尝试引入新话题。"
print("机器人1:" + initial_message)
while True:
    # 机器人2发言
    response2 = client.chat.completions.create(
        model="ep-20241210181727-9fjv5",
        messages=[
            {"role": "user", "content": initial_message + limit_message}
        ],
        temperature=0.8,  # 引入一定随机性
    )
   
    response2_content = response2.choices[0].message.content.strip()
    print("机器人2:" + response2_content)
    # 暂停1秒
    time.sleep(1)
   
    # 机器人1发言
    response1 = client.chat.completions.create(
        model="ep-20241210181727-9fjv5",
        messages=[
            {"role": "user", "content": response2_content + limit_message}
        ],
        temperature=0.8,  # 引入一定随机性
    )
   
    response1_content = response1.choices[0].message.content.strip()
    print("机器人1:" + response1_content)
    # 暂停1秒
    time.sleep(1)
    # 更新初始消息为机器人1的回复
    initial_message = response1_content
Last Updated:
Contributors: houlinsen, 爱博卡鲁