Python 调用接口:从 Curl 到 Requests 的深度解析
在现代软件开发中,与外部服务或系统进行数据交互是核心任务之一。无论是获取天气预报、调用支付网关、访问社交媒体 API,还是与微服务架构中的其他组件通信,接口调用(或称为 API 调用)无处不在。
在命令行环境中,curl
无疑是进行 HTTP 请求测试和简单交互的瑞士军刀。它的语法简洁、功能强大,几乎是每个开发者都熟悉或至少接触过的工具。然而,当我们需要在 Python 程序中进行复杂的接口调用、处理响应数据、集成业务逻辑,或者需要进行批量、自动化的请求时,直接调用 curl
命令变得笨拙且低效。这时,我们需要一个更强大、更灵活、更“Pythonic”的库来完成这项任务。
这就是 requests
库的用武之地。它被誉为“HTTP for Humans”,是 Python 中进行网络请求的 de facto 标准。本文将深入探讨如何从 curl
的命令行思维平滑过渡到 requests
的 Python 编程模式,详细对比两者在各种场景下的用法,并展示 requests
的强大功能和便利性。
1. curl
:命令行中的 HTTP 利器
在开始转向 Python 之前,我们先回顾一下 curl
的基本用法。理解 curl
的常见命令行选项,有助于我们更好地理解如何在 requests
中实现相同的功能。
curl
的基本语法通常是:
bash
curl [options] [URL]
以下是一些 curl
的常见应用示例:
1.1 发送 GET 请求
最简单的 curl
用法是发送 GET 请求,默认情况下 curl
就是发送 GET。
bash
curl http://api.example.com/resource
这会向指定的 URL 发送一个 GET 请求,并将服务器返回的响应体打印到标准输出。
1.2 发送 POST 请求
发送 POST 请求通常需要指定请求方法 -X POST
和请求体数据 -d
或 --data
。
发送表单数据 (application/x-www-form-urlencoded):
bash
curl -X POST -d "param1=value1¶m2=value2" http://api.example.com/submit
发送 JSON 数据 (application/json),通常需要设置 Content-Type 头部:
bash
curl -X POST -H "Content-Type: application/json" -d '{"key": "value"}' http://api.example.com/json_submit
这里:
* -X POST
:指定 HTTP 请求方法为 POST。
* -d
:指定请求体数据。如果是多个参数,用 &
连接。如果数据是 JSON,需要用单引号或双引号括起来,并注意转义。
* -H "Content-Type: application/json"
:设置请求头 Content-Type
为 application/json
,告诉服务器请求体是 JSON 格式。
1.3 添加请求头 (Headers)
除了 Content-Type,我们经常需要添加其他请求头,如认证信息 (Authorization)、用户代理 (User-Agent) 等,这通过 -H
选项实现。
bash
curl -H "Authorization: Bearer your_token" -H "User-Agent: MyCurlClient/1.0" http://api.example.com/secure_resource
多个请求头可以多次使用 -H
选项。
1.4 处理查询参数 (Query Parameters)
GET 请求的查询参数直接附加在 URL 后面,curl
会正确处理包含参数的 URL。
bash
curl http://api.example.com/search?query=test&page=1
1.5 设置超时
curl
可以使用 --connect-timeout
设置连接超时,使用 -m
或 --max-time
设置整个请求的最大允许时间。
bash
curl --connect-timeout 5 -m 10 http://api.example.com/slow_resource
这表示连接阶段最多等待 5 秒,整个请求过程最多等待 10 秒。
1.6 处理 SSL/TLS 证书问题
默认情况下,curl
会验证 SSL/TLS 证书。如果遇到自签名证书或证书链问题,可以使用 -k
或 --insecure
选项禁用证书验证(但这样做不安全,应谨慎使用)。
bash
curl -k https://self-signed-example.com/
1.7 使用代理
通过 -x
或 --proxy
选项指定代理服务器。
bash
curl -x http://proxy.example.com:8080 http://api.example.com/
1.8 保存响应到文件
使用 -o
选项将响应体保存到文件。
bash
curl -o response.html http://api.example.com/page
curl
还有很多其他高级功能,比如处理 Cookie、跟随重定向、限制带宽等。它在命令行环境中非常方便快捷,适合进行即时测试和简单的脚本编写。
2. 为什么从 curl
转向 Python 的 requests
?
尽管 curl
功能强大,但在以下场景下,在 Python 程序中直接调用 requests
库是更优的选择:
- 编程逻辑集成: 你需要在获取数据后进行复杂的处理、分析、存储或与其他数据源整合,这需要一个完整的编程环境。
- 自动化和批量处理: 你需要根据条件动态生成请求、循环调用 API、处理大量的请求和响应,这在 Python 中更容易实现。
- 错误处理和重试机制:
requests
提供了更细粒度的错误处理方式(如捕获特定异常、检查状态码),方便实现健壮的重试逻辑。 - 数据格式处理:
requests
能够自动处理 JSON、表单数据等格式的发送和接收,特别是响应的 JSON 解析,非常方便。 - 代码可读性和维护性: 将 API 调用逻辑写在 Python 代码中,比编写复杂的 shell 脚本调用
curl
更易读、易懂、易维护。 - 模块化和复用: 可以将 API 调用封装成函数或类,在程序的不同部分复用。
- 依赖管理和环境隔离: Python 项目通常使用虚拟环境管理依赖,包括
requests
,避免系统环境污染。
简而言之,当你的 API 调用不再是简单的一次性测试,而是成为应用程序的一部分时,requests
提供了比 curl
脚本更优雅、更强大、更易于集成的解决方案。
3. requests
:Python 中的 HTTP 库
requests
库是 Python 社区广泛推荐和使用的 HTTP 客户端库。它的设计理念是“HTTP for Humans”,力求简洁直观。
首先,你需要安装它:
bash
pip install requests
安装完成后,就可以在 Python 代码中导入并使用它了。
4. curl
命令到 requests
代码的映射
现在,我们来详细对比 curl
的常见用法如何在 requests
中实现。
4.1 基本 GET 请求
curl
:
bash
curl http://api.example.com/resource
requests
:
“`python
import requests
url = “http://api.example.com/resource”
response = requests.get(url)
打印响应状态码
print(f”状态码: {response.status_code}”)
打印响应体 (字符串形式)
print(f”响应体:\n{response.text}”)
检查请求是否成功 (状态码在 200-399 之间)
print(f”请求成功? {response.ok}”)
“`
解释:
* 导入 requests
库。
* 使用 requests.get(url)
发送 GET 请求。这个函数返回一个 Response
对象。
* response.status_code
:获取 HTTP 状态码(如 200, 404, 500 等)。
* response.text
:获取响应体的文本内容(通常是 HTML, XML, JSON 等字符串)。
* response.ok
:一个布尔值,表示状态码是否在 200 到 399 之间。
4.2 发送 POST 请求
发送表单数据 (application/x-www-form-urlencoded
)
curl
:
bash
curl -X POST -d "param1=value1¶m2=value2" http://api.example.com/submit
requests
:
“`python
import requests
url = “http://api.example.com/submit”
payload = {‘param1’: ‘value1’, ‘param2’: ‘value2’}
response = requests.post(url, data=payload)
print(f”状态码: {response.status_code}”)
print(f”响应体:\n{response.text}”)
“`
解释:
* 使用 requests.post(url, data=...)
发送 POST 请求。
* data
参数接受一个字典。requests
会自动将这个字典编码为 application/x-www-form-urlencoded
格式,并设置 Content-Type
头部。
发送 JSON 数据 (application/json
)
curl
:
bash
curl -X POST -H "Content-Type: application/json" -d '{"key": "value"}' http://api.example.com/json_submit
requests
:
“`python
import requests
import json # 虽然 requests 内部处理,但了解 json 库有益
url = “http://api.example.com/json_submit”
payload = {‘key’: ‘value’, ‘number’: 123, ‘bool’: True} # Python 字典
方式一:使用 json 参数 (推荐)
response = requests.post(url, json=payload)
print(f”状态码 (json 参数): {response.status_code}”)
print(f”响应体:\n{response.text}”)
方式二:手动 json.dumps() 并使用 data 参数加 headers (等效,但不推荐除非有特殊需求)
headers = {‘Content-Type’: ‘application/json’}
response = requests.post(url, data=json.dumps(payload), headers=headers)
print(f”状态码 (data 参数+headers): {response.status_code}”)
print(f”响应体:\n{response.text}”)
“`
解释:
* requests
提供了 json
参数专门用于发送 JSON 数据。你只需提供一个 Python 字典或列表。
* 使用 json=payload
时,requests
会自动:
* 将 Python 对象编码为 JSON 字符串(内部调用 json.dumps()
)。
* 设置 Content-Type
请求头为 application/json
。
* 这是发送 JSON 数据最便捷的方式。
4.3 添加请求头 (Headers)
curl
:
bash
curl -H "Authorization: Bearer your_token" -H "Custom-Header: custom_value" http://api.example.com/secure_resource
requests
:
“`python
import requests
url = “http://api.example.com/secure_resource”
headers = {
“Authorization”: “Bearer your_token”,
“Custom-Header”: “custom_value”,
“User-Agent”: “MyRequestsClient/1.0” # 也可以自定义 User-Agent
}
response = requests.get(url, headers=headers)
print(f”状态码: {response.status_code}”)
print(f”响应头:\n{response.headers}”) # Response headers 是一个类似字典的对象
print(f”响应体:\n{response.text}”)
“`
解释:
* headers
参数接受一个字典,键是请求头名称,值是请求头的值。
* requests
会自动合并你提供的头部与一些默认头部(如 Connection, Accept-Encoding 等)。
* response.headers
可以访问服务器返回的响应头。
4.4 处理查询参数 (Query Parameters)
curl
:
bash
curl http://api.example.com/search?query=test&page=1
requests
:
“`python
import requests
url = “http://api.example.com/search”
params = {
‘query’: ‘test search’, # 包含空格等特殊字符也没问题
‘page’: 1,
‘active’: True # requests 会自动处理布尔值等类型
}
response = requests.get(url, params=params)
requests 会自动构建完整的 URL: http://api.example.com/search?query=test+search&page=1&active=True
print(f”请求的完整 URL: {response.url}”)
print(f”状态码: {response.status_code}”)
print(f”响应体:\n{response.text}”)
“`
解释:
* params
参数接受一个字典。
* requests
会自动将字典中的键值对编码并附加到 URL 中作为查询字符串,无需手动处理 URL 编码(如空格变 %20
或 +
)。这使得处理包含特殊字符或多种数据类型的参数变得非常方便。
4.5 设置超时 (Timeout)
curl
:
bash
curl --connect-timeout 5 -m 10 http://api.example.com/slow_resource
requests
:
“`python
import requests
url = “http://api.example.com/slow_resource”
方式一:单一数值,同时用于连接超时和读取超时
response = requests.get(url, timeout=10) # 整个请求过程最多 10 秒
方式二:元组,(连接超时, 读取超时)
try:
# 连接超时 5 秒,读取超时 10 秒
response = requests.get(url, timeout=(5, 10))
print(f”状态码: {response.status_code}”)
print(f”响应体:\n{response.text}”)
except requests.exceptions.Timeout as e:
print(f”请求超时: {e}”)
except requests.exceptions.RequestException as e:
print(f”请求发生其他错误: {e}”)
“`
解释:
* timeout
参数用于设置请求超时时间。
* 可以是一个浮点数或整数,表示从发送请求到接收完整响应的最大总时间。
* 也可以是一个元组 (connect_timeout, read_timeout)
,分别设置连接建立的超时时间和从连接读取数据的超时时间。
* 超时时会抛出 requests.exceptions.Timeout
异常,通常需要在 try...except
块中捕获。
4.6 处理 SSL/TLS 证书问题 (Verify)
curl
:
bash
curl -k https://self-signed-example.com/ # 不安全
requests
:
“`python
import requests
url = “https://self-signed-example.com/”
默认情况下 verify=True (安全)
try:
response = requests.get(url)
print(f”状态码 (默认 verify=True): {response.status_code}”)
except requests.exceptions.SSLError as e:
print(f”SSL 证书验证失败 (默认 verify=True): {e}”)
print(“这可能是因为服务器使用了自签名证书或其他证书问题。”)
警告:设置为 False 会禁用证书验证,存在安全风险!
仅在你知道自己在做什么、信任目标网站,或者在特定测试/开发场景下临时使用。
try:
print(“\n尝试禁用 SSL 证书验证 (verify=False)…”)
response = requests.get(url, verify=False)
print(f”状态码 (verify=False): {response.status_code}”)
# 注意:禁用验证会触发 InsecureRequestWarning 警告,可以用以下代码过滤掉
# import urllib3
# urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
except requests.exceptions.RequestException as e:
print(f”请求发生错误 (verify=False): {e}”)
更安全的做法是指定信任的 CA 证书文件
response = requests.get(url, verify=’/path/to/your/certificate.crt’)
“`
解释:
* requests
默认 verify=True
,会验证 SSL/TLS 证书的有效性。这是保证通信安全的重要措施。
* 如果服务器证书无效(如自签名、过期、域名不匹配等),会抛出 requests.exceptions.SSLError
。
* 设置 verify=False
可以禁用证书验证,但会使你的连接容易受到中间人攻击,应非常谨慎使用,并尽量避免在生产环境中使用。
* 对于内部服务或特定场景,可以通过 verify='/path/to/ca_bundle.pem'
指定信任的 CA 证书文件。
4.7 使用代理 (Proxies)
curl
:
bash
curl -x http://proxy.example.com:8080 http://api.example.com/
curl -x socks5://user:pass@socks_proxy.example.com:9050 http://api.example.com/
requests
:
“`python
import requests
url = “http://api.example.com/”
proxies = {
“http”: “http://proxy.example.com:8080”,
“https”: “http://proxy.example.com:8080”, # HTTPS 流量也可以通过 HTTP 代理转发
# “https”: “https://secure_proxy.example.com:8443”, # 或者 HTTPS 流量通过 HTTPS 代理
# “socks5”: “socks5://user:pass@socks_proxy.example.com:9050” # SOCKS 代理
}
try:
response = requests.get(url, proxies=proxies)
print(f”状态码: {response.status_code}”)
print(f”响应体:\n{response.text}”)
except requests.exceptions.RequestException as e:
print(f”使用代理时发生错误: {e}”)
“`
解释:
* proxies
参数接受一个字典,键是协议 (http
, https
, socks4
, socks5
),值是代理服务器的地址。
* requests
支持 HTTP、HTTPS 以及 SOCKS 代理。
4.8 处理认证 (Authentication)
基本认证 (Basic Authentication)
curl
:
bash
curl -u user:password http://api.example.com/basic_auth
requests
:
“`python
import requests
url = “http://api.example.com/basic_auth”
方式一:使用 auth 参数 (推荐)
response = requests.get(url, auth=(‘user’, ‘password’))
print(f”状态码 (auth 参数): {response.status_code}”)
print(f”响应体:\n{response.text}”)
方式二:手动构建 Authorization 头部 (等效)
import base64
credentials = f”user:password”
encoded_credentials = base64.b64encode(credentials.encode()).decode()
headers = {“Authorization”: f”Basic {encoded_credentials}”}
response = requests.get(url, headers=headers)
print(f”状态码 (手动头部): {response.status_code}”)
print(f”响应体:\n{response.text}”)
“`
解释:
* requests
的 auth
参数可以接受一个元组 (username, password)
来处理 Basic Authentication。
* requests
会自动计算 Base64 编码并设置 Authorization: Basic ...
头部。
requests
还支持 Digest Authentication 以及通过提供可调用的 auth
参数实现自定义认证方案。对于更复杂的认证(如 OAuth),通常会结合其他库或手动管理头部。
4.9 文件上传 (File Uploads)
curl
:
bash
curl -X POST -F "fieldname=@/path/to/local/file.txt" http://api.example.com/upload
requests
:
“`python
import requests
url = “http://api.example.com/upload”
打开本地文件,注意使用二进制读取模式 ‘rb’
try:
with open(‘/path/to/local/file.txt’, ‘rb’) as f:
# files 参数接受一个字典,键是表单字段名,值可以是文件对象、(文件名, 文件对象)、
# (文件名, 文件对象, content_type) 或 (文件名, 文件对象, content_type, headers)
files = {‘fieldname’: f} # 或者 {‘fieldname’: (‘my_file.txt’, f)} 来指定文件名
response = requests.post(url, files=files)
print(f"状态码: {response.status_code}")
print(f"响应体:\n{response.text}")
except FileNotFoundError:
print(“错误: 文件 ‘/path/to/local/file.txt’ 未找到.”)
except requests.exceptions.RequestException as e:
print(f”文件上传时发生错误: {e}”)
“`
解释:
* files
参数接受一个字典。
* 字典的值通常是一个打开的文件对象。为了兼容性,特别是服务器可能依赖文件名时,推荐使用元组形式 ('filename_on_server', file_object, 'content_type')
。
* requests
会自动构建 multipart/form-data
请求体。
4.10 处理 Cookies 和会话 (Sessions)
curl
默认不保留 Cookie,需要使用 -c
和 -b
选项来写入和读取 Cookie 文件。
bash
curl -c cookies.txt http://api.example.com/set_cookie
curl -b cookies.txt http://api.example.com/use_cookie
requests
提供了更高级的会话管理机制,使用 requests.Session()
对象可以实现跨请求的 Cookie 持久化和其他会话级别的配置。
“`python
import requests
创建一个 Session 对象
session = requests.Session()
在同一个 session 中发送请求
第一个请求可能会设置 cookie
url_set_cookie = “http://api.example.com/set_cookie”
response1 = session.get(url_set_cookie)
print(f”第一次请求状态码: {response1.status_code}”)
print(f”Session 中的 Cookie: {session.cookies.get_dict()}”) # 可以查看 session 中保存的 cookie
第二个请求会自动带上 session 中保存的 cookie
url_use_cookie = “http://api.example.com/use_cookie”
response2 = session.get(url_use_cookie)
print(f”第二次请求状态码: {response2.status_code}”)
Session 对象还可以持久化其他配置,如 headers, auth, proxies 等
session.headers.update({‘x-test’: ‘true’}) # 所有后续请求都会带上这个头部
url_another_request = “http://api.example.com/another”
response3 = session.get(url_another_request) # 会带上 x-test 头部和之前设置的 cookie
print(f”第三次请求状态码: {response3.status_code}”)
print(f”第三次请求的头部 (发送的): {response3.request.headers}”) # 可以查看实际发送的头部
Session 使用完毕后通常不需要手动关闭,Python 的垃圾回收会处理
但对于更复杂的资源管理,可以考虑使用 with 语句
with requests.Session() as session:
# 在 session 环境中进行请求
response = session.get(…)
# session 在 with 块结束时会自动关闭
“`
解释:
* 创建一个 requests.Session
对象。
* 在同一个 Session
对象上调用 get()
, post()
等方法发送请求。
* Session
对象会自动在请求之间保持 Cookie。
* 此外,Session
对象还可以设置默认的请求头、认证方式、代理等,这些设置会应用于通过该 Session
对象发送的所有请求,非常适合与同一个 API 进行多次交互。
5. requests
的更多强大特性
除了与 curl
功能的对应,requests
还有许多其他便利的特性:
-
自动 JSON 解码: 对于返回 JSON 数据的 API,你可以直接使用
response.json()
方法将响应体解析为 Python 字典或列表,无需手动导入json
库并调用json.loads(response.text)
。“`python
import requestsurl = “http://api.example.com/data.json”
response = requests.get(url)if response.status_code == 200:
try:
data = response.json() # 直接解析 JSON 到 Python 对象
print(f”解析后的数据类型: {type(data)}”)
print(f”部分数据: {data.get(‘some_key’)}”)
except requests.exceptions.JSONDecodeError:
print(“响应体不是有效的 JSON 格式。”)
else:
print(f”请求失败,状态码: {response.status_code}”)
“` -
响应对象 (Response Object):
requests.get()
或requests.post()
等方法返回的Response
对象非常丰富,提供了访问请求和响应各个方面的属性和方法:response.status_code
: 状态码response.headers
: 响应头 (字典状对象)response.text
: 响应体 (字符串)response.content
: 响应体 (字节串),适合处理非文本数据(如图片、文件)response.json()
: 响应体解析为 JSONresponse.url
: 最终请求的 URL (考虑了重定向)response.request
: 发送的 Request 对象response.cookies
: 响应中的 Cookieresponse.history
: 重定向历史 (Response 对象列表)response.raise_for_status()
: 如果状态码表示客户端或服务器错误 (4xx 或 5xx),则抛出HTTPError
异常。这是一种便捷的错误检查方式。
-
错误处理的优雅:
requests
定义了一系列详细的异常类(如ConnectionError
,Timeout
,HTTPError
,SSLError
,RequestException
),使得你可以根据不同类型的错误进行精确处理。通常,捕获requests.exceptions.RequestException
可以处理请求过程中发生的几乎所有错误。“`python
import requestsurl = “http://this-url-does-not-exist.com/”
try:
response = requests.get(url, timeout=5)
response.raise_for_status() # 检查状态码,如果不是 2xx,抛出 HTTPError
print(“请求成功!”)
print(response.text)
except requests.exceptions.ConnectionError as e:
print(f”连接错误 (如域名无法解析,或服务器拒绝连接): {e}”)
except requests.exceptions.Timeout as e:
print(f”请求超时: {e}”)
except requests.exceptions.HTTPError as e:
print(f”HTTP 错误 (状态码 4xx 或 5xx): {e}”)
except requests.exceptions.RequestException as e:
print(f”发生未知错误: {e}”)“`
6. curl
vs requests
:何时选择谁?
-
选择
curl
:- 在命令行中快速测试 API 端点。
- 进行简单的、一次性的 HTTP 请求。
- 调试网络问题,查看详细的请求/响应信息(配合
-v
或--verbose
选项)。 - 在简单的 shell 脚本中执行基本操作。
-
选择
requests
:- 在 Python 应用程序中进行 API 调用。
- 需要处理复杂的请求逻辑(动态参数、条件判断)。
- 需要对响应数据进行编程处理(解析 JSON、XML、HTML)。
- 需要健壮的错误处理和重试机制。
- 需要进行批量的、自动化的请求。
- 需要管理会话状态(如登录后的多次操作)。
- 希望代码更具可读性、可维护性和可复用性。
总而言之,curl
是优秀的命令行工具,而 requests
是出色的 Python 库。它们服务于不同的目的和场景,但理解如何将 curl
的概念映射到 requests
中,对于熟悉 curl
的开发者来说,是学习和掌握 requests
的一个非常高效的途径。
7. 总结
从 curl
的命令行到 requests
的 Python 代码,是一个从即时交互工具到强大编程库的转变。requests
库凭借其简洁的 API、丰富的功能和友好的设计,极大地简化了 Python 中进行 HTTP 请求的复杂性。本文通过对比 curl
的常见用法,详细介绍了如何在 requests
中实现相同甚至更强大的功能,包括 GET/POST 请求、头部设置、参数传递、超时控制、证书处理、代理配置、基本认证、文件上传以及最重要的会话管理。
掌握 requests
库是 Python 开发者与外部世界进行数据交互的基础。无论是开发网络爬虫、构建 API 客户端、自动化运维任务,还是与其他服务集成,requests
都能成为你的得力助手。现在,放下你的 curl
脚本,拥抱 requests
带来的编程便利吧!深入阅读 requests
的官方文档,探索更多高级特性,你将发现更多可能性。