不装 selenium:用 CDP 直接从本机 Chrome 取指定域 Cookie

举报
yd_241537561 发表于 2026/10/09 19:21:38 2026/10/09
【摘要】 自动化脚本常要复用浏览器登录态。用 Chrome 调试端口 + websocket 直连 CDP,20 行代码就能按目标域取出 Cookie 并按域名宽度合并,比 selenium 轻,也不会被 webdriver 特征识别。

写每日任务脚本时最麻烦的往往不是接口,而是登录态。把账号密码写进脚本既不安全,又随时可能被风控拦。更稳的做法是复用已经在跑的 Chrome:它已经完成过滑块、验证码,Cookie 是全的。我们要做的只是把这些 Cookie 按目标域取出来。

一、开调试端口,但不要暴露到公网

chrome.exe --remote-debugging-port=9333 ^
       --user-data-dir=C:\your_profile --no-first-run

端口只用 127.0.0.1 访问,不要为了省事写成 0.0.0.0——CDP 端口一旦被别的进程连上,等于把浏览器完全交出去。用 GET http://127.0.0.1:9333/json/version 探活即可判断是否可用。

二、必须连到 page,不能连 browser

这是第一个坑:/json/version 里给的是 browser 级 websocket,在它上面调 Page.navigate 常常静默不生效,调 Runtime.evaluate 又报找不到上下文。要操作页面,得从 /json 里挑 type == 'page' 的条目,连它的 webSocketDebuggerUrl。

def page_ws(substr=""):
    for t in requests.get(CDP + "/json", timeout=8).json():
        if t.get("type") == "page" and substr in t.get("url", ""):
            return create_connection(t["webSocketDebuggerUrl"],
                                     timeout=120, suppress_origin=True)
    raise RuntimeError("no page tab")

suppress_origin=True 不能省。新版 Chrome 会校验 Origin 头,不带这个参数握手阶段就被 403 掉,报错还只写在响应体里,很容易以为是端口没开。

三、取 Cookie:Storage.getCookies 比 Network 全

Network.getCookies 默认只给当前页面能用到的 Cookie,httpOnly、跨子域的会漏。Storage.getCookies 返回浏览器仓库里的全部条目,包含 domain、path、httpOnly、secure、expirationDate,后面按 host 筛选就靠它。

def cookie_rows(ws):
    return cdp_call(ws, "Storage.getCookies").get("cookies") or []


def jar_for(rows, host):
    best = {}
    for c in rows:
        d = (c.get("domain") or "").lstrip(".")
        if not (host == d or host.endswith("." + d)):
            continue
        depth = d.count(".")
        cur = best.get(c["name"])
        if cur is None or depth < cur[0]:
            best[c["name"]] = (depth, c["value"])
    return {k: v[1] for k, v in best.items()}

合并逻辑的关键是「同名取域名更宽的那份」:.example.com 这种根域 Cookie 才是服务端做会话校验时真正读到的那一条,子域上的同名条目多半是前端埋点写的。拍平(后写覆盖先写)会随机踩到错的那条,表现就是接口报 CSRF 或判定未登录。

四、执行 JS 记得 awaitPromise

def ev(ws, expr, wait=True):
    ws.settimeout(60)
    r = cdp_call(ws, "Runtime.evaluate",
                 {"expression": expr, "returnByValue": True, "awaitPromise": wait})
    return (r.get("result") or {}).get("value")

returnByValue 拿到的是可 JSON 化的值,否则返回的是 RemoteObject 引用,打印出来只有一堆 objectId。awaitPromise 配合表达式写成 (async () => {...})() 才能等到 fetch 完成。

另一个细节是超时:如果之前调过 drain()(把缓冲区里的事件读干净),socket 超时可能被留在 0.05 秒,下一次调用直接抛 WebSocketTimeoutException。每次正式调用前显式 settimeout(60) 比较稳。

小结

CDP 裸连的好处很实际:没有 webdriver 特征、不用装驱动、单文件几十行,拿到的 Cookie 与浏览器完全一致。要注意的只有四点——连 page 不是 browser、握手加 suppress_origin、按 host 和域名宽度筛 Cookie、给 socket 显式超时。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。