几行代码从网上获取数据
大多数真实的 Python 程序迟早都要通过网络与别的服务通信:REST API、天气服务、GitHub 接口、文件下载。标准库可以做到这些(通过 urllib),但 Python 世界里事实上的标准工具是一个叫 requests 的第三方库。它的 API 友好太多了,值得你执行一次 pip install。
pip install requests
如果你还没有在虚拟环境中工作,先创建一个,这样安装的库只作用于你的项目。
第一个 GET 请求
三行代码:调用 get,检查状态码,查看响应体。response.text 是字符串形式的响应体。这里做了截断,因为完整的 JSON 很长。
值得像速查表一样记住的状态码:
- 200:OK,一切正常。
- 201:已创建(常见的 POST 响应)。
- 301 / 302:重定向;
requests默认会自动跟随。 - 400:错误请求;你发送的内容有问题。
- 401 / 403:未认证 / 无权限。
- 404:资源不存在。
- 429:请求过于频繁,被限流了,请放慢速度。
- 500:服务器错误。
解析 JSON 响应
当接口返回 JSON 时,对响应调用 .json(),它会解析响应体并返回一个字典(或列表):
在底层,.json() 和 json.loads(response.text) 是一样的,只是针对常见情况的快捷方式。
发送查询参数
不要手动把 ?key=value&... 拼接到 URL 上。把一个字典传给 params=:
requests 会替你处理 URL 编码,空格、特殊字符、Unicode 都能安全处理。
实际发出的 URL 可以通过 response.url 查看,调试时很方便。
带 JSON 请求体的 POST 请求
要发送数据,比如创建资源、提交表单、调用会修改数据的接口,就用 requests.post:
import requests
payload = {
"title": "Docs update",
"body": "Added HTTP requests page.",
"labels": ["docs"],
}
response = requests.post(
"https://api.example.com/issues",
json=payload,
headers={"Authorization": "Bearer YOUR_TOKEN"},
)
print(response.status_code)
print(response.json())
json= 参数做了两件事:把 payload 序列化为 JSON,并设置 Content-Type: application/json。你也可以用 data=json.dumps(payload) 加上显式的请求头手动完成,但 json= 是惯用的快捷写法。
如果要发送表单编码的数据(传统 HTML 表单发送的那种),改用 data=:
requests.post("https://example.com/login", data={"user": "rosa", "password": "..."})
请求头
以字典的形式传入任意自定义请求头:
import requests
response = requests.get(
"https://api.example.com/profile",
headers={
"Authorization": "Bearer abc123",
"User-Agent": "my-tool/1.0",
},
)
大多数 API 都要求用 Authorization 请求头进行认证。具体的认证方案(Bearer、Basic、Token)请查看它们的文档。
超时设置不是可选项
默认情况下,requests 会一直等待响应。在真实的程序中,这会让一个不稳定的服务器把你的脚本卡死。一定要传入 timeout:
import requests
try:
response = requests.get("https://api.example.com/slow", timeout=5)
except requests.Timeout:
print("Server took too long.")
这个数字的单位是秒。timeout=5 表示“5 秒内没收到响应就放弃”。想要更精细的控制,可以传入一个元组 (connect_timeout, read_timeout)。
错误处理
可能出现两类问题:
- HTTP 层面的错误(4xx、5xx):服务器响应了,但响应是一个错误。
response.status_code会告诉你。 - 网络层面的问题:超时、DNS 解析失败、主机不可达。这些会抛出异常。
惯用的写法把两者结合起来:
对于 2xx 响应,raise_for_status() 什么也不做,否则就抛出异常。RequestException 是 requests 抛出的所有错误的基类,捕获它就能一次覆盖“与这个接口通信时出的任何问题”。
下载文件
对于大型二进制文件的下载,以流的方式读取响应,避免它全部留在内存中:
import requests
url = "https://example.com/large.zip"
with requests.get(url, stream=True, timeout=30) as r:
r.raise_for_status()
with open("large.zip", "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
stream=True 告诉 requests 不要预先加载响应体。iter_content(chunk_size=...) 一次产出一块响应体,你直接把它写到磁盘上。
会话:复用连接和默认设置
如果你要向同一个服务发送多个请求,就用 Session。它会复用底层的 TCP 连接(更快),还能让你一次性设置默认值:
import requests
session = requests.Session()
session.headers.update({"Authorization": "Bearer abc123"})
# Every request through this session carries the header.
a = session.get("https://api.example.com/users/1")
b = session.get("https://api.example.com/users/2")
c = session.post("https://api.example.com/users", json={"name": "Rosa"})
对于要几十次调用同一个 API 的脚本,使用会话能明显提速。
一个贴近实际的例子:简易 GitHub 客户端
获取某个仓库的最新发布版本:
import requests
def latest_release(owner, repo):
url = f"https://api.github.com/repos/{owner}/{repo}/releases/latest"
response = requests.get(url, timeout=10)
response.raise_for_status()
data = response.json()
return {
"tag": data["tag_name"],
"name": data["name"],
"published": data["published_at"],
"url": data["html_url"],
}
release = latest_release("python", "cpython")
print(release)
不到十五行代码:构建 URL、发送请求、检查错误、提取你关心的字段。你将来写的大多数 API 客户端都是这个样子。
那 urllib 呢?
标准库的 urllib.request 能做到 requests 能做的一切,只是代码更多、用起来更别扭。如果你确实无法添加依赖,可以用它:
import json
import urllib.request
with urllib.request.urlopen("https://api.github.com/repos/python/cpython") as r:
data = json.loads(r.read().decode("utf-8"))
print(data["name"])
只要不是临时写的小脚本,requests(需要异步时用 httpx)都值得安装。
几个好习惯
- 始终设置超时。 没有例外。
- 在预期请求会成功的脚本中使用
raise_for_status(),它会把错误的响应变成一个醒目的异常。 - 把字典传给
params=和json=,不要手动拼接字符串。 - 频繁调用同一个 API 时,把相关调用放进一个
Session。 - 调试时记录
response.status_code和response.text,响应体通常会准确告诉你服务器不满意的地方。
下一篇:日期和时间
有了 requests 这件工具,你就可以与任何现代 Web API 通信、下载文件,并在服务之间构建小型集成。结合前面的 JSON 和 CSV 页面,你现在已经掌握了完整的“获取数据、读取数据、处理数据、写回数据”流程,大量真实的 Python 脚本都是这个结构。不过,这些数据大多带有时间戳,下一页将介绍 Python 如何表示日期和时间,以及值得避开的时区陷阱。
常见问题
Python 中怎么发送 HTTP 请求?
用 pip install requests 安装 requests 库,然后调用 requests.get(url) 发送 GET 请求,或调用 requests.post(url, json=...) 发送 POST 请求。响应对象有 .status_code、.text、.json() 和 .headers。例如:r = requests.get('https://api.example.com/users/1')。
应该用 requests 还是 urllib?
凡是要自己手写的请求都用 requests,它的 API 友好得多。urllib 是内置的,在无法添加依赖时也够用,但处理 JSON 请求体和会话等功能需要写更多代码。在生产环境中,很多团队还会使用 httpx(一个兼容 requests、支持异步的库)。
Python 中怎么在 POST 请求里发送 JSON?
把 json={'key': 'value'} 传给 requests.post(...)。requests 会把字典序列化成 JSON,并替你设置 Content-Type: application/json。不要同时传 data= 和 json=,二选一。
用 requests 库时怎么处理错误?
检查 response.status_code(200 表示成功),或者调用 response.raise_for_status(),在遇到 4xx/5xx 时抛出异常。网络层面的问题(超时、DNS 解析失败)会抛出 requests.RequestException 的子类,捕获它就能同时覆盖这两种情况。