小白python入门 - 34. 用 requests 调外部接口
2026/7/26 6:29:15 网站建设 项目流程

1. 本课定位:是什么、为何重要

第 33 课认清了 HTTP 的URL / 方法 / 状态码 / Header / Body
本课把这些约定,落成Python 里可维护的客户端代码

概念一句话
requests第三方 HTTP 客户端库:发请求、收响应,比标准库urllib更短、更清晰
Response 对象一次调用的结果:状态码、头、正文;.json()再变成 dict/list

为何重要:调天气、支付、GitHub、公司内部 API,几乎都是「写客户端」;写不稳(无超时、乱拼密钥、不看状态码)会在生产里埋雷。

对比已学:

已学(Day33 / urllib)本课(requests)
手写Request+urlopen+ 自己json.loadsrequests.get/post+r.json()
概念上的五件套五件套对应到函数参数与属性
知道要超时强制timeout写成习惯
知道 4xx/5xxraise_for_status/ 异常类型
pipinstallrequests

2. 本质、约束与常见坑

本质:
一次requests.get/post(...)≈ 组装请求(方法、URL、头、体)→ 经网络发出 → 得到Response
默认同步阻塞:这行不返回,后面的代码不会跑(这是第 35~36 课并发的伏笔)。

约束:

  1. 必须能访问目标主机;证书、代理、防火墙会导致失败。
  2. 默认不会永远重试;失败要你自己处理。
  3. 对方返回的不一定是 JSON(错误页常是 HTML)。
  4. r.json()得到的是新的Python 对象,只存在本机内存,不改服务器数据。

常见坑:

现象正确做法
不写timeout网络卡住时进程挂死几乎每次调用都带timeout=
r.json()不看状态码404 HTML 导致 JSON 解析炸raise_for_status()或判断status_code
json=data=混用Content-Type / 服务端解析不对JSON 用json=;表单常用data=
密钥写进源码泄露进 Git环境变量 / 配置文件(不进仓库)
asyncio里直接requests.get堵死事件循环异步用httpx/aiohttp(第 36 课)
无限猛重试打爆对方、自己被封有限次数 + 间隔;尊重 429

3. 最小调用:GET / POST

importrequests r=requests.get("https://httpbingo.org/get",params={"q":"python"},timeout=20,)print(r.status_code)print(r.json().get("args"))r2=requests.post("https://httpbingo.org/post",json={"name":"bob"},timeout=20,)print(r2.json().get("json")orr2.json().get("data"))

预期输出(形态):

200 {'q': ['python']} {'name': 'bob'}

调用前:没有 Response。
调用后:可读status_code.text.json()等。


4. 传参对照(params / json / data / headers)

参数数据放哪典型用途
params=URL 查询串GET 过滤、分页
json=Body,且设为 JSONPOST/PUT API
data=Body(常表单编码)传统 form
headers=请求头Token、UA、Accept
timeout=(不是 Body)最长等待秒数

小对照:

# 查询串:.../get?a=1requests.get(url,params={"a":1},timeout=20)# JSON Bodyrequests.post(url,json={"a":1},timeout=20)# 表单 Body(application/x-www-form-urlencoded)requests.post(url,data={"a":1},timeout=20)

同一份{"a":1},位置不同,服务端收到的「槽位」就不同——对应 Day33 的 query vs Body。


5. 响应怎么读(属性分类)

类别常用含义
状态status_codeokraise_for_status()成败与是否抛错
headers对方回的元数据
正文textcontentjson()字符串 / 字节 / 解析后的对象
r=requests.get("https://httpbingo.org/get",params={"x":1},timeout=20)print(r.status_code,r.ok)r.raise_for_status()data=r.json()# 新 dict,不是改服务器print(type(data),data.get("args"))

okvsraise_for_status

写法行为
if r.ok:2xx 为真,自行分支
r.raise_for_status()4xx/5xx 抛HTTPError,适合「失败就别往下走」

6. 能力分组:发送 · 稳健 · 会话 · 身份

6.1 发送类
  • requests.get/post/put/delete/patch
  • 或统一requests.request("GET", url, ...)
6.2 稳健类(强烈建议形成肌肉记忆)
try:r=requests.get(url,timeout=10)r.raise_for_status()data=r.json()exceptrequests.Timeout:print("超时")exceptrequests.HTTPErrorase:print("HTTP 错误",e.response.status_code)exceptrequests.RequestExceptionase:print("网络或请求层错误",e)

超时演示(访问约 5 秒延迟、timeout=2):

importrequeststry:requests.get("https://httpbingo.org/delay/5",timeout=2)exceptrequests.Timeout:print("Timeout")

预期输出:

Timeout

重试(教学版,有限次数 + 间隔):

importtimeimportrequests url="https://httpbingo.org/status/200"forattemptinrange(1,4):resp=requests.get(url,timeout=15)print(f"attempt{attempt}: status={resp.status_code}")ifresp.ok:breaktime.sleep(0.5)

预期输出:

attempt 1: status=200

收到429应降速,而不是加大并发硬刚。

6.3 会话类 Session
每次requests.getSession
Cookie默认不跨请求保持可自动保持
默认头每次传session.headers.update(...)一次
连接相对一次性更易复用(性能细节入门了解即可)
session=requests.Session()session.headers.update({"User-Agent":"python-lab-day34/1.0"})r=session.get("https://httpbingo.org/get",params={"page":1},timeout=20)
6.4 身份与密钥
importosimportrequests key=os.environ.get("DEMO_API_KEY","")headers={"Authorization":f"Bearer{key}"}ifkeyelse{}r=requests.get("https://httpbingo.org/headers",headers=headers,timeout=15)

错误:Authorization = "Bearer sk-xxxx"写死在仓库。
正确:环境变量注入;文档里只写变量名。


7. 落地场景(故事 + 写法)

场景方法关键参数
查公开仓库信息GETURL 路径参数 / 查询串
提交注册 JSONPOSTjson={...}
带令牌访问GET/POSTheaders=Authorization
对方偶发 503GET有限重试 + timeout

教学仍可用回声服务验证「参数有没有发对」;真实项目换成业务 Base URL 即可。


8. 综合实践(一键脚本)

mkdir-p~/python-lab/src/day34cd~/python-lab/src/day34 pipinstallrequests

Windows 推荐 Cygwin 或 WSL。

cat>requests_demo.py<<'EOF' # Day34: requests client import os import time import requests def main(): session = requests.Session() session.headers.update({"User-Agent": "python-lab-day34/1.0"}) print("--- GET params ---") r = session.get( "https://httpbingo.org/get", params={"q": "python", "page": 1}, timeout=20, ) r.raise_for_status() data = r.json() print("status:", r.status_code) print("args:", data.get("args")) print("--- POST json ---") r2 = session.post( "https://httpbingo.org/post", json={"name": "bob", "role": "student"}, timeout=20, ) r2.raise_for_status() body = r2.json() print("echo:", body.get("json") or body.get("data")) print("--- timeout demo ---") try: session.get("https://httpbingo.org/delay/5", timeout=2) except requests.Timeout: print("Timeout:", "Timeout") print("--- retry pattern ---") for attempt in range(1, 4): resp = session.get("https://httpbingo.org/status/200", timeout=15) print(f"attempt {attempt}: status={resp.status_code}") if resp.ok: break time.sleep(0.3) key = os.environ.get("DEMO_API_KEY", "") print("--- auth header style ---") print("DEMO_API_KEY set:", bool(key)) headers = {} if key: headers["Authorization"] = f"Bearer {key}" r3 = session.get("https://httpbingo.org/headers", headers=headers, timeout=15) r3.raise_for_status() hdrs = r3.json().get("headers") or {} has_auth = any(k.lower() == "authorization" for k in hdrs) print("Authorization in request:", has_auth or bool(key)) if __name__ == "__main__": main() EOFpython3 requests_demo.py

实测输出:

--- GET params --- status: 200 args: {'page': ['1'], 'q': ['python']} --- POST json --- echo: {'name': 'bob', 'role': 'student'} --- timeout demo --- Timeout: Timeout --- retry pattern --- attempt 1: status=200 --- auth header style --- DEMO_API_KEY set: False Authorization in request: False

总结与学习路线

  • requests把 Day33 的 HTTP 五件套变成参数与 Response 属性。
  • timeout + 状态处理 + 参数绑定式传参 + 密钥不进库是客户端底线。
  • Session适合连续调用;异步场景不要硬塞同步requests

建议学习路径:

  1. 基础— 会get/post,能打印status_codejson
  2. 操作— 分清params/json/data/headers
  3. 进阶raise_for_status、Timeout、有限重试、Session
  4. 实战— 用环境变量做 Bearer;封装小组件get_json(url)

思维拔高:好的客户端默认不信任网络——任何远程调用都可能慢、失败、返回怪数据。

下一课:并发模型怎么选(多个请求如何重叠等待)。

文档:Requests: HTTP for Humans


小练笔

题 1

为什么几乎总要设置timeout

题 2

params={"a": 1}json={"a": 1}区别?

题 3

判断:把 API Key 写进源码提交到 Git 是好习惯。

题 4

r.okr.raise_for_status()使用上的主要差别?

题 5

判断:r.json()会修改服务器上的数据。


小练笔参考答案

题 1

避免网络异常时程序无限等待。

题 2

params进入 URL 查询串;json进入请求 Body(JSON,并带合适 Content-Type)。

题 3

题 4

ok只给布尔值由你分支;raise_for_status在 4xx/5xx 时直接抛HTTPError

题 5

。只是把响应正文解析成本地 Python 对象。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询