一句话总结 :CDP 提供了比 JavaScript document.cookie 更强大的 Cookie 操作 API,你可以读取任意域名的 Cookie、设置带各种标志位的 Cookie、精确删除指定 Cookie,甚至清空整个浏览器的所有 Cookie。
目录
为什么用 CDP 操作 Cookie
前置准备:连接 Chrome
读取 Cookie
新增 Cookie
删除 Cookie
修改 Cookie
实战一:Cookie 注入实现免登录
实战二:导出 Cookie 供 requests 复用
常见踩坑与最佳实践
为什么用 CDP 操作 Cookie 在前端,操作 Cookie 最常用的方法是 document.cookie,但它有诸多限制:
功能
document.cookie
CDP Cookie API
读取所有 Cookie
✅ 仅当前域名
✅ 任意域名 (整个浏览器)
设置 HttpOnly Cookie
❌ 无法设置
✅ 完全支持
设置 SameSite 属性
⚠️ 有限支持
✅ 完整支持
读取 Secure 标志
❌ 无法读取
✅ 完全读取
按域名筛选
❌ 需自己解析
✅ 直接指定域名
跨域操作
❌ 同源限制
✅ 无限制
清空所有 Cookie
❌ 只能逐条删
✅ 一键清空
简单说:CDP 的 Cookie API 相当于浏览器的”管理员模式” ,没有同源策略限制,可以操作任意 Cookie。
前置准备:连接 Chrome 首先用 Python 连接 Chrome 的 CDP 端口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 import asyncio, json, websocketsCDP_URL = "ws://127.0.0.1:9222/devtools/browser/1a6114ab-..." CMD_ID = [0 ] async def cdp (ws, method, params=None , session_id=None ): """发送 CDP 命令并等待返回结果""" CMD_ID[0 ] += 1 msg = {"id" : CMD_ID[0 ], "method" : method, "params" : params or {}} if session_id: msg["sessionId" ] = session_id await ws.send(json.dumps(msg)) async for resp in ws: data = json.loads(resp) if data.get("id" ) == CMD_ID[0 ]: return data.get("result" , {}) async def attach_to_page (ws ): """连接到页面目标并返回 session_id""" targets = await cdp(ws, "Target.getTargets" ) target_id = targets["targetInfos" ][0 ]["targetId" ] session = await cdp(ws, "Target.attachToTarget" , { "targetId" : target_id, "flatten" : True }) return session["sessionId" ] async def main (): async with websockets.connect(CDP_URL) as ws: targets = await cdp(ws, "Target.getTargets" ) target_id = targets["targetInfos" ][0 ]["targetId" ] session = await cdp(ws, "Target.attachToTarget" , { "targetId" : target_id, "flatten" : True }) session_id = session["sessionId" ] print (f"已连接到页面: {target_id} " ) await ws.send(json.dumps({ "sessionId" : session_id, "id" : 3 , "method" : "Network.getCookies" , "params" : {} })) asyncio.run(main())
💡 实用技巧 :如果你用的是 CloakBrowser 或自建 CDP 连接,建议封装一个 CDPClient 类,自动管理 sessionId 和命令 ID,后续例子会用这个简化写法。
读取 Cookie 读取所有 Cookie 1 2 3 4 async def get_all_cookies (ws, session_id ): """获取当前页面所有 Cookie""" resp = await cdp(ws, "Network.getAllCookies" , session_id=session_id) return resp.get("cookies" , [])
返回示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [ { "name" : "session_id" , "value" : "abc123xyz" , "domain" : ".example.com" , "path" : "/" , "expires" : 1776000000 , "size" : 24 , "httpOnly" : true , "secure" : true , "session" : false , "sameSite" : "Lax" , "priority" : "Medium" } ]
按 URL 筛选 Cookie 只读取特定 URL 相关的 Cookie(更精确):
1 2 3 4 5 6 7 8 9 async def get_cookies_for_url (ws, session_id, urls ): """获取指定 URL 的 Cookie(可传多个 URL)""" resp = await cdp(ws, "Network.getCookies" , {"urls" : urls}, session_id) return resp.get("cookies" , []) cookies = await get_cookies_for_url(ws, session_id, ["https://example.com" , "https://api.example.com" ] )
使用 Storage 域读取 Storage 域也提供了类似方法,适合需要精确控制存储域的场景:
1 2 3 4 async def get_cookies_storage (ws, session_id ): """使用 Storage 域获取 Cookie""" resp = await cdp(ws, "Storage.getCookies" , session_id=session_id) return resp.get("cookies" , [])
Network vs Storage 域的区别 :Network.getCookies 主要用于网络请求相关的 Cookie 读取,会带上浏览器当前的 Cookie 状态;Storage.getCookies 偏向存储层面。大部分场景用 Network.getCookies 就够了。
新增 Cookie 设置单个 Cookie 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 async def set_cookie (ws, session_id, name, value, domain="" , path="/" , http_only=False , secure=False , same_site="Lax" , expires=None ): """设置一个 Cookie""" params = { "name" : name, "value" : value, "domain" : domain, "path" : path, "httpOnly" : http_only, "secure" : secure, "sameSite" : same_site, } if expires: params["expires" ] = expires resp = await cdp(ws, "Network.setCookie" , params, session_id) return resp.get("success" , False ) await set_cookie(ws, session_id, "user_pref" , "dark_mode" , domain=".example.com" ) await set_cookie(ws, session_id, "session_id" , "abc123" , domain=".example.com" , http_only=True , secure=True , same_site="Strict" )
批量设置 Cookie 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 async def set_cookies_bulk (ws, session_id, cookies ): """批量设置多个 Cookie""" resp = await cdp(ws, "Network.setCookies" , {"cookies" : cookies}, session_id) return resp await set_cookies_bulk(ws, session_id, [ { "name" : "token" , "value" : "eyJhbGciOiJIUzI1NiJ9..." , "domain" : ".example.com" , "path" : "/" , "httpOnly" : True , "secure" : True }, { "name" : "user_id" , "value" : "12345" , "domain" : ".example.com" , "path" : "/" } ])
删除 Cookie 删除指定 Cookie 1 2 3 4 5 6 7 8 9 10 11 12 13 async def delete_cookie (ws, session_id, name, domain="" , path="/" ): """删除指定的 Cookie""" params = {"name" : name} if domain: params["domain" ] = domain if path: params["path" ] = path return await cdp(ws, "Network.deleteCookies" , params, session_id) await delete_cookie(ws, session_id, "session_id" , domain=".example.com" )
清空所有 Cookie 1 2 3 async def clear_all_cookies (ws, session_id ): """清空浏览器的所有 Cookie""" return await cdp(ws, "Network.clearBrowserCookies" , session_id=session_id)
⚠️ 注意 :clearBrowserCookies 会清空整个浏览器的所有 Cookie,不仅仅是当前标签页!生产脚本慎用。
修改 Cookie CDP 没有 专门的”修改 Cookie”命令。实现修改的策略是:删除旧 Cookie + 新增新 Cookie 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 async def update_cookie (ws, session_id, old_name, new_name, new_value, domain="" , path="/" ): """修改 Cookie(删除旧 + 创建新)""" params = {"name" : old_name} if domain: params["domain" ] = domain if path: params["path" ] = path await cdp(ws, "Network.deleteCookies" , params, session_id) set_params = { "name" : new_name, "value" : new_value, "domain" : domain or ".example.com" , "path" : path or "/" } return await cdp(ws, "Network.setCookie" , set_params, session_id)
实战一:Cookie 注入实现免登录 最实用的场景:从浏览器 A 导出登录态,注入到浏览器 B,实现免登录。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 async def export_login_state (ws_source, session_id_source ): """导出登录态(所有 Cookie)""" cookies = await get_all_cookies(ws_source, session_id_source) login_cookies = [ c for c in cookies if c.get("name" ) in ("session_id" , "token" , "auth" , "sid" ) ] return login_cookies or cookies async def import_login_state (ws_target, session_id_target, cookies ): """导入登录态""" await set_cookies_bulk(ws_target, session_id_target, cookies) print (f"已注入 {len (cookies)} 个 Cookie" ) async def cookie_login_flow (): """完整的 Cookie 注入免登录流程""" async with websockets.connect("ws://127.0.0.1:9222/devtools/browser/..." ) as ws_a: session_a = await attach_to_page(ws_a) await cdp(ws_a, "Page.navigate" , {"url" : "https://example.com/dashboard" }, session_a) await asyncio.sleep(2 ) cookies = await export_login_state(ws_a, session_a) print (f"导出 {len (cookies)} 个 Cookie" ) async with websockets.connect("ws://127.0.0.1:9223/devtools/browser/..." ) as ws_b: session_b = await attach_to_page(ws_b) await import_login_state(ws_b, session_b, cookies) await cdp(ws_b, "Page.navigate" , {"url" : "https://example.com/dashboard" }, session_b) print ("免登录成功!" )
执行顺序很关键 :必须先注入 Cookie,再导航到目标页面。如果先导航再注入,页面可能已经重定向到登录页了。
实战二:导出 Cookie 供 requests 复用 有时候我们想用 Python 的 requests 库来抓取数据,但目标网站有复杂的登录验证。可以用 CDP 先把 Cookie 导出来,喂给 requests:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import requestsdef cdp_cookies_to_requests_session (cdp_cookies ): """将 CDP Cookie 格式转为 requests Session 可用格式""" session = requests.Session() for c in cdp_cookies: session.cookies.set ( c["name" ], c["value" ], domain=c.get("domain" , "" ), path=c.get("path" , "/" ) ) return session async def fetch_with_browser_cookies (): """用浏览器 Cookie 发起 requests 请求""" async with websockets.connect(CDP_URL) as ws: session_id = await attach_to_page(ws) cookies = await get_all_cookies(ws, session_id) session = cdp_cookies_to_requests_session(cookies) resp = session.get("https://example.com/api/user/profile" ) print (f"状态码: {resp.status_code} " ) print (f"响应内容: {resp.text[:200 ]} " )
这个模式特别适合:
需要登录后才能访问的 API 数据抓取
浏览器环境复杂(JS 渲染、验签等),但数据传输用 requests 更高效
不想在 Python 里重写整套登录逻辑的场景
常见踩坑与最佳实践 踩坑 1:Cookie 注入后不生效 1 2 3 4 5 6 7 await navigate(ws, session_id, "https://example.com" )await set_cookies(ws, session_id, cookies) await set_cookies(ws, session_id, cookies)await navigate(ws, session_id, "https://example.com" )
踩坑 2:域名不匹配 CDP 设置 Cookie 时,domain 参数必须以点开头 才能跨子域生效:
1 2 3 4 5 6 7 8 await set_cookie(ws, session_id, "token" , "xxx" , domain="example.com" )await set_cookie(ws, session_id, "token" , "xxx" , domain=".example.com" )await set_cookie(ws, session_id, "token" , "xxx" , domain=".api.example.com" )
踩坑 3:Secure 标志 通过 CDP 设置带 secure: true 的 Cookie 时,必须同时指定 URL 使用 HTTPS 协议。如果不在意协议,就不要设 secure: true:
1 2 3 4 await set_cookie(ws, session_id, "token" , "xxx" , domain=".example.com" , secure=False )
踩坑 4:SameSite 策略 新版本的 Chrome 默认 SameSite=Lax。如果你需要在跨站请求中携带 Cookie,手动指定:
1 2 3 4 5 6 7 await set_cookie(ws, session_id, "token" , "xxx" , same_site="None" , secure=True ) await set_cookie(ws, session_id, "token" , "xxx" , same_site="Lax" )
踩坑 5:批量设置时的过期时间 expires 字段是 Unix 时间戳(秒),注意不要用毫秒:
1 2 3 4 5 6 7 import timeexpires_correct = int (time.time()) + 86400 * 30 expires_wrong = int (time.time() * 1000 ) + 86400 * 30 * 1000
最佳实践清单
注意点
建议
注入顺序
先注入 Cookie,再导航到目标页面
域名格式
用 .example.com(点号开头)跨子域生效
Secure 标志
非 HTTPS 页面不要设 secure: true
HttpOnly
CDP 可以读写 HttpOnly Cookie,这是 JS 做不到的
清空范围
clearBrowserCookies 是全浏览器范围,慎用
调试
设置后用 Network.getAllCookies 确认写入成功
完整参考:CDP Cookie 操作类 为了方便复用,这里提供封装好的类,集成了本文所有操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import asyncioimport jsonimport websocketsclass CDPCookieManager : """CDP Cookie 管理器""" def __init__ (self, ws, session_id ): self .ws = ws self .session_id = session_id self ._cmd_id = 0 async def _send (self, method, params=None ): self ._cmd_id += 1 msg = { "sessionId" : self .session_id, "id" : self ._cmd_id, "method" : method, "params" : params or {} } await self .ws.send(json.dumps(msg)) async for resp in self .ws: data = json.loads(resp) if data.get("id" ) == self ._cmd_id: return data.get("result" , {}) async def get_all (self ): return (await self ._send("Network.getAllCookies" )).get("cookies" , []) async def get_by_url (self, urls ): return (await self ._send("Network.getCookies" , {"urls" : urls})).get("cookies" , []) async def set (self, name, value, **kwargs ): params = {"name" : name, "value" : value, **kwargs} return (await self ._send("Network.setCookie" , params)).get("success" , False ) async def set_many (self, cookies ): await self ._send("Network.setCookies" , {"cookies" : cookies}) async def delete (self, name, **kwargs ): await self ._send("Network.deleteCookies" , {"name" : name, **kwargs}) async def clear_all (self ): await self ._send("Network.clearBrowserCookies" )
使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 async with websockets.connect(CDP_URL) as ws: session_id = await attach_to_page(ws) mgr = CDPCookieManager(ws, session_id) cookies = await mgr.get_all() print (f"当前有 {len (cookies)} 个 Cookie" ) await mgr.set ("test_cookie" , "hello_cdp" , domain=".example.com" ) await mgr.delete("test_cookie" , domain=".example.com" )
总结 :CDP 的 Cookie API 让你拥有了浏览器级的管理权限——没有同源限制,支持 HttpOnly/Secure/SameSite 等完整属性,可以精确控制每个 Cookie 的生命周期。结合前几篇文章学的网络拦截和页面控制,你已经可以用 CDP 做出非常强大的浏览器自动化工具了。
上一篇回顾:CDP 性能追踪与 Lighthouse 集成:用 Python 测量 Web Vitals 与自动化性能审计。
下一篇预告:CDP DOM 操作完全指南:用 Python 实时监听与操纵页面元素。