说实话,刚开始学网络爬虫或者后端接口调用的时候,我对HTTP请求这事儿挺懵的。以前总觉得浏览器点击个链接就是“发送请求”了,但背后到底发生了什么、怎么在Python里精准控制这个过程,真的是一头雾水。后来接触了requests库,才发现——这玩意儿简直是普通开发者写HTTP交互的救星。今天咱们不聊枯燥的理论定义,就实打实地从代码里把GET、POST、状态码判断和异常处理这块儿掰开揉碎了讲清楚。
一、先装好工具,别急着跑
在开始之前,确保你电脑上已经装好了requests。如果你用的是Python 3,大概率还没装,打开终端或命令行,敲这一句就够了:
pip install requests
装完之后,随便写个import requests都不报错,就算环境就位了。
小提醒:别用
urllib或者http.client这些标准库硬扛,除非你有特殊需求。requests对新手太友好,对老手也同样高效。
二、GET请求:最基础的“拉取”操作
GET请求是HTTP里最常见的一种,通常用来获取数据,比如抓取一个网页的HTML、请求某个API返回的JSON等。
2.1 最简单的GET请求
假设我们要访问百度首页,看看它返回了什么:
import requests
# 发送GET请求
response = requests.get('https://www.baidu.com')
# 查看状态码
print(f'状态码: {response.status_code}')
# 查看响应内容(默认是字符串形式)
print(response.text[:200]) # 只打印前200个字符,避免刷屏
运行结果大概是:
状态码: 200
<!DOCTYPE html>
<!--STATUS OK-->
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
<title>百度一下,你就知道</title>
...
这里状态码: 200是关键信号——200代表成功,说明服务器正常返回了数据。
2.2 带参数的GET请求
很多时候我们不是直接访问一个固定URL,而是要带查询参数。比如搜索“Python教程”:
import requests
# 方法一:直接把参数拼在URL里(不推荐,容易出错)
url = 'https://www.baidu.com/s?wd=Python教程'
response = requests.get(url)
# 方法二:用params参数(推荐,更安全、清晰)
params = {
'wd': 'Python教程',
'ie': 'utf-8'
}
response = requests.get('https://www.baidu.com/s', params=params)
print(response.url) # 查看最终请求的完整URL
print(response.status_code)
运行后你会看到:
https://www.baidu.com/s?wd=Python%E6%95%99%E7%A8%8B&ie=utf-8
200
注意看,Python教程被自动编码成了Python%E6%95%99%E7%A8%8B,这就是params参数的魅力——它帮你自动处理URL编码,省去了自己用urllib.parse.quote的麻烦。
2.3 设置请求头(模拟浏览器)
有些网站会检查User-Agent,如果你直接发请求,可能会被拒绝。这时候就要加请求头:
import requests
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
'AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/120.0.0.0 Safari/537.36'
}
response = requests.get('https://httpbin.org/get', headers=headers)
print(response.json()) # httpbin.org 会返回请求的详细信息
输出里你会看到headers字段,证实了你的请求头确实被发送了出去。
三、POST请求:向服务器“提交”数据
如果说GET是“问服务器要数据”,那POST就是“往服务器送数据”。常见场景包括:登录提交表单、上传文件、调用写入类API等。
3.1 最简单的POST请求
import requests
# 模拟登录一个测试接口
url = 'https://httpbin.org/post'
data = {
'username': 'zhangsan',
'password': '123456'
}
response = requests.post(url, data=data)
print(response.status_code)
print(response.json())
httpbin.org是一个专门用于测试HTTP请求的服务,它会把收到的请求原样返回。你可以看到返回的JSON里有一个form字段,里面就是你提交的数据。
3.2 提交JSON数据
现在很多API都要求发送JSON格式的数据,这时候要用json参数:
import requests
url = 'https://httpbin.org/post'
payload = {
'name': '李四',
'age': 28,
'city': '北京'
}
response = requests.post(url, json=payload)
print(response.json())
注意区分data和json:
data=data:发送的是表单数据(application/x-www-form-urlencoded)json=payload:发送的是JSON数据(application/json),并且requests会自动帮你序列化并设置正确的Content-Type头
3.3 上传文件
POST还能用来上传文件,比如图片、文档等:
import requests
url = 'https://httpbin.org/post'
files = {
'file': open('report.pdf', 'rb') # 注意用二进制模式打开
}
response = requests.post(url, files=files)
print(response.status_code)
files['file'].close() # 用完记得关闭文件
这里files参数接受一个字典,键是文件字段名,值是文件对象或元组(文件名、文件内容、MIME类型)。
四、状态码:读懂服务器的“脸色”
HTTP状态码是服务器对请求的回应,三位数字,分成五类:
| 范围 | 含义 | 常见状态码 |
|---|---|---|
| 1xx | 信息性 | 100 Continue |
| 2xx | 成功 | 200 OK, 201 Created |
| 3xx | 重定向 | 301 Moved Permanently, 302 Found |
| 4xx | 客户端错误 | 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found |
| 5xx | 服务端错误 | 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable |
4.1 如何判断状态码
import requests
response = requests.get('https://httpbin.org/status/404')
# 方法一:直接判断
if response.status_code == 404:
print('页面不存在')
# 方法二:使用raise_for_status()(推荐,见下文异常处理)
4.2 常见坑点:301/302重定向
有时候你访问一个URL,服务器返回301或302,告诉你“去新地址”。requests默认会自动跟随重定向,但你可以选择关闭:
# 禁止自动重定向
response = requests.get('https://httpbin.org/redirect/1', allow_redirects=False)
print(response.status_code) # 输出302
print(response.headers['Location']) # 重定向的目标URL
五、异常处理:别让程序因为一个请求就崩掉
这是新手最容易忽略的部分。网络请求永远不是100%可靠的,可能遇到超时、连接失败、DNS解析错误、服务器返回500等等。如果不处理异常,你的程序会直接报错退出。
5.1 requests的主要异常类型
| 异常 | 触发场景 |
|---|---|
requests.exceptions.ConnectionError |
网络连接失败(DNS错误、拒绝连接等) |
requests.exceptions.HTTPError |
HTTP状态码表示错误(需配合raise_for_status()) |
requests.exceptions.Timeout |
请求超时 |
requests.exceptions.TooManyRedirects |
重定向次数过多 |
requests.exceptions.RequestException |
所有异常的基类 |
5.2 完整的异常处理模板
import requests
from requests.exceptions import ConnectionError, Timeout, HTTPError, TooManyRedirects
def safe_get(url, timeout=10):
try:
response = requests.get(url, timeout=timeout)
# 如果状态码 >= 400,会抛出HTTPError
response.raise_for_status()
return response
except ConnectionError:
print(f'❌ 网络连接失败: {url}')
except Timeout:
print(f'⏱️ 请求超时: {url}')
except HTTPError as e:
print(f'🚫 HTTP错误: {e}')
print(f' 状态码: {e.response.status_code}')
except TooManyRedirects:
print(f'🔄 重定向过多: {url}')
except Exception as e:
print(f'💥 未知错误: {type(e).__name__}: {e}')
return None
# 使用示例
resp = safe_get('https://httpbin.org/status/500')
if resp:
print('请求成功,内容:', resp.text[:100])
输出可能是:
🚫 HTTP错误: 500 Internal Server Error
状态码: 500
5.3 设置超时:一定要设!
很多新手写代码忘了设timeout,结果程序卡在那里几十秒甚至几分钟。默认情况下requests不会超时,这非常危险。
# 推荐:同时设置connect timeout和read timeout
response = requests.get('https://example.com', timeout=(5, 10))
# 第一个5是连接超时(秒),第二个10是读取超时(秒)
六、实战案例:抓取某网站数据并处理各种情况
假设我们要从一个模拟API获取用户列表,并处理各种可能出现的问题:
import requests
import json
API_URL = 'https://jsonplaceholder.typicode.com/users'
def fetch_users():
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
try:
# 发送请求,带超时和请求头
response = requests.get(API_URL, headers=headers, timeout=10)
# 检查状态码
response.raise_for_status() # 4xx或5xx会抛出HTTPError
# 解析JSON
users = response.json()
# 输出前3个用户
for user in users[:3]:
print(f"姓名: {user['name']}, 邮箱: {user['email']}")
return users
except requests.exceptions.ConnectionError:
print('错误:无法连接到服务器,请检查网络')
except requests.exceptions.Timeout:
print('错误:请求超时,请稍后重试')
except requests.exceptions.HTTPError as e:
print(f'错误:HTTP错误 {e.response.status_code}')
except json.JSONDecodeError:
print('错误:服务器返回的数据不是有效的JSON')
except Exception as e:
print(f'未知错误:{e}')
return []
# 执行
if __name__ == '__main__':
users = fetch_users()
print(f'\n共获取到 {len(users)} 个用户')
运行结果:
姓名: Leanne Graham, 邮箱: Sincere@april.biz
姓名: Ervin Howell, 邮箱: Shanna@melissa.tv
姓名: Clementine Bauch, 邮箱: Nathan@yesenia.net
共获取到 10 个用户
七、几个实用小技巧
7.1 Session对象:保持会话
如果你需要多次请求同一个网站(比如需要先登录再抓取数据),用Session可以自动保留cookie:
import requests
session = requests.Session()
# 第一步:登录
login_data = {'username': 'admin', 'password': 'secret'}
session.post('https://example.com/login', data=login_data)
# 第二步:访问需要登录的页面(cookie已保留)
response = session.get('https://example.com/dashboard')
print(response.status_code)
7.2 代理设置
如果目标网站有IP限制,可以用代理:
proxies = {
'http': 'http://127.0.0.1:7890',
'https': 'https://127.0.0.1:7890'
}
response = requests.get('https://example.com', proxies=proxies, timeout=10)
7.3 重试机制
对于不稳定的接口,可以加重试逻辑:
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = requests.Session()
retry = Retry(
total=3, # 最多重试3次
backoff_factor=1, # 重试间隔:1s, 2s, 4s
status_forcelist=[500, 502, 503, 504] # 这些状态码才重试
)
adapter = HTTPAdapter(max_retries=retry)
session.mount('http://', adapter)
session.mount('https://', adapter)
response = session.get('https://example.com/api')
八、总结:记住这几点就够了
- GET用来获取,POST用来提交,别混用。
- 永远设置
timeout,不然程序可能卡死。 - 用
params传GET参数,用json或data传POST数据,让代码更清晰。 - 状态码200≠一定成功,还要看业务逻辑;但4xx/5xx一定有问题。
- 异常处理不能省,用
try-except包裹网络请求,别让你的程序轻易崩溃。 - 需要保持登录状态用
Session,它会自动管理cookie。
HTTP请求这事儿,乍一看挺复杂,但掌握了requests之后,你会发现它其实就那几招。多写多练,遇到具体问题再查文档,慢慢就熟练了。下次再有人问你“怎么发HTTP请求”,你可以自信地说:用requests,三行代码搞定。
