Python Requests 请求头的设置与验证
操作流程
显式发送请求头
配置受控请求,检查准备发送的请求头。
在接收端验证
对比回显结果和响应头,区分目标服务与代理凭据。
通过 headers= 传入字典,即可使用 Python Requests 发送请求头。用 response.request.headers 检查发出的请求,用 response.headers 检查收到的响应。这两个对象对应一次 HTTP 交互的不同方向。
请求头可以声明期望的响应格式、标识应用或向 API 提供鉴权信息。它们不会改变来源 IP、执行 JavaScript,也不能保证网站接受自动化请求。
显式设置并发送请求头
使用 python -m pip install requests 安装 Requests。启动随附的 network_demo.py,再运行下面的本地示例。演示端点会回显它收到的请求头。
import requests
headers = {
"User-Agent": "MiyaIP-Tutorial/1.0",
"Accept": "application/json",
}
with requests.Session() as session:
session.trust_env = False # Isolate this loopback demonstration.
response = session.get(
"http://127.0.0.1:8766/headers", headers=headers, timeout=(1, 3)
)
response.raise_for_status()
print("sent_user_agent:", response.request.headers.get("User-Agent"))
print("received_type:", response.headers.get("Content-Type"))
observed = response.json()["headers"]
print("server_saw:", observed.get("User-Agent"))本地输出中,发出的 User-Agent 为 MiyaIP-Tutorial/1.0,响应类型为 application/json,服务器观察到的 User-Agent 也是同一教程标识。请只对比用于诊断的非敏感字段,不要将已鉴权的生产请求的全部请求头打印出来。

根据接收服务选择请求头
请求头 | 表达的含义 | 常见误用 |
|---|---|---|
| 希望收到的响应格式 | 认为客户端请求 JSON 后,服务器就一定返回 JSON |
| 正在发送的请求体格式 | 实际发送表单编码内容,却声明为 JSON |
| 由客户端提供的应用标识 | 把看起来像浏览器的字符串视为完整浏览器环境 |
| 目标服务的鉴权凭据 | 填入代理密码,或将 Token 发给错误的主机 |
| 语言偏好 | 认为它会改变网络出口所在国家 |
HTTP 字段名不区分大小写,但使用约定俗成的拼写更便于阅读。字段值仍需遵循各自的语法。Requests 快速入门说明了自定义请求头及其优先级。
让请求体格式与 Content-Type 一致
发送 JSON 请求体时使用 json=。Requests 会序列化对象,并设置对应的 JSON Content-Type,除非你自行覆盖该请求头。以下本地示例仅发送演示数据:
import requests
with requests.Session() as session:
session.trust_env = False
session.headers.update({"User-Agent": "MiyaIP-Tutorial/1.0"})
response = session.post(
"http://127.0.0.1:8766/headers",
json={"demo": 1},
headers={"Accept": "application/json"},
timeout=(1, 3),
)
response.raise_for_status()
print(response.request.headers.get("Content-Type"))
print(response.json()["method"])该示例会输出 application/json 和 POST。相比之下,通过 data= 传入字典通常会创建表单请求体。只有确定 Content-Type 与实际发送内容一致时,才手动设置它。
Session 可以为多个请求设置通用请求头,单次请求中的值可以覆盖这些默认值。鉴权信息应限定于预期的目标服务;不要将密钥放入被广泛复用的 Session,再把它发往无关服务。
将目标请求头与代理凭据分开
连接 MIYAIP 代理时,请按连接指南通过 proxies 参数配置代理 URL 和凭据。不要把代理密码作为普通的 Authorization 请求头发给目标网站。MCP 密钥和 Crawler API Token 同样只属于各自的服务及其鉴权方式。
异常结果 | 检查内容 |
|---|---|
请求 JSON 却收到 HTML | 响应状态、实际 Content-Type、最终 URL,以及端点是否支持 JSON |
请求头值与字典不同 | Session 默认值、鉴权配置、准备后的请求及重定向 |
HTTP 401 | 目标服务的鉴权要求 |
HTTP 407 | 代理鉴权,需与目标请求头独立排查 |
HTTP 403 | 响应中的说明和允许的访问方式;单改 User-Agent 并不是通用修复方法 |
连接或 TLS 错误 | 先诊断连接,再调整内容协商请求头 |
发生重定向时,response.request 描述最终请求。若需检查更早的请求,请查看 response.history 中的记录。只检查诊断必需的字段,不要将 Cookie 和 Bearer Token 写入共享日志。
常见问题与下一步
常见问题
response.headers 是客户端发出的请求头吗?
不是。它包含的是响应头。请用 response.request.headers 检查关联的发出请求,或使用自己控制的回显端点查看接收服务器实际观察到的字段。
Accept-Language 会选择代理国家吗?
不会。它声明语言偏好。代理位置应通过产品支持的连接配置选择。
请求头可以让 Requests 执行 JavaScript 吗?
不可以。任务需要执行 JavaScript 或进行界面交互时,应选择浏览器工作流。
分别验证请求配置与网络路由
先确认响应符合预期。只有任务需要代理的路由或出口行为时,再配置代理。
下一步
运行本地演示
下载 network_demo.py,或将以下完整代码保存为同名文件。使用 Python 3 运行 python network_demo.py。服务仅监听 127.0.0.1:8766,使用完按 Ctrl+C 退出。
"""Local HTTP demonstration. No external requests, credentials or MIYAIP service calls."""
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import urlsplit
import json
import time
class DemoHandler(BaseHTTPRequestHandler):
def log_message(self, *_):
pass
def reply(self, code, body=b"", headers=None):
self.send_response(code)
for key, value in (headers or {}).items():
self.send_header(key, value)
self.send_header("Content-Length", str(len(body)))
try:
self.end_headers()
self.wfile.write(body)
except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError):
pass
def do_CONNECT(self):
self.reply(407, headers={"Proxy-Authenticate": 'Basic realm="local-demo"'})
def do_GET(self):
path = urlsplit(self.path).path
if path == "/redirect":
return self.reply(302, headers={"Location": "/headers"})
if path == "/loop":
return self.reply(302, headers={"Location": "/loop"})
if path == "/slow":
time.sleep(2)
if path in {"/headers", "/slow"}:
payload = {"method": self.command, "headers": dict(self.headers)}
return self.reply(200, json.dumps(payload, indent=2).encode(),
{"Content-Type": "application/json"})
self.reply(404, b"Use /headers, /redirect, /loop or /slow")
def do_POST(self):
body = self.rfile.read(int(self.headers.get("Content-Length", "0")))
if urlsplit(self.path).path == "/redirect307":
return self.reply(307, headers={"Location": "/headers"})
if urlsplit(self.path).path == "/redirect302":
return self.reply(302, headers={"Location": "/headers"})
payload = {"method": self.command, "headers": dict(self.headers),
"body": body.decode("utf-8", errors="replace")}
self.reply(200, json.dumps(payload, indent=2).encode(),
{"Content-Type": "application/json"})
if __name__ == "__main__":
print("Local demonstration: http://127.0.0.1:8766 (Ctrl+C to stop)", flush=True)
ThreadingHTTPServer(("127.0.0.1", 8766), DemoHandler).serve_forever()