直接用 aiohttp.clientsession() 会丢失 cookie,因其默认使用临时 cookiejar,每次新建 session 即清空;必须复用同一 session 实例,并确保未手动设置 cookie 头、set-cookie 格式合法。

为什么直接用 aiohttp.ClientSession() 会丢失 Cookie
因为默认的 aiohttp.ClientSession 使用的是临时 CookieJar,每次新建 session 就清空一次。如果你发完登录请求再发后续请求,但没复用同一个 session 实例,服务端就认不出你——它根本没收到上次 Set-Cookie 的上下文。
常见错误现象:401 或 302 跳转回登录页,response.cookies 看似有值,但下次请求 header 里没带 Cookie 字段。
- 必须复用同一个
ClientSession实例(不能每次请求都async with aiohttp.ClientSession() as session:) - 确保没手动设置
headers={'Cookie': ...},这会绕过CookieJar自动管理 - 登录接口返回的
Set-Cookie必须是合法格式(比如没多出空格、没乱码),否则CookieJar会静默忽略
怎么让 Cookie 自动跨请求持久化
靠 aiohttp.CookieJar 的默认行为就行,不用额外配置。只要 session 复用,它就会自动解析 Set-Cookie、匹配域名路径、按规则附加 Cookie 请求头。
关键点在于初始化时传入一个可持久化的 CookieJar 实例(虽然默认就是,但显式声明更可控):
import aiohttp <p>jar = aiohttp.CookieJar(unsafe=True) # 允许非标准域名(如 localhost) session = aiohttp.ClientSession(cookie_jar=jar) </p>
-
unsafe=True是开发调试必需的,否则localhost或 IP 地址域名会被拒绝存 Cookie - 生产环境建议关掉
unsafe,改用真实域名 + HTTPS + 正确的Domain属性 - 不需要调用
jar.update_cookies()或手动操作 cookie 字典——那是旧版或 requests 的用法,aiohttp 里全由 session 自动调度
需要手动干预 Cookie 的几种真实场景
绝大多数情况不用管,但以下情形得主动处理:
- 登录后要提取某个特定字段(比如
csrf_token)用于后续表单提交:从response.cookies里取,不是从session.cookie_jar - 某些网站用 JavaScript 动态写入 Cookie(如通过
document.cookie),aiohttp 完全不执行 JS,这类 Cookie 必须自己构造并塞进cookie_jar.update_cookies() - 需要预设初始 Cookie(比如已知的 session id):调用
jar.update_cookies({'session_id': 'abc123'}, response_url),注意第二个参数必须是yarl.URL类型,不能是字符串
示例(预设 Cookie):
from yarl import URL
<p>jar.update_cookies(
{'auth_token': 'xyz789'},
URL('<a href="https://www.php.cn/link/e4639aefe47ac53c3df3d8f9846b5161">https://www.php.cn/link/e4639aefe47ac53c3df3d8f9846b5161</a>')
)
</p>
Cookie 持久化失效的隐蔽原因
最常被忽略的是时间戳和有效期校验。aiohttp 的 CookieJar 默认启用严格过期检查,如果系统时间比服务端快几分钟,它会直接丢弃所有带 Expires 的 Cookie。
- 检查系统时间是否准确:
timedatectl status(Linux)或对比 NTP 时间 - 抓包确认响应头里的
Set-Cookie是否含Expires=...或Max-Age=... - 临时绕过时间校验(仅调试):
aiohttp.CookieJar(unsafe=True, quote_cookie=False),但不要长期依赖 - 注意:即使 Cookie 存进 jar,如果请求 URL 的 scheme/domain/path 不匹配,它也不会自动发送——这是 RFC 合规行为,不是 bug
真正难排查的是 domain 匹配逻辑:比如服务端设了 Domain=example.com,而你请求的是 sub.example.com,它能匹配;但若设了 Domain=sub.example.com,请求 example.com 就不会带上。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











