python 爬虫如何正确的使用cookie
发布时间:2026/10/8 16:02:09 作者:尧图编辑部 阅读量:1,286

前言先把最要紧的一句放在开头Cookie 等同于你的登录凭据。它不是一个普通的请求参数而是服务端下发给你、用来在后续请求里认出「你还是刚才那个人」的凭据。理解这一点之后下面的两条铁律就顺理成章了绝不能把自己的 Cookie 交给别人也绝不能拿别人的 Cookie 来用——后者等同于盗用他人身份。也不要把 Cookie 提交给第三方站点更不要把它写进公开的代码仓库。第二个常见误解是「Cookie 是爬虫用来伪装的工具」。不是。Cookie 的正当用法只有一种在同一会话session内保持状态比如登录之后继续访问需要登录的页面、保持分页的筛选条件、维持服务端下发的 CSRF 令牌。它记录的是「你和服务端之间的一次会话」不是「如何让自己看起来不像爬虫」。第三个误解是「设了 Cookie 就能解决被反爬」。如果站点明确不欢迎你403、验证码带上 Cookie 也改变不了这个判断而且拿别人的 Cookie 去绕过登录墙属于盗用身份本文不会提供任何相关做法。本文讲三件事Cookie 是什么、Set-Cookie的每个属性是什么意思、以及用http.cookiejar与requests.Session在同一会话内正确保持登录态。本机没有 Python 解释器也没有装requests代码未在本机运行验证只作逐行人工推演涉及第三方库的地方会注明需要先安装。示例全部使用本地字符串和 RFC 2606 保留的示例域名不指向任何真实站点。一、Cookie 是什么服务端下发的状态凭据HTTP 本身是无状态的每个请求在服务端看来都是独立的它没法凭请求本身知道「你现在是不是登录着」。Cookie 就是解决这个问题的机制——服务端通过Set-Cookie响应头把一小段状态交给你保管你在后续请求里通过Cookie请求头把它带回去服务端据此恢复上下文。一个典型的登录流程是这样的你提交凭据服务端校验通过后在响应里下发一个包含会话 ID 的 Cookie之后你访问同一个站点的其他页面时浏览器自动带上这个 Cookie服务端检查会话 ID有效就返回个性化页面无效就删掉它并返回通用页面或要求重新登录。这里有个关键的角色划分服务端是签发方客户端是保管方。Cookie 的内容由服务端决定客户端只负责按规则存和按规则带。所以「我能不能改这个 Cookie」这个问题本身就是错的——你可以改但改了之后服务端认不认是另一回事而且在会话类 Cookie 上改动通常只会让自己掉线。还要区分一下 Cookie 和浏览器本地存储。现代浏览器提供了 Web Storage、IndexedDB 等专门做客户端存储的 API普通数据不该再塞进 CookieCookie 会随每个同域请求自动发出塞进去的东西越多每个请求就越肥。Cookie 的定位应该是会话相关的少量状态。二、Set-Cookie的各个属性Set-Cookie头由「名字值」加上若干属性构成。这些属性决定了这个 Cookie 会被发到哪些请求、能不能被 JavaScript 读到、跨站时怎么处理。理解它们是「正确使用」的核心。属性作用典型取值对爬虫的含义Domain允许携带该 Cookie 的域example.invalid决定 Cookie 的作用域不写则默认仅当前主机Path允许携带该 Cookie 的路径前缀/、/app路径不匹配就不会带上别以为所有请求都有Expires绝对过期时间HTTP 日期不写或过期即为会话 CookieMax-Age相对过期时间秒3600与Expires同时存在时Max-Age优先Secure只在安全协议下发送无值只在 HTTPS 下携带明文 HTTP 不发送HttpOnly禁止 JavaScript 读取无值只能用 HTTP 请求带上脚本读不到SameSite跨站请求是否携带Strict/Lax/NoneNone时必须同时设置Secure几个要点展开Max-Age优先于Expires。Max-Age是从现在起还有多少秒过期Expires是一个绝对时间同时出现时以Max-Age为准。Max-Age为零或负数表示立即过期。没有Expires也没有Max-Age的是会话 Cookie客户端关闭时删除。注意——很多浏览器有「会话恢复」功能会把会话 Cookie 一起恢复所以会话 Cookie 在实践中可能活得比你想的久。这对写爬虫有实际影响别假设「关掉就没了」。Secure只保证传输通道加密不保证内容安全。官方文档特意提醒不要以为设了Secure就万无一失——如果同时没有HttpOnlyJavaScript 仍然能读写它能接触客户端磁盘的人也能读到它。所以「Cookie 里绝不放敏感信息」这条依然成立。HttpOnly是防 XSS 的措施带这个属性的 Cookie 无法被 JavaScript比如document.cookie读取只能随 HTTP 请求到达服务端。会话类 Cookie 都应该带它。SameSite控制跨站携带。Strict只在同站请求里发送Lax允许「顶层导航 安全方法如 GET」的跨站请求携带但会挡掉fetch、子资源加载和POST之类的跨站请求None表示跨站也发送但必须同时设置Secure。现代浏览器在未指定时大多把默认值当作Lax。还有一组前缀约定值得知道名字以__Secure-开头的 Cookie 必须由 HTTPS 页面设置且带Secure以__Host-开头的除了Secure之外还必须不设Domain且Path必须是/这样能保证它只发回给设置它的那个主机不会泄露给同域的其他子域。这些前缀是服务端用来约束自己的客户端只要照常保管即可。用标准库的http.cookies可以在本地把Set-Cookie字符串和对象互相转换完全不需要网络# 适用于 Python 3.8from http.cookies import SimpleCookie, CookieError# 解析一段本地的 Set-Cookie 字符串raw sessionidabc123; Path/; Secure; HttpOnly; SameSiteLaxtry:c SimpleCookie()c.load(raw)except CookieError as e:raise SystemExit(f解析失败: {e})morsel c[sessionid]print(morsel.key) # sessionidprint(morsel.value) # abc123print(morsel[path]) # /print(morsel[samesite]) # Lax# 反过来构造一个 Cookie 并生成 Set-Cookie 头out SimpleCookie()out[sessionid] abc123out[sessionid][path] /out[sessionid][secure] Trueout[sessionid][httponly] Trueout[sessionid][samesite] Laxout[sessionid][max-age] 3600print(out.output())这段代码里唯一需要注意的地方是官方文档明确建议来自浏览器的 Cookie 数据要准备接住CookieError因为非法属性、非法的Set-Cookie头都会让解析抛异常。所以load()一定要包在try里而不是假设输入永远合法。另外output()产出的属性顺序不保证按你写入的顺序排列写测试时别去比对整串字符串。三、在同一会话内保持登录态http.cookiejar模块就是干这个的它提供一个「Cookie 罐子」CookieJar配合urllib.request的处理器让 Cookie 的存取自动发生。你只管发请求罐子负责带上去、收回来。# 适用于 Python 3.8import http.cookiejarimport urllib.request# 一个能识别出你自己的 UA而不是伪装成某个浏览器UA MyCrawler/0.1 (遵守目标站点 robots 与服务条款)jar http.cookiejar.CookieJar()opener urllib.request.build_opener(urllib.request.HTTPCookieProcessor(jar))opener.addheaders [(User-Agent, UA)]# 从这里开始凡是经由 opener.open(...) 发出的请求# 1) 收到 Set-Cookie 会自动收进 jar# 2) 下次请求会自动带上 jar 里符合 Domain/Path 的 Cookie# 示例不实际发起请求请在你获得授权的目标上使用。关键点是所有请求都必须走同一个opener。如果你用urllib.request.urlopen()直接发请求那个请求不会经过你的 Cookie 处理器罐子是空的、也不会被填充——这是「明明登录了却还是未登录」最常见的原因。需要跨进程保留会话时可以用http.cookiejar.LWPCookieJar或MozillaCookieJar它们支持把罐子存到文件再读回来接口是save(filename, ignore_discard, ignore_expires)与load(...)。但请把保存 Cookie 的文件当作密钥文件对待放在不会被同步、不会被提交的位置用尽量收紧的权限绝不要放进代码仓库。注意save()默认不保存会话 Cookie除非明确要求忽略 discard 标记。如果用requests第三方库需先pip install requests对应的做法是requests.Session()会话对象自己持有一个 Cookie 罐子用同一个 session 发请求登录态就自动维持。本机没有安装requests且我不打算凭印象写出它的具体参数所以这里只讲思路——会话对象的用法请以 requests 官方文档为准。需要记住的只有一条原则同一个会话里所有请求都要用同一个 session 对象发混用requests.get()和session.get()会得到两个互不相通的罐子。四、安全铁律这一节不是补充说明而是本文最重要的部分Cookie 就是你的登录凭据。拿到你的会话 Cookie 的人在它过期之前可以完全冒充你。很多站点把这个窗口设得很长所以「反正会过期」不是理由。绝不把自己的 Cookie 给别人。不发给同事帮你「试一下」不发到群里求助不粘进任何在线调试工具或第三方网站。要帮人排查就给脱敏后的请求结构而不是真凭据。绝不拿别人的 Cookie 来用。用别人的会话 Cookie 访问站点等同于盗用他人身份无论你的目的是「测试」还是「学习」。这也包括从浏览器里复制、从抓包文件里翻出来的那些。不把 Cookie 提交给第三方站点。把 A 站的 Cookie 发给 B 站等于把 A 站的身份送给了 B 站。不写进公开代码仓库。硬编码在脚本里的Cookie头、提交上去的cookies.txt都是常见的凭据泄露源。用环境变量或专门的密钥管理并把相关文件加进忽略清单。日志里不要打印完整的 Cookie。调试时可以打印「有哪些 Cookie 名」「长度是多少」不要打印值。顺带一句站点在你登录成功后应该重新下发会话 Cookie这是为了防止「会话固定」攻击攻击者预先让受害者用上一个已知的会话 ID。如果你在写服务端这条也要照做。常见坑点❌ 用urllib.request.urlopen()直接发请求却奇怪为什么 Cookie 没被带上。✅ 所有请求都走同一个由build_opener()创建、装了HTTPCookieProcessor的opener罐子才会自动存取。❌ 把 Cookie 值直接硬编码在源码里然后提交进公开仓库。✅ Cookie 是凭据走环境变量或密钥管理相关文件加进忽略清单日志也不要打印完整值。❌ 拿从别处复制来的 Cookie 去绕过登录墙。✅ 使用他人的会话 Cookie 等同于盗用身份登录态只能来自你自己的账号与你自己的授权。❌ 假设「没设Expires就是关掉进程就失效」。✅ 会话 Cookie 没有Expires也没有Max-Age但浏览器的会话恢复会让它活得更久不要依赖「关闭即失效」。❌ 同时看到Max-Age和Expires按Expires算过期时间。✅Max-Age优先Max-Age为零或负值表示立即过期。❌ 以为设了Secure就安全把敏感信息塞进 Cookie。✅Secure只保证只在加密通道发送没有HttpOnly时 JavaScript 仍可读写能接触磁盘的人也能读取敏感信息不应放 Cookie。❌ 只设置SameSiteNone不设Secure。✅SameSiteNone时必须同时设置Secure否则会被浏览器拒绝。❌ 解析浏览器导出的 Cookie 时不接异常遇到一个非法属性就整个脚本崩掉。✅ 官方文档建议对来自浏览器的 Cookie 数据准备接住CookieError把load()包进try里处理。总结概念准确理解常见误解Cookie 的本质服务端下发的状态凭据等于登录凭据「爬虫的伪装工具」正当用途在同一会话内保持登录态与状态「绕过登录墙」谁签发服务端签发的客户端只保管「客户端可以随意定制」Domain/Path决定哪些请求会带上它「设置了就所有请求都带」Max-Age/Expires前者优先都没有则是会话 Cookie「会话 Cookie 关掉就没了」Secure/HttpOnly前者管通道后者挡 JavaScript「设了Secure就绝对安全」SameSiteNone必须同时设Secure「可以单独用」保管方式环境变量 / 密钥管理绝不公开「写在脚本里方便」正确使用 Cookie 的答案其实一句话就能概括用同一个会话对象、走同一个请求通道把服务端下发的凭据原样保管和回送除此之外不要做任何多余的事。不需要伪造、不需要拼接、更不需要去借别人的。真正需要警惕的从来不是「怎么让它生效」而是「别让它落到不该拿到的人手里」——因为它一旦泄露泄露的是你的身份。参考http.cookies、http.cookiejar、urllib.request的接口以 Python 官方文档为准Set-Cookie各属性的语义与Cookie请求头的行为以 MDN 的 HTTP 文档为准requests为第三方库需另行安装接口以其官方文档为准本文代码未在本机运行仅作人工推演。