上一篇你已经让程序”上网”了,可拿回来的是一整张画给人看的网页,想从里面抠出一个数字,费劲还容易碎。今天学的 API,就是网站专门留给程序的”官方点餐窗口”:你递上一句正经请求,它直接把干干净净的数据端给你。今天只引入一个新概念:API——以及它的黄金搭档
回应.json(),把数据原封不动拆成 Python 里的字典
一、从”抠网页”的痛苦说起
先讲讲上一篇留下的隐患。上一篇结尾让你试着上网查天气,你多半是这么干的:requests.get("https://www.weather.com.cn/..."),拿回来一大坨 HTML,然后……然后就卡住了。
那坨 HTML 是画给人看的:里面除了”沈阳 晴 20°C”,还有一堆导航栏、广告位、脚本代码、样式表。你想拿的就一个数字,却要在几千行字符里大海捞针。更要命的是,网站哪天改个版式,你辛辛苦苦写好的”抠取逻辑”当场报废。
这感觉就像:你想知道饭店今天有什么菜,人家明明有菜单,你却趴在后厨窗户上扒着看厨师炒菜,从油烟里猜今天卖什么。
其实人家有正经的点餐窗口。
很多网站早就料到”程序也会来问问题”,于是专门开了个窗口:不画页面、不搞装修,你说”我要沈阳今天的天气”,它直接把一份干干净净的数据递出来——天气就是天气,数字就是数字,一个多余的字都没有。这个窗口,行话叫 API。
今天就一个任务:学会在点餐窗口点菜。
二、今天的新概念:API——网站的”官方点餐窗口”
API 到底是什么?用点餐来想
API,全名 Application Programming Interface(应用程序编程接口)——名字挺唬人,拆开看就是”程序之间打交道的接口”。跟AI聊的时候你直接说”API”就行,全世界都这么叫。
还是用点餐打比方,一次 API 调用就是完整的一趟点餐:
| 点餐 | API 调用 | 你已经会的部分 |
|---|
|——|———-|—————-|
| 走到点餐窗口 | 指定网址(含”点什么”) | 上一篇的网址和参数 |
|---|---|---|
| 说”一份番茄炒蛋” | requests.get(网址) |
上一篇的 requests.get |
| 服务员递出一份菜 | 网站返回一份数据 | 上一篇的”拆包裹” |
| 菜装在标准餐盒里 | 数据装在 JSON 里 | 第27篇的 JSON |
| 你把菜倒进自家碗里 | 回应.json() 变成字典 |
今天的新动作 |
看出来了吗?四个环节里三个你都学过了。今天真正的新面孔只有一个:API 这个”点餐窗口”本身——知道它存在、会找它、会点菜,就毕业了。而”把菜倒进自家碗里”的那个动作 回应.json(),本质就是第27篇的 JSON 解析,只是 requests 帮你把这一步包成了一个方法,简单到不像新知识。
三行代码,点第一份菜
先点一份最简单的:一言(hitokoto.cn,一个免费开放的句子接口,不要注册、不要密钥,特别适合第一次练手)。
import requests
# 走到点餐窗口,点一份"随机一句话"
回应 = requests.get("https://v1.hitokoto.cn/", timeout=10)
# 把餐盒里的菜倒进自家碗里——得到一个字典
数据 = 回应.json()
print(数据)
print("今日一句:", 数据["hitokoto"])
print("出自:", 数据["from"])
跑完你会看到类似这样的输出:
{'id': 7432, 'hitokoto': '种一棵树最好的时间是十年前,其次是现在。', 'from': '谚语', ...}
今日一句: 种一棵树最好的时间是十年前,其次是现在。
出自: 谚语
注意 回应.json() 这一行干了什么:上一篇 回应.text 给你的是一整段字符串(要自己拿第27篇的 json.loads() 去拆),而 回应.json() 直接把 JSON 拆成了 Python 字典——键值对、能用 数据["hitokoto"] 直接取值的那种字典。一步到位,连 import json 都省了。
就这一行 回应.json(),你点的菜就从”装在餐盒里的半成品”变成了”倒进自家碗里、筷子就能夹”的状态。
“菜单”长什么样:参数就是你点的菜
点餐窗口当然不能只有一道菜。API 的”菜单”一般由两部分组成:网址(窗口开在哪儿)和参数(你点什么、有什么忌口)。
参数的写法你上一篇也会了——网址后面挂问号。比如一言接口想点一句”动漫类”的句子:
import requests
# 节目类型(c=动漫)、返回格式(encode=json)都写在参数里
回应 = requests.get(
"https://v1.hitokoto.cn/",
params={"c": "k", "encode": "json"},
timeout=10,
)
数据 = 回应.json()
print("动漫一言:", 数据["hitokoto"], "——《" + 数据["from"] + "》")
params={"c": "k"} 里的 c=k 是什么意思?接口说了算。这就是 API 和普通网页最大的不同:每个 API 都有自己的”菜单说明”,行话叫接口文档——写清楚有哪些菜(参数)、怎么点(网址格式)、端出来什么(返回哪些字段)。
一言的菜单在这里:https://developer.hitokoto.cn/。你不用背,会看菜单比会背菜单重要一百倍——今天先把”看文档点菜”这个动作练熟,以后换任何一家”餐厅”都通用。
端出来的菜长什么样:JSON 就是第27篇的老朋友
打开上面程序打印的 数据 仔细看:{'id': 7432, 'hitokoto': '...', 'from': '...'}——这就是第27篇的字典,一模一样。API 端出来的菜,九成九装在 JSON 餐盒里,原因也简单:JSON 就是”程序之间交换数据的通用语”,第27篇讲过的那句话在今天完全应验。
所以取数据的功夫你早就会了:
数据 = 回应.json()
句子 = 数据["hitokoto"] # 字典取值,第8篇的老朋友
if "逗号" in 句子: # 条件判断,第5篇的老朋友
print("这句里有逗号")
for 键, 值 in 数据.items(): # 遍历字典,第8篇的老朋友
print(键, "→", 值)
没有一个语法是新的。这就是今天真正的学习量:认识 API、会 回应.json(),剩下的全是复习。
顺便把上一篇欠的”天气”补上
还记得上一篇预告说”查天气做成真家伙”吗?先别急着做完整工具(那是下一篇的活儿),今天先用 API 点一次天气菜,感受一下”官方窗口”和”扒网页”的差距:
import requests
# wttr.in:免费的天气接口,网址里直接写城市名,?format=j1 就是"端JSON给我"
回应 = requests.get("https://wttr.in/沈阳?format=j1", timeout=15)
数据 = 回应.json()
今天 = 数据["current_condition"][0]
print("当前温度:", 今天["temp_C"], "°C")
print("天气描述:", 今天["weatherDesc"][0]["value"])
对比一下上一篇:同样是查天气,上一篇拿到的是几千行画给人看的 HTML,今天拿到的是两行就能打印出来的干净数据。这就是”官方点餐窗口”和”扒后厨窗户”的差距。
先打一针疫苗:这个例子用到了中文城市名”沈阳”直接拼进网址。个别环境里中文网址会闹脾气(编码问题),真做工具时让AI帮你处理;今天先跑通主流程。另外 wttr.in 的数据结构是它家自己定的(current_condition 这种嵌套),别的接口不会长这样——结构看文档,别死记。
三、跟AI说需求→读懂→改进:第一次”API版”三步走
今天的保留节目,做一个真实小工具:“每日一句”卡片——每天跑一次,随机存一句名言到文件里,攒起来就是你自己的金句本。
第一步:跟AI说需求
“我是编程初学者,刚学 API 调用。帮我写个Python程序:调用一言接口 https://v1.hitokoto.cn/ (返回JSON,句子在 hitokoto 字段,出处在 from 字段),把’句子 —— 出处’追加保存到 quotes.txt 里,每跑一次存一条。要求带 timeout 和 try/except,出错不崩。代码完整可运行,加中文注释。”
注意这段话里的三个关键信息:具体网址(不是”找个名言API”)、字段名(hitokoto、from)、防崩要求。给AI的料越具体,代码越能直接用——这是上一篇网络版特供建议的延续。
第二步:读懂(逐行对照)
AI大概率给你这样的代码:
import requests
def 存一句():
try:
回应 = requests.get("https://v1.hitokoto.cn/", timeout=10)
if 回应.status_code == 200: # 先看回执,上一篇的规矩
数据 = 回应.json() # 今天的主角:拆JSON餐盒
一行 = 数据["hitokoto"] + " —— " + 数据["from"]
with open("quotes.txt", "a", encoding="utf-8") as f:
f.write(一行 + "n") # 第9篇的追加写入
print("已存下:", 一行)
else:
print("点餐失败,回执编号:", 回应.status_code)
except requests.RequestException as 原因:
print("网络出岔子:", 原因)
存一句()
逐行看:requests.get + status_code 是上一篇的,try/except 是第10篇的,open(..., "a") 是第9篇的,字典取值是第8篇的——唯一的新面孔还是 回应.json()。每篇只引入一个新概念,到这里又一次兑现。
第三步:改进(这是拉开差距的一步)
拿AI的代码直接跑,会踩到两个坑,正好当今天的”坑点疫苗”:
坑1:接口偶尔返回的字段跟文档不一样,数据["from"] 直接 KeyError 崩给你看。API 是”别人家的窗口”,人家少端一个菜是常事。改进的话这么说:
“请用 数据.get(‘from’, ‘佚名’) 取出处,字段缺失时不要崩,默认显示’佚名’。”
坑2:一直往 quotes.txt 里追加,一个月后文件几百条,想看看攒了多少句还得自己数。改进的话:
“每次存完后,把文件里的总条数也打印出来,比如’本子上已有42句’。”
合起来的成品(完整可运行):
import requests
def 存一句(文件名="quotes.txt"):
try:
回应 = requests.get("https://v1.hitokoto.cn/", timeout=10)
if 回应.status_code != 200:
print("点餐失败,回执编号:", 回应.status_code)
return
数据 = 回应.json()
句子 = 数据.get("hitokoto", "").strip()
出处 = 数据.get("from", "佚名") # 字段缺失也不崩
if not 句子:
print("这回没端出句子来,跳过")
return
一行 = f"{句子} —— {出处}"
with open(文件名, "a", encoding="utf-8") as f:
f.write(一行 + "n")
# 看看本子上攒了多少句
with open(文件名, "r", encoding="utf-8") as f:
条数 = len(f.readlines())
print(f"已存下:{一行}(本子上已有{条数}句)")
except requests.RequestException as 原因:
print("网络出岔子:", 原因)
存一句()
数据.get("from", "佚名") 这个小改动,是今天最值钱的一行:它把”别人家少端一个菜”从”程序崩溃”降级成”显示佚名”。对接 API,这手”拿不准就给默认值”的功夫必须会——你管得住自己的代码,管不住人家的接口。
四、四个必踩的坑(先打疫苗)
坑1:上来就找”要注册、要密钥”的API,卡在申请环节。很多正经API(汇率、股票、地图)要先注册、申请密钥(行话叫 API Key)、可能还要收费。第一次练手请认准”免注册免密钥“的:一言(v1.hitokoto.cn)、wttr.in 天气、httpbin.org(练参数回显)都是好老师。疫苗:跟AI说需求时加一句”找一个不需要API Key、免费、稳定的接口“,让它别给你挖坑。
坑2:回应.json() 说崩就崩。JSONDecodeError 是今天最常见的红字——因为你点的压根不是”菜”:网址写错、接口挂了、或者对方返回的是网页HTML,.json() 拆不开餐盒就掀桌子。疫苗:先看 status_code 是不是 200,再 .json();实在不放心就先 print(回应.text[:200]) 瞄一眼端出来的到底是菜还是菜单。
坑3:把 数据["xxx"] 当保险箱,一个 KeyError 全盘崩。JSON 里的字段是”人家说了算”,今天有 from 明天可能就没了。疫苗:拿不准的字段一律 数据.get("键", 默认值),核心字段缺失时宁可少显示,不要崩。
坑4:把免费接口当自家后院,一秒点一百次菜。免费API大多有频率限制(行话叫限流),写循环疯狂调用,轻则被限流拒绝,重则IP被封。疫苗:循环调用记得 time.sleep() 歇口气;真做”每小时自动查一次”的工具,用”跑一次就退出”的定时任务(想想第21篇的倒计时思路),别写 while True 死循环猛刷。
五、跟AI协作的正确姿势(API版特供)
写 API 代码,AI 有个天然优势也有个天然盲区:它背过海量接口文档,但它没见过你今天面对的这个接口此刻还活着没有。三条特供建议:
- 把接口文档或示例返回贴给AI。别问”怎么调API”,要问”这个接口 https://xxx 返回这种JSON(把真实返回贴上),帮我提取里面的YYY”。AI看过真实数据,字段名就不可能写错。
- 让AI先写”摸底代码”。新接口上手,先说”先写一段代码打印接口的完整返回和所有字段名,别急着提取”。摸清菜单再点菜,比对着猜强十倍。
- 要”防御性点菜”。直接说”请加 timeout、try/except、用 .get() 取字段,接口异常时打印友好提示并给默认值”。这三条是 API 代码的标配,说一遍AI就会一直帮你带着。
六、今日小练习
练习1:点一份”中文一言”。调用 https://v1.hitokoto.cn/?c=d(c=d 是”文学类”),打印句子和出处。
提示:数据 = 回应.json() 之后,句子在 数据["hitokoto"]、出处在 数据["from"]。试着换参数 c=k(动漫)、c=i(诗词)多点几道菜,看看菜单换菜的效果。
练习2:给”每日一句”加上筛选。只把长度超过15个字的句子存进 quotes.txt,太短的打印”这句太短,不要”跳过。
提示:len(句子) 量长度(第15篇单词统计器的老朋友),if 分个岔就行。想想这个小筛选放在”存一句”函数的哪一行最合适。
练习3(动脑题):翻开你自己的”点餐清单”。上一篇让你列过三个”每天想自动查的东西”,今天用 API 的眼光再看一遍:哪个东西有现成的免费接口(跟AI问一句”XXX有免密钥的API吗”)?哪个只能扒网页(那要等第40篇之后的爬虫功夫)?把三个都标上”API / 爬虫 / 不确定”。
提示:这题不用写代码——但请认真做,下篇预告里,我们就要把清单上最经典的那一个做成真家伙。
下篇预告
下一篇(第39篇):天气查询工具 —— 把”看天吃饭”交给程序。今天你已经用 API 点过一次天气菜了,下一篇把它做成一个像样的工具:输入城市名、自动查天气、温度和描述一目了然,还能顺手存进记录文件。requests + JSON + 函数三件套合体,你将拥有第一个”联网的正经软件”。
发表回复