Python调用接口:从Curl到Requests – wiki基地


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&param2=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-Typeapplication/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 库是更优的选择:

  1. 编程逻辑集成: 你需要在获取数据后进行复杂的处理、分析、存储或与其他数据源整合,这需要一个完整的编程环境。
  2. 自动化和批量处理: 你需要根据条件动态生成请求、循环调用 API、处理大量的请求和响应,这在 Python 中更容易实现。
  3. 错误处理和重试机制: requests 提供了更细粒度的错误处理方式(如捕获特定异常、检查状态码),方便实现健壮的重试逻辑。
  4. 数据格式处理: requests 能够自动处理 JSON、表单数据等格式的发送和接收,特别是响应的 JSON 解析,非常方便。
  5. 代码可读性和维护性: 将 API 调用逻辑写在 Python 代码中,比编写复杂的 shell 脚本调用 curl 更易读、易懂、易维护。
  6. 模块化和复用: 可以将 API 调用封装成函数或类,在程序的不同部分复用。
  7. 依赖管理和环境隔离: 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&param2=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}”)

“`

解释:
* requestsauth 参数可以接受一个元组 (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 requests

    url = “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(): 响应体解析为 JSON
    • response.url: 最终请求的 URL (考虑了重定向)
    • response.request: 发送的 Request 对象
    • response.cookies: 响应中的 Cookie
    • response.history: 重定向历史 (Response 对象列表)
    • response.raise_for_status(): 如果状态码表示客户端或服务器错误 (4xx 或 5xx),则抛出 HTTPError 异常。这是一种便捷的错误检查方式。
  • 错误处理的优雅: requests 定义了一系列详细的异常类(如 ConnectionError, Timeout, HTTPError, SSLError, RequestException),使得你可以根据不同类型的错误进行精确处理。通常,捕获 requests.exceptions.RequestException 可以处理请求过程中发生的几乎所有错误。

    “`python
    import requests

    url = “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 的官方文档,探索更多高级特性,你将发现更多可能性。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部