开发者指南

Python Requests POST:JSON、表单与文件

使用 Python Requests 发送 JSON、表单和 multipart 文件,通过本地回显核对请求体,处理编码、响应解析及任务重复提交问题。

下载示例文件
Python Requests 的 json、data 和 files 参数分别对应 JSON、表单和 multipart 接收格式。

发送 Python Requests POST 时,先选择目标服务要求的请求体格式。需要将 Python 对象转为 JSON 时使用 json=,表单字段或明确的正文使用 data=,multipart 文件上传使用 files=。这些选择会影响发送的字节和 Content-Type。

本指南使用本地回显服务器,方便检查提交内容,无需将示例数据发送到外部服务。用 python3 demo_server.py 启动附带服务,再通过 python -m pip install requests 安装 Requests。

流程概览

  1. 1. 发送 JSON 对象

    json= 序列化对象并添加 JSON Content-Type,除非你显式覆盖。传入 Python 字典或列表,不要传入已经序列化的字符串,除非 API 明确要求一个 JSON 字符串值。

  2. 2. 发送表单字段

    字典会进行表单编码。回显服务将值表示为列表,以保留重复键。desk & lamp 中的 & 仍属于值,不会变为字段分隔符。

  3. 3. 通过 multipart 上传文件

    创建含虚构数据的小型 sample.csv,再使用:

  4. 4. 使用端点文档要求的请求体格式

    不要将 json= 与独立的 data= 或 files= 混用,并假定全部内容会合成一个 JSON 请求。应选择一种格式;文档明确支持的表单字段与文件 multipart 组合除外。

  5. 5. 分别检查请求和响应

    请求的 Content-Type 描述发送内容,响应的 Content-Type 描述返回内容。只在正文不含私密数据时检查实际内容;调试时避免记录凭据或个人记录。

  6. 6. 处理错误时避免重复执行操作

    超时不能证明 POST 在到达服务器之前就失败了。重试创建任务、支付等有副作用的操作前,遵循服务的幂等或查询机制。

1. 发送 JSON 对象

import requests

url = "http://127.0.0.1:8766/echo"
payload = {"category": "office", "limit": 3}
response = requests.post(url, json=payload, timeout=(3, 15))
response.raise_for_status()
print(response.json()["json"])
# {'category': 'office', 'limit': 3}

json= 序列化对象并添加 JSON Content-Type,除非你显式覆盖。传入 Python 字典或列表,不要传入已经序列化的字符串,除非 API 明确要求一个 JSON 字符串值。

仅设置 Content-Type: application/json 不会把 Python 字典变成 JSON 请求体,编码方式仍然重要。Requests 快速入门介绍了 JSON 和表单处理。

2. 发送表单字段

response = requests.post(
    "http://127.0.0.1:8766/echo",
    data={"category": "office", "query": "desk & lamp"},
    timeout=(3, 15),
)
response.raise_for_status()
print(response.json()["form"])
# {'category': ['office'], 'query': ['desk & lamp']}

字典会进行表单编码。回显服务将值表示为列表,以保留重复键。desk & lamp 中的 & 仍属于值,不会变为字段分隔符。

同名字段使用元组列表,例如 [('tag', 'office'), ('tag', 'lighting')]。普通字典无法表达两个拥有相同键的独立条目。

3. 通过 multipart 上传文件

创建含虚构数据的小型 sample.csv,再使用:

from pathlib import Path
import requests

Path("sample.csv").write_text("id,name\np1,Travel Mug\n", encoding="utf-8")
with open("sample.csv", "rb") as handle:
    response = requests.post(
        "http://127.0.0.1:8766/echo",
        data={"source": "tutorial"},
        files={"file": ("sample.csv", handle, "text/csv")},
        timeout=(3, 15),
    )
response.raise_for_status()
print(response.json()["parts"])

让 Requests 构造 multipart Content-Type 和 boundary。手动只设置 multipart/form-data 可能缺少服务器解析正文所需的边界。以二进制模式打开文件,并保持句柄到请求结束。

字段名 file 仅为示例,应符合目标 API 要求的字段名、文件名约束和上传大小限制。

4. 使用端点文档要求的请求体格式

目标要求

Requests 参数

常见错误

JSON 对象或数组

json=payload

将已编码的 JSON 字符串当作对象传入

URL 编码表单

data=fields

给表单正文添加 JSON Content-Type

精确文本或二进制正文

data=text_or_bytes

假设 Requests 会推断所需内容类型

multipart 字段和文件

data=fields, files=files

提供不完整的 multipart boundary 请求头

不要将 json= 与独立的 data= 或 files= 混用,并假定全部内容会合成一个 JSON 请求。应选择一种格式;文档明确支持的表单字段与文件 multipart 组合除外。

Python Requests 的 json、data 和 files 参数分别对应 JSON、表单和 multipart 接收格式。
本地接收端区分 JSON、URL 编码表单字段和 multipart 文件部分。

5. 分别检查请求和响应

print(response.request.method)
print(response.request.headers.get("Content-Type"))
print(response.status_code)
print(response.headers.get("Content-Type"))

请求的 Content-Type 描述发送内容,响应的 Content-Type 描述返回内容。只在正文不含私密数据时检查实际内容;调试时避免记录凭据或个人记录。

用 raise_for_status() 检查 HTTP 失败,再验证 API 自己的成功字段。有效 JSON 也可能表示业务错误;HTTP 成功也可能没有正文,不应对 204 响应无条件调用 response.json()。

6. 处理错误时避免重复执行操作

问题

优先检查

400 Bad Request

必填字段、类型、正文语法和服务错误信息

401 或 403

鉴权及操作权限

415 Unsupported Media Type

正文编码与 Content-Type

JSON 解码失败

响应状态、类型及是否存在正文

提交后超时

服务器是否已经接受或完成操作

超时不能证明 POST 在到达服务器之前就失败了。重试创建任务、支付等有副作用的操作前,遵循服务的幂等或查询机制。

MIYAIP Crawler API 的 POST 会创建异步任务。保存返回的任务 ID,在运行期间查询它,不要重复提交。任务指南规定了真实请求结构与鉴权方式。

常见问题

应该使用 data=json.dumps(payload) 吗?

这样会发送明确的正文,但不会自动添加 JSON Content-Type。常规对象转 JSON 使用 json=payload;需要精确编码时,有意识地同时设置正文和请求头。

timeout=(3, 15) 会把整个任务限制为 18 秒吗?

不会。它配置连接与读取等待,不是整个任务的总时限。重定向、重试和持续传输都可能增加实际耗时。

多次 POST 使用 Session 有帮助吗?

Session 可复用连接并保留 Cookie 等状态,但不能使非幂等 POST 安全重复,也不能验证响应的业务含义。

本地回显确认请求体格式后,使用目标服务文档规定的参数。对于 MIYAIP,遵循 Crawler API 接口定义,并按 ID 获取创建的任务。

将正确的请求格式用于采集任务