返回博客

Python Requests请求头教程指南:GET、POST与User-Agent

Marcus Bennett

2026年7月22日 · 教程 · 18 分钟阅读

在Python Requests中,请求头以字典形式传给 requests.get()requests.post() 等方法的 headers 参数即可。若多个请求需要复用同一组值,可以通过 Session.headers.update() 配置到会话中。本文会从一个简单的GET请求讲起,依次说明POST请求头、自定义User-Agent、Session默认请求头、请求头检查方式,以及403问题的排查思路。

文中的示例面向调用JSON API,或在获得许可后采集网页数据的开发者。读完后,你可以分别测试单次请求和Session级别的请求头,复现一个可控的403响应,并判断修改请求头是否真的与请求失败有关。

快速结论

要为单次请求添加请求头,把字典传给 headers 即可,例如:requests.get(url, headers={"Accept": "application/json"})。当接口要求客户端表明身份时,可在该字典中加入 "User-Agent"。需要长期复用的默认值应放在 session.headers.update(...) 中。
要核对实际结果,请查看 response.request.headers,它表示发出的请求头;response.headers 则是服务器返回的响应头。

Python Requests中的请求头是什么?

HTTP请求头是随请求或响应一起传输的键值对,用来承载不适合放进消息正文的信息。在Python Requests中,发出的请求头通常以字典形式,通过 headers= 传给 requests.get()requests.post() 等方法;服务器返回的响应头可通过 response.headers 读取。

为什么要使用Python Requests请求头?

请求头能帮助服务器识别:客户端希望获得JSON还是HTML、请求体采用何种编码、请求携带了哪些凭据,以及请求来自哪个应用。常见字段包括 AcceptContent-TypeAuthorizationUser-Agent。填写准确的值不仅能让服务器正确理解请求,也更便于定位API调用失败的原因。

单次请求使用 headers=;多个请求需要共享默认值时,使用 requests.Session。如果想确认Requests最终准备并发出的内容,请检查 response.request.headers。在配置GET、User-Agent、POST和Session请求头前,先搭建一个可重复使用的本地测试环境。

准备Python Requests测试环境

安装依赖

创建虚拟环境,并在其中安装固定版本的依赖:

python3 -m venv .venv
source .venv/bin/activate
python -m pip install requests==2.34.2 PySocks==1.7.1

PySocks 只在后文可选的SOCKS代理示例中需要;基础请求头示例只依赖Requests。

组件 已验证版本 用途
Python 3.14.0 运行示例
Requests 2.34.2 发送和检查HTTP请求
PySocks 1.7.1 支持 socks5h:// 代理URL
测试目标 本地HTTP回显服务器 无需依赖第三方服务,也能得到可重复的测试结果

这个演示会在 127.0.0.1 启动一台服务器,接收每个请求并输出提交的值;测试结束后可直接在本机关闭。整个过程不会采集外部网站的数据。
python requests 请求头文章图1

图1:检查Python和Requests版本,然后准备示例环境。

启动本地测试服务器

下面所有示例都会访问你自己电脑上的可用URL。请将以下代码保存为 code/local_echo_server.py

import json
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer


class Handler(BaseHTTPRequestHandler):
    def reply(self, status=200):
        length = int(self.headers.get("Content-Length", "0"))
        raw_body = self.rfile.read(length) if length else b""
        parsed_json = None
        body_text = None
        if raw_body:
            body_text = raw_body.decode("utf-8", errors="replace")
            try:
                parsed_json = json.loads(raw_body)
            except json.JSONDecodeError:
                pass
        payload = {
            "method": self.command,
            "path": self.path,
            "headers": dict(self.headers),
            "json": parsed_json,
            "body_text": body_text,
        }
        body = json.dumps(payload, indent=2).encode("utf-8")
        self.send_response(status)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)

    def do_GET(self):
        self.reply(403 if self.path == "/blocked" else 200)

    def do_POST(self):
        self.reply()

    def log_message(self, format, *args):
        return


server = ThreadingHTTPServer(("127.0.0.1", 8765), Handler)
print("Local test server: http://127.0.0.1:8765", flush=True)
print("Press Control-C to stop.", flush=True)
try:
    server.serve_forever()
except KeyboardInterrupt:
    pass
finally:
    server.server_close()

这个回显服务器会在请求体是合法JSON时返回解析后的JSON;如果请求体不是JSON,则将其以文本形式保留。因此,你既可以用它测试 json=,也可以测试 data=

在第一个终端窗口运行下面的命令,并保持窗口开启:

python3 code/local_echo_server.py

python requests 请求头文章图2

图2:本地服务器正在监听端口8765,已可接收客户端示例请求。

如何配置Python Requests请求头

创建请求头字典

HTTP请求头会在请求体之前传递元数据。它可以说明客户端可接收的数据格式、正在发送的请求体媒体类型、认证信息、缓存偏好,或发起请求的软件信息。

Requests通常使用普通字典表示这些值:

import requests

url = "http://127.0.0.1:8765/headers"
headers = {
    "User-Agent": "inventory-client/2.1",
    "Accept": "application/json",
}
response = requests.get(url, headers=headers, timeout=(2, 5))
response.raise_for_status()
print(response.status_code)

HTTP请求头名称不区分大小写,但统一采用首字母大写的写法,代码会更易读。请求头的值应为字符串。不要手动设置 Content-Length,Requests会根据编码后的请求体自动计算它。

还要注意,有些专用参数的优先级会高于 headers 中的同名值。例如 auth= 可能替换 Authorization;代理URL中携带的凭据也可能替换 Proxy-Authorization

为GET请求添加请求头

以下是为单次Python Requests GET请求添加请求头的最小可运行示例:

import requests

url = "http://127.0.0.1:8765/headers"
headers = {
    "User-Agent": "inventory-client/2.1 (+https://rola-ip.co/)",
    "Accept": "application/json",
    "X-Trace-ID": "items-read-001",
}
response = requests.get(url, headers=headers, timeout=(2, 5))
response.raise_for_status()
received = response.json()
print("Status:", response.status_code)
print("Method:", received["method"])
print("User-Agent:", received["headers"]["User-Agent"])
print("X-Trace-ID:", received["headers"]["X-Trace-ID"])

这里的 Accept 只是告诉服务器:客户端希望获得JSON;它不能强制服务器一定返回JSON。调用 response.json() 前,应先确认请求成功。对于响应格式不稳定的接口,还应确认返回的 Content-Type 符合预期。

每次请求都应设置 timeout。元组中的第一个数值限制Requests建立连接时的等待时间;第二个数值限制它在两段响应数据之间最多等待多久。请注意,这一设置并不保证整个下载一定会在两个数值相加的秒数内完成。

python requests 请求头文章图3

图3:本地回显服务器收到了单次请求的请求头,并返回HTTP 200。

在Python Requests中设置User-Agent

Requests默认会使用库自身的 User-Agent,例如 python-requests/2.34.2。当接口要求客户端标识自身时,建议为应用设置清晰、可识别的值:

import requests

url = "http://127.0.0.1:8765/headers"
response = requests.get(
    url,
    headers={"User-Agent": "inventory-client/2.1 (+https://rola-ip.co/)"},
    timeout=(2, 5),
)
response.raise_for_status()
print(response.json()["headers"]["User-Agent"])

对API运营方而言,包含产品名称、版本号以及联系或说明页面的User-Agent,通常比从其他客户端照搬的浏览器字符串更有用。若服务方规定了User-Agent格式,应按其要求填写。

修改Python Requests的User-Agent,并不会让客户端变成Chrome。Requests依旧不会执行JavaScript、复现浏览器的TLS行为、管理浏览器客户端提示(client hints),也不会自动完成认证挑战。照搬浏览器的值甚至可能与请求的其他部分相互矛盾。因此,应把User-Agent视为客户端标识,而不是解决403的通用办法。

可选:通过SOCKS5代理发送获授权请求

安装 PySocks 后,Requests可以使用 socks5h:// 代理URL。将代理地址和凭据保存到环境变量,而不是写入代码;socks5h 会让域名解析在代理一侧进行。以下示例仅适用于已获目标服务许可的API调用或数据访问任务:

import os

import requests

proxy_url = os.environ["ROLA_SOCKS5_PROXY"]
response = requests.get(
    "https://api.example.com/v1/items",
    headers={
        "User-Agent": "inventory-client/2.1 (+https://rola-ip.co/)",
        "Accept": "application/json",
    },
    proxies={"http": proxy_url, "https": proxy_url},
    timeout=(2, 5),
)
response.raise_for_status()
print(response.json())

例如,可在受控的本地环境中将 ROLA_SOCKS5_PROXY 配置为 socks5h://username:password@proxy.example:1080。代理只决定网络路径,不能授予访问权限,也不能替代接口认证、访问政策或速率限制;发送前应确认目标服务允许该工作流。

正确发送Python Requests POST请求头

添加POST请求头和JSON请求体

调用JSON API时,请通过 json= 传入Python对象:

import requests

url = "http://127.0.0.1:8765/items"
headers = {
    "User-Agent": "inventory-client/2.1 (+https://rola-ip.co/)",
    "Accept": "application/json",
    "X-Trace-ID": "item-create-001",
}
payload = {"name": "monitor", "quantity": 2}
response = requests.post(
    url,
    headers=headers,
    json=payload,
    timeout=(2, 5),
)
response.raise_for_status()
received = response.json()
print("Status:", response.status_code)
print("Method:", received["method"])
print("User-Agent:", received["headers"]["User-Agent"])
print("Content-Type:", received["headers"]["Content-Type"])
print("JSON received:", received["json"])

使用 json=payload 时,Requests会序列化该对象,并添加恰当的JSON Content-Type。通常不要先用 json.dumps() 把对象转成字符串,再将该字符串传给 json=;那样编码的是一个JSON字符串,而不是原本的对象结构。

如果接口需要表单字段,或需要已编码的请求体,应使用 data=。正确的编码方式取决于接口契约,而不是单纯取决于HTTP方法。

服务器期望的数据 Requests参数 常见的自动生成 Content-Type
JSON对象 json={"key": "value"} application/json
HTML表单字段 data={"key": "value"} application/x-www-form-urlencoded
原始字节或文本 data=encoded_body 按API契约设置
文件上传 files={...} multipart/form-data; boundary=...

python requests 请求头文章图4

图4:测试POST请求正确传递了User-Agent、追踪请求头、Content-Type和JSON对象,并且没有报错。

复用并检查Python Requests请求头

通过Session复用请求头

requests.Session 会在多次调用之间保存默认请求头和Cookie,并能复用到同一主机的连接。因此,它很适合小型API客户端,或经过授权的多页面采集任务:

import requests

with requests.Session() as session:
    session.headers.update(
        {
            "User-Agent": "inventory-client/2.1",
            "Accept": "application/json",
        }
    )
    response = session.get(
        "http://127.0.0.1:8765/headers",
        headers={"X-Trace-ID": "session-001"},
        timeout=(2, 5),
    )
    response.raise_for_status()
    received = response.json()
    print("Status:", response.status_code)
    print("User-Agent:", received["headers"]["User-Agent"])
    print("X-Trace-ID:", received["headers"]["X-Trace-ID"])

Requests会将单次调用的请求头字典与Session默认请求头合并。同一个键同时出现时,该次请求中传入的值会覆盖Session默认值。不要在并发线程间随意共享可变的Session,因为Cookie等状态会变得难以预测;应让一个Session只由一个工作单元使用,或做好并发访问保护。

python requests 请求头文章图5

图5:Session级别的User-Agent与请求级别的X-Trace-ID合并后,一同出现在请求中。

检查请求头与响应头

不要默认认为原始字典会原样发出,而应检查已准备好的请求:

import requests

response = requests.get(
    "http://127.0.0.1:8765/headers",
    headers={"User-Agent": "inspection-client/1.0"},
    timeout=(2, 5),
)
response.raise_for_status()
print("Method:", response.request.method)
print("URL:", response.request.url)
print("User-Agent sent:", response.request.headers["User-Agent"])
print("Response Content-Type:", response.headers["Content-Type"])

response.headersresponse.request.headers 的含义不同:

表达式 包含的内容
response.request.headers 客户端实际发出的请求头
response.headers 服务器返回的响应头
session.headers Session中配置的默认值
response.history 重定向过程中产生的早期响应

生产环境中不要不加筛选地打印所有请求头。AuthorizationCookieProxy-Authorization、API密钥和自定义令牌都可能泄漏到日志。只输出安全字段,或先对敏感值进行脱敏。

python requests 请求头文章图6

图6:可运行的检查脚本区分了Requests发出的请求头与服务器返回的响应头。

理解请求头的优先级

Requests会根据多个输入构建最终请求。以下三条规则可以解释许多看似反常的结果:

  • 对于Requests支持的认证机制,优先使用 auth=,而不是手动拼接 Authorization。从 .netrc 读取的凭据,或通过 auth= 提供的凭据,都可能覆盖 headers 中的Authorization值。
  • 代理凭据应放在配置好的代理URL中,或按照代理服务商文档提供的认证流程配置。Requests可能会替换手动填写的 Proxy-Authorization
  • 让Requests自行计算 Content-Length。如果它能知道请求体长度,手写的值可能会被替换。

当重定向跳转到另一台主机时,Requests还会移除Authorization。这是安全措施,并不说明原始字典被忽略了。排查重定向问题时,应结合 response.history 和已准备好的请求头一起查看。

排查Python Requests请求头故障

修改请求头前,先诊断状态码

先检查状态码、响应正文、最终URL、重定向历史,以及已准备请求中安全的那部分请求头。给每个失败请求都盲目添加更多浏览器风格字段,往往只会掩盖真正的问题,而不能解决它。

现象 可能原因 如何验证 实用处理方法
400 Bad Request 语法、请求头值或请求体结构无效 阅读API错误正文,并与接口schema对照 修正指定字段或编码方式
401 Unauthorized 凭据缺失、过期或被覆盖 检查认证方案和可安全查看的令牌元数据 刷新凭据;支持时使用 auth=
403 Forbidden 权限、策略、限速、IP信誉或反自动化控制 对照账号权限、文档限制、响应正文及允许使用的网络 申请权限、降低频率,或使用获准的网络路径
406 Not Acceptable Accept 值不被支持 查看支持的响应格式 发送文档规定的媒体类型
415 Unsupported Media Type 请求体编码与 Content-Type 不一致 同时检查已准备的请求体和请求头 正确使用 json=data=files=
429 Too Many Requests 超出速率限制 检查 Retry-After 和服务商配额请求头 加入随机抖动后退避,并降低并发
连接/读取超时 网络、代理或上游服务过慢 分别测试DNS、直连和代理路径 设置有上限的超时,只重试安全操作

遇到403时,先确认URL和认证信息,再查看网站的访问政策并降低请求频率。自定义User-Agent可能满足某些接口的客户端标识要求,但它不能赋予访问权限。在不了解来源的情况下加入 Referer、复制Cookie,或添加浏览器专用的 Sec-CH-UA-* 字段,反而可能让请求前后不一致。

重试同样需要谨慎。重复GET通常是安全的;自动重复POST则可能创建重复记录,除非API支持幂等键(idempotency key)。存在 Retry-After 时应遵从它,并为指数退避设置上限。

下面的脚本会复现一个已知403,但不会过早调用 raise_for_status(),从而保留已准备的请求,方便进一步诊断:

import requests

response = requests.get(
    "http://127.0.0.1:8765/blocked",
    timeout=(2, 5),
)
print("Status:", response.status_code)
print("URL:", response.request.url)
print("User-Agent sent:", response.request.headers["User-Agent"])
if response.status_code == 403:
    print("Diagnosis: controlled 403 reproduced; inspect policy and permissions.")

python requests 请求头文章图7

图7:示例有意复现403,再检查已准备请求中的User-Agent,然后决定下一步应调整什么。

使用Rola IP处理网络路由与IP限制

如果已获授权的请求在一个网络中可用,却从共享服务器IP反复失败,限制因素可能是IP信誉、地理位置或单IP限速规则,而不是Python Requests的请求头字典。经过认证的代理可以独立处理网络路径,而应用请求头仍负责描述请求本身。

Rola IP提供住宅代理动态数据中心代理和移动代理网络。数据中心代理适合需要稳定性和速度的API调用及批量任务;住宅代理通过家庭网络出口转发;移动代理则适合确实需要移动网络出口的工作流。选择何种类型,应依据目标服务的访问规则、所需位置和工作负载规模决定。

在Rola IP控制台中打开Proxy Setup,然后选择地区、认证方式、协议和账户。页面会提供连接所需的主机、端口、用户名与密码。请把这些值保存在环境变量或密钥管理器中,不要把有效凭据直接写入源代码。控制台配置流程可参考官方的代理连接信息快速入门

python requests 请求头文章图8

图8:在Rola IP的Proxy Setup页面中查找代理主机、端口和认证信息。

当多次请求必须保持同一个出口IP时,选择粘性会话;当独立请求应通过不同出口发出时,使用按请求轮换。位置参数用于将获准流量路由到工作流所需的国家或地区。

需要明确区分两个层面:请求头描述的是应用层请求,代理改变的是网络路径。新的IP无法修正无效认证或错误的 Content-Type;同样,换一个User-Agent也无法解决IP信誉或地理位置限制。

代理路由不能替代授权,也不能免除与robots指令、合同、隐私和速率限制相关的义务。它同样不能渲染JavaScript。只有当页面确实需要浏览器,且该工作流已获允许时,才应使用浏览器自动化。

Python Requests请求头最佳实践

  • 当服务要求客户端表明身份时,使用具体且真实的User-Agent。
  • Accept 和请求体编码与API文档保持一致。
  • 发送JSON时优先使用 json=,不要手动拼接请求体和长度。
  • 同时设置连接超时和读取超时。
  • 在保留所需诊断响应后,再调用 raise_for_status()
  • 调试时检查 response.request.headers,并对敏感信息脱敏。
  • 对相关的顺序请求复用同一个Session。
  • 只重试适合重试的方法,限制退避时长,并遵从 Retry-After
  • 将令牌和代理凭据保存在环境变量或密钥管理器中。
  • 遵守访问条款、隐私规则、适用的robots指令和公开的速率限制。

总结

单次请求使用 headers 字典,多个请求共享默认值时使用Session。让Requests负责JSON编码和与请求体相关的请求头计算;遇到意外结果时,检查已准备的请求。

如果经许可的工作流受网络信誉或地理位置限制,而非请求头限制,经过认证的Rola IP路由可以处理这一独立层面。同时仍应设置合理超时、控制请求频率、妥善保管凭据,并遵循目标服务的访问规则。

常见问题

准备开始大规模采集数据了吗?

免费试用