requests默认返回403因User-Agent为python-requests/2.xx,服务器直接识别为爬虫;需构造含User-Agent、Accept、Accept-Language等真实浏览器头,并避免静态复用或依赖fake-useragent。

为什么 requests 默认请求会被返回 403
网站看到 requests 发出的请求,第一眼就认出是爬虫——因为默认 User-Agent 是 python-requests/2.xx,连浏览器影子都没有。服务器直接拦截,不给任何机会。
这不是你代码写错了,而是对方压根没打算让你进。真实浏览器访问时带一堆头信息,比如 Accept、Accept-Language、Sec-Ch-Ua(新版 Chrome)、Referer,甚至 cookie 和 TLS 指纹。光换 User-Agent 不够,得像真人一样“呼吸”。
- 只改
User-Agent→ 大概率还是 403(尤其对 Cloudflare、Akamai 等防护强的站点) - 没带
Accept或Accept-Language→ 部分站点会拒收 - 用
requests.get(url, headers={...})手动拼 header → 容易漏字段、大小写错(如user-agent不生效,必须是User-Agent) - 重复使用同一套 header 访问多个页面 → 可能被识别为脚本行为(缺少 referer 跳转链、时间间隔太短)
怎么构造一套靠谱的请求头
别自己凭空编,从浏览器开发者工具里「复制为 curl」最稳。打开 Network 面板,刷一下目标页面,点一个 xhr 或 doc 请求,右键 → Copy → Copy as cURL,再用在线工具(比如 curlconverter.com)转成 Python 代码。
转出来的 headers 字典可以直接用,但要注意两点:一是删掉 Cookie(除非你真需要登录态),二是把 Sec-Ch-Ua 这类带双引号的值保留原样(Python 字符串里要处理好引号嵌套)。
- 推荐保留的最小集:
User-Agent、Accept、Accept-Language、Accept-Encoding、Connection、Upgrade-Insecure-Requests -
Sec-Ch-Ua和Sec-Ch-Ua-Mobile在较新 Chrome 中必带,缺了可能触发挑战页 - 避免用静态 header 字典全局复用;每次请求前可微调
User-Agent版本号或加个随机Referer,降低指纹一致性
requests + fake-useragent 会踩什么坑
fake-useragent 库听起来很美,实际在生产环境容易翻车。它默认从 external API(如 useragentstring.com)拉数据,而这个 API 经常 403、超时、返回空,导致你的爬虫启动就挂。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
更麻烦的是,它生成的 UA 字符串未必配套其他 header,比如给了个 Edge 的 User-Agent,但 Sec-Ch-Ua 还是 Chrome 的,服务器一比对就露馅。
- 本地缓存没开 → 启动时频繁请求外部源,不稳定
- 没设 fallback →
ua.random报AttributeError或返回None - UA 字符串含非法字符(如换行、不可见空格)→ requests 报
InvalidHeader - 建议做法:自己维护一个精简 UA 池(5–10 条主流浏览器最新 UA),配合随机选 + 固定 header 结构,可控性强得多
403 还是解不开?先确认是不是真卡在 header
别一上来就猛调 header,先排除其他常见原因。很多 403 其实和 header 无关,比如:
- 目标 URL 是前端路由(如
/product/123),但真实接口是/api/v1/product?id=123→ 你请求的是 HTML 页面,但服务器只允许 Ajax 请求(检查X-Requested-With: XMLHttpRequest) - 网站开了 Referer 白名单 → 缺
Referer或值不对(比如写成https://example.com却少了个/) - IP 被限频或封禁 → 换代理或等一会再试,header 再像也没用
- 需要执行 JS 渲染(比如反爬 token 动态生成)→ requests 拿不到完整内容,得上
playwright或selenium
最简单的验证方式:用 curl 命令复现,把浏览器 copy 出来的完整 curl 粘贴执行,如果成功,说明问题就在你的 Python 请求构造上;如果也 403,那大概率是 IP 或账号态问题。
header 伪装只是基础门槛,不是银弹。真正难的,是让整个请求链路(DNS 解析、TCP 握手、TLS 指纹、HTTP/2 流控、JS 行为模拟)都像真人——而 requests 在这层已经到头了。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










