🐍 Python Day40:HTTP 协议 — 理解 Web 的基础
🕐 预计用时:2-3 小时 | 🎯 目标:掌握请求方法、状态码、Headers、Cookies、Session
📖 今日目录
- 请求方法(GET/POST/PUT/DELETE)
- 请求参数(Query String / Body)
1. HTTP 是什么?
HTTP(HyperText Transfer Protocol)是 Web 的基础协议。浏览器访问网页、App 调用 API,底层都是 HTTP。
# HTTP 通信过程
# 客户端(浏览器) 服务器
# | |
# |--- HTTP 请求 ----------->| "我要看 /index.html"
# | |
# |<-- HTTP 响应 ------------| "这是 HTML 内容"
# | |
# HTTP 的特点:
# 1. 无状态 — 服务器不记住你是谁(用 Cookie/Session 补救)
# 2. 请求-响应模式 — 客户端先发请求,服务器再响应
# 3. 基于 TCP — 可靠传输
2. 请求与响应的结构
HTTP 请求
# 一个完整的 HTTP 请求:
GET /api/users?page=1 HTTP/1.1 ← 请求行(方法 + 路径 + 版本)
Host: api.example.com ← 请求头(键: 值)
User-Agent: Mozilla/5.0
Accept: application/json
Authorization: Bearer xxx-token-xxx
Cookie: session_id=abc123
← 空行
← 请求体(GET 通常没有)
HTTP 响应
# 一个完整的 HTTP 响应:
HTTP/1.1 200 OK ← 状态行(版本 + 状态码 + 原因)
Content-Type: application/json ← 响应头
Content-Length: 256
Server: nginx/1.20
Set-Cookie: session_id=xyz789
← 空行
{"users": [{"name": "张三"}], "total": 1} ← 响应体
3. 请求方法
# RESTful API 示例
GET /api/users → 获取用户列表
GET /api/users/123 → 获取 ID=123 的用户
POST /api/users → 创建新用户(数据在请求体)
PUT /api/users/123 → 更新 ID=123 的用户
DELETE /api/users/123 → 删除 ID=123 的用户
4. 状态码
| | |
|---|
| | 100 Continue, 101 Switching Protocols |
| | 200 OK, 201 Created, 204 No Content |
| | 301 Moved Permanently, 302 Found, 304 Not Modified |
| | 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 405 Method Not Allowed |
| | 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable |
# 状态码速记
# 2xx → 成功("OK, 搞定了")
# 3xx → 重定向("去别处找")
# 4xx → 你的错("请求有问题")
# 5xx → 服务器的错("我挂了")
# 最常见的:
200 OK — 成功
201 Created — 创建成功
400 Bad Request — 请求格式错误
401 Unauthorized — 未登录
403 Forbidden — 无权限
404 Not Found — 资源不存在
500 Internal Server Error — 服务器内部错误
5. 请求头 Headers
| | |
|---|
| | Host: api.example.com |
| | Mozilla/5.0 ... |
| | application/json |
| | application/json |
| | Bearer xxx-token |
| | session_id=abc123 |
| | https://example.com/ |
| | no-cache |
# Content-Type 常见值
application/json — JSON 数据(最常用)
application/x-www-form-urlencoded — 表单提交(默认)
multipart/form-data — 文件上传
text/plain — 纯文本
text/html — HTML 页面
6. 请求参数
Query String(URL 参数)
# GET 请求的参数放在 URL 后面
# https://api.example.com/users?page=1&size=20&sort=name
# ^^^^^^^^^^^^^^^^^^^^^^^^
# Query String
from urllib.parse import urlencode, urlparse, parse_qs
# 构建 URL 参数
params = {"page": 1, "size": 20, "sort": "name"}
url = f"https://api.example.com/users?{urlencode(params)}"
print(url) # https://api.example.com/users?page=1&size=20&sort=name
# 解析 URL 参数
parsed = urlparse("https://api.example.com/users?page=1&size=20")
query = parse_qs(parsed.query)
print(query) # {'page': ['1'], 'size': ['20']}
请求体(Request Body)
# POST/PUT 请求的数据放在请求体中
# JSON 格式
POST /api/users HTTP/1.1
Content-Type: application/json
{"name": "张三", "age": 25}
# 表单格式
POST /login HTTP/1.1
Content-Type: application/x-www-form-urlencoded
username=admin&password=123456
7. Cookies 与 Session
# HTTP 是无状态的 — 每次请求都是独立的
# Cookies 和 Session 用来"记住"用户状态
# ---- Cookies ----
# 存储在客户端(浏览器),每次请求时自动发送
# 服务器设置 Cookie:
Set-Cookie: session_id=abc123; Path=/; HttpOnly
Set-Cookie: theme=dark; Path=/
# 客户端发送 Cookie:
Cookie: session_id=abc123; theme=dark
# ---- Session ----
# 存储在服务器端,客户端只保存 Session ID
# 1. 用户登录 → 服务器创建 Session,返回 Session ID(通过 Cookie)
# 2. 用户再次访问 → 浏览器发送 Cookie(含 Session ID)
# 3. 服务器根据 Session ID 找到对应的 Session 数据
# Cookie 属性
Set-Cookie: name=value;
Path=/; # Cookie 有效的路径
Domain=.example.com; # Cookie 有效的域名
Max-Age=3600; # 有效期(秒)
Secure; # 只通过 HTTPS 发送
HttpOnly; # JavaScript 无法访问(防 XSS)
SameSite=Strict; # 防 CSRF 攻击
💡 Cookie vs Session:
• Cookie:存在客户端,不安全,有大小限制(~4KB)
• Session:存在服务器,安全,无大小限制,但占服务器内存
• 实际项目:Session ID 通过 Cookie 传递,数据存在服务器
8. HTTPS 与安全
# HTTPS = HTTP + TLS/SSL(加密传输)
# HTTP: http://example.com → 明文传输,容易被窃听
# HTTPS: https://example.com → 加密传输,安全
# HTTPS 的工作原理:
# 1. 客户端连接服务器
# 2. 服务器返回 SSL 证书(含公钥)
# 3. 客户端验证证书,生成随机密钥
# 4. 用公钥加密随机密钥,发给服务器
# 5. 服务器用私钥解密,得到随机密钥
# 6. 双方用随机密钥加密通信(对称加密)
# Python 中:
# 普通 socket → 需要 ssl 包装
import ssl
context = ssl.create_default_context()
secure_socket = context.wrap_socket(socket.socket(), server_hostname="example.com")
9. 用 socket 发送 HTTP 请求
import socket
def http_get(host, path="/", port=80):
"""用原始 socket 发送 HTTP GET 请求"""
# 创建 TCP 连接
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
# 构建 HTTP 请求
request = f"GET {path} HTTP/1.1\r\n"
request += f"Host: {host}\r\n"
request += f"User-Agent: PythonSocket/1.0\r\n"
request += f"Accept: text/html\r\n"
request += f"Connection: close\r\n"
request += f"\r\n" # 空行表示请求头结束
# 发送请求
s.send(request.encode("utf-8"))
# 接收响应
response = b""
while True:
data = s.recv(4096)
if not data:
break
response += data
s.close()
return response.decode("utf-8")
# 使用
response = http_get("httpbin.org", "/get")
print(response[:500]) # 打印前 500 字符
# 解析 HTTP 响应
def parse_http_response(raw):
"""解析原始 HTTP 响应"""
# 分离头部和主体
header_part, body = raw.split("\r\n\r\n", 1)
# 解析状态行
lines = header_part.split("\r\n")
status_line = lines[0]
version, status_code, reason = status_line.split(" ", 2)
# 解析响应头
headers = {}
for line in lines[1:]:
key, value = line.split(": ", 1)
headers[key] = value
return {
"version": version,
"status_code": int(status_code),
"reason": reason,
"headers": headers,
"body": body
}
# 使用
response = http_get("httpbin.org", "/get")
parsed = parse_http_response(response)
print(f"状态码: {parsed['status_code']}")
print(f"Content-Type: {parsed['headers'].get('Content-Type')}")
print(f"Body: {parsed['body'][:200]}")
10. 用 http.client 发送请求
import http.client
import json
# ---- GET 请求 ----
conn = http.client.HTTPSConnection("httpbin.org")
conn.request("GET", "/get")
response = conn.getresponse()
print(f"状态码: {response.status}") # 200
print(f"原因: {response.reason}") # OK
print(f"Headers: {dict(response.getheaders())}")
data = json.loads(response.read())
print(f"Body: {data}")
conn.close()
# ---- POST 请求 ----
conn = http.client.HTTPSConnection("httpbin.org")
payload = json.dumps({"name": "张三", "age": 25})
headers = {"Content-Type": "application/json"}
conn.request("POST", "/post", body=payload, headers=headers)
response = conn.getresponse()
data = json.loads(response.read())
print(f"响应: {data}")
conn.close()
💡 http.client 是标准库,但实际项目中大家更常用 requests 库(明天学),它更简洁优雅。http.client 适合理解底层原理。
11. RESTful API 设计
# RESTful 响应格式
# 成功
{"status": "success", "data": {"id": 123, "name": "张三"}}
# 列表
{"status": "success", "data": [...], "total": 100, "page": 1}
# 错误
{"status": "error", "message": "用户不存在", "code": 404}
12. 今日小结
| |
|---|
| |
| |
| 2xx 成功,3xx 重定向,4xx 客户端错,5xx 服务器错 |
| Content-Type、Authorization、Cookie 等 |
| URL 参数:?key=value&key2=value2 |
| |
| |
| 服务器存储,通过 Cookie 中的 Session ID 关联 |
| |
| 用 HTTP 方法表示操作(GET/POST/PUT/DELETE) |
🎯 练习建议:
1. 用 socket 手写一个 HTTP 请求,访问 httpbin.org/get
2. 用 http.client 调用一个公开 API(如 GitHub API)
3. 设计一个博客系统的 RESTful API(列出所有接口)
📚 Day40 完成!明天学习 requests 库 — 优雅地发送 HTTP 请求