403 Forbidden主因是服务器识别出爬虫,非单纯IP封禁;需用Session模拟真实浏览器行为,完整配置User-Agent、Accept、Accept-Language、Sec-Fetch-*等请求头,并复用会话维持cookie与连接。

403 Forbidden 通常不是封 IP,而是服务器通过 User-Agent、请求头或行为模式识别出你是爬虫后主动拒绝——换 UA 或加 delay 往往不够,得模拟真实浏览器行为。
为什么 requests 默认请求会被 403?
多数反爬网站会检查 User-Agent 是否为空或过于简单(如 python-requests/2.28.1),也会验证 Accept、Accept-Language、Sec-Ch-Ua 等现代浏览器必带的 header。requests 默认只发基础字段,一眼就被识别。
- 直接用
requests.get(url)发请求,几乎必 403 - 只加一个
User-Agent仍可能失败,因为缺少配套 header 和 cookie 上下文 - 某些站点还会校验
Referer或要求 JS 渲染后的请求,纯 requests 无法满足
用 requests 模拟浏览器的最小可行配置
不用 Selenium,也能绕过大部分基础反爬。关键是凑齐一套“像人”的请求头,并复用 session 维持上下文。
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",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
"Accept-Encoding": "gzip, deflate",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
"Sec-Fetch-User": "?1",
}
session = requests.Session()
session.headers.update(headers)
response = session.get("https://example.com")
- 所有 header 字段都来自真实 Chrome 120 浏览器抓包,不是随意拼凑
- 必须用
Session(),否则每次请求都是新连接,丢失 cookie 和 connection 复用 - 不要硬编码 UA 字符串,可从
fake-useragent库动态获取,但注意该库已停更,建议自己维护几条主流 UA
遇到 403 还要查什么?
如果 header 和 session 都对了还 403,说明对方做了更细粒度检测,需进一步排查:
立即学习“Python免费学习笔记(深入)”;
- 检查响应 headers 中是否有
X-Robots-Tag或X-Frame-Options,它们暗示服务端有 WAF(如 Cloudflare)介入 - 用浏览器开发者工具 → Network → 刷一次页面,对比你代码发的请求和浏览器发的请求:差在哪?少哪个 header?有没有多发了某个参数?
- 有些站点会验证
Cookie是否含有效__cf_bm或cf_clearance,这种必须走 Selenium 或 playwright 解决 - 确认目标 URL 是否为前端路由(如
/product/123),实际数据走 AJAX 接口,而接口可能需要额外 token 或 sign 参数
真正难啃的 403 往往卡在细节:比如 header 顺序错一位、Sec-Ch-Ua 缺失、Accept-Encoding 不支持 br 压缩、甚至请求时间间隔太整齐被判定为脚本行为。别迷信“加个 sleep 就行”,先抓包,再对齐,最后调速。


















