Scrapy 如何处理 Cookies 和多会话登录?
直接答案
Scrapy 默认会通过 CookiesMiddleware 维护 cookie:同一个会话里的响应 Set-Cookie 会被保存,后续请求会自动带上。登录类爬虫通常用 FormRequest 先提交账号密码,再把登录后的请求接在回调里;如果要同时爬多个账号、多个店铺或多个地区,就用 meta['cookiejar'] 隔离会话。真正容易出错的地方不是“能不能带 cookie”,而是 cookie 什么时候该让 Scrapy 管、什么时候该你自己管。手动在 headers 里塞 Cookie 看起来快,但会绕开 Scrapy 的 cookie 合并逻辑,后续重定向、刷新 token、跨域跳转都可能变乱。
pythonimport scrapy class AccountSpider(scrapy.Spider): name = "account" custom_settings = {"COOKIES_ENABLED": True, "COOKIES_DEBUG": False} def start_requests(self): yield scrapy.FormRequest( "https://example.com/login", formdata={"username": "u1", "password": "p1"}, meta={"cookiejar": "u1"}, callback=self.after_login, ) def after_login(self, response): yield response.follow("/user/orders", callback=self.parse_orders, meta={"cookiejar": response.meta["cookiejar"]}) def parse_orders(self, response): yield {"url": response.url, "title": response.css("title::text").get()}
如果站点把登录状态放在 localStorage 或 JS 变量里,单靠 cookie 不够,要先在浏览器抓包确认真正校验的是 cookie、Authorization header,还是隐藏接口里的 token。生产环境还要注意 cookie 的生命周期:短期任务放内存即可,跨天任务才需要持久化到 Redis、数据库或加密文件。保存 cookie 时不要把明文账号、验证码结果、敏感 token 写进日志,尤其不要开启 COOKIES_DEBUG 后直接把日志上传到公共平台。
追问
什么时候用 Scrapy 自动 cookie,什么时候手动传 cookies?
能让 Scrapy 自动管理时优先让它管,因为它会处理 Set-Cookie、域名、路径、过期时间和重定向后的合并。手动传 cookies={...} 适合一次性请求,比如带一个固定地区、语言或实验分组标识。不要把完整 Cookie 字符串放进 headers,除非你明确知道不会再依赖响应里的 Set-Cookie。这个取舍的边界是会话是否会变化:会变化就交给中间件,不变化才手动给。
多账号同时爬时 cookie 会不会串号?
默认情况下,同一个 spider 会共用一个 cookie jar,所以多账号并发登录如果不隔离,确实可能出现 A 账号请求带上 B 账号 cookie 的事故。解决办法是为每个账号设置不同的 meta['cookiejar'],并在后续所有请求里继续传递这个值。踩坑点是 response.follow() 不会替你“记住业务身份”,忘了传 meta 就会回到默认 jar。并发越高,这类串号越隐蔽,最好把账号标识也写入 item 或日志方便排查。
登录后仍然被跳回登录页,应该先查什么?
先看登录响应是否真的成功,不要只看 HTTP 200,很多站点失败时也返回 200 但页面里写着错误提示。其次检查是否缺少 CSRF token、验证码、动态签名或必须的 Referer。再看后续请求是不是丢了 cookiejar,以及重定向过程中 cookie 域名是否从 www.example.com 变成了 example.com。如果这些都正常,再考虑是否存在设备指纹或风控,而不是一上来就换代理。
cookie 需要持久化吗?
短任务不建议持久化,内存 cookie 简单、干净,任务结束就释放,也不容易留下敏感信息。长周期采集、登录成本高或验证码昂贵时,可以把 cookie 加密后存储,并记录过期时间和账号状态。边界在于 cookie 失效成本和泄露风险:越敏感的站点越要少存、加密存、按需刷新。一个常见坑是复用过期 cookie 后误判为“选择器失效”,其实页面已经被重定向到登录页。
Scrapy 能处理 JWT 或 Authorization 吗?
能处理,但 JWT 不属于 cookie 机制,通常要放在请求头或接口参数里。登录后从响应 JSON、HTML 脚本或浏览器存储中取到 token,再在后续请求里加 Authorization: Bearer xxx。如果 token 会刷新,需要在中间件或回调里统一更新,避免部分请求继续使用旧 token。取舍上,纯接口站点用 header 管 token 更清晰;传统 Web 站点则继续让 cookie 中间件处理会话。
还有一个容易被忽略的细节:Scrapy 的 cookie jar 是跟请求链路走的,不是跟账号对象自动绑定的。如果你从一个登录回调里拆出多个分页请求,分页请求都要带同一个 cookiejar;如果中途又发起刷新 token 或退出登录请求,也要确认它不会污染同组会话。对于需要定期续期的站点,可以把“检测是否登录失效”写成一个小函数,例如看到登录按钮、特定错误码或跳转地址时重新登录。这样比在每个解析函数里临时判断更稳,也能避免同一个账号被多个并发请求同时刷新,造成服务端把旧 cookie 全部作废。
如果你要把 cookie 持久化,建议同时保存来源域名、账号标识、创建时间和最近验证时间,而不是只保存一串 cookie 值。恢复时先访问一个轻量的个人中心或状态接口确认有效,再进入正式抓取。这样做多了一次请求,但能避免大量业务请求都拿着失效会话去跑。对于涉及个人数据的网站,还要把 cookie 当成密码处理,权限、加密和清理策略都不能省。