登录的时候在这两页面来回跳,就是不弹出验证码.


解决方法很简单,就是登录的时候不要用调试模式,登录后需要控制浏览器时再用调试模式.
下面是详细解释:
为什么脚本登录总卡在人机验证?——「登录」和「自动化」必须分家
你可能遇到过这个场景:
写了个小工具,用 Playwright 打开 Chrome,去登 Google。账号密码都对,然后卡在「验证身份 · 证明您不是自动程序」那一页:点勾,转圈,回到空方框,再点,再转圈。循环十分钟,一次都没过。
换成手动打开浏览器,同样的账号、同样的网络、同一台电脑,验证码一点就过。
于是开始怀疑人生:是不是账号被风控了?是不是 IP 被标记了?是不是得买个住宅代理?是不是要装个 stealth 插件?
都不是。
真正的原因只有一句话:那个窗口不是你开的,是程序开的。 而"登录"这件事,服务器审核的恰恰就是"这个浏览器是谁在开"。
一、先用一个判读,把方向定死
先看现象细节。人机验证卡住通常有两种完全不同的表现:
| 表现 | 真正的原因 |
|---|---|
| 卡片里写「请检查网络连接」「网络错误,请重试」 | 资源没加载出来:www.google.com/recaptcha/* 被墙、代理挂了、证书有问题 |
| 卡片干净地转圈 → 退回未勾选的空方框 → 再点再转 | 资源是通的,是服务端反复拒绝你的验证结果 |
第二种才是本文要说的。区别很重要:
- 第一种是"你的网络到不了验证码服务器",换代理、换 DNS 能解决。
- 第二种是"验证码服务器到了,但它不认你"。这时候再怎么换 IP、清 cookie、重装浏览器,基本都在原地打转。
判读口诀:能看见验证码的界面,就说明网络没断;界面反复退回初始态,就是信任不够。
二、登录页到底在审核什么
很多人把登录理解成"提交账号密码,服务器校验一下密码对不对"。
不是的。密码只是入场券,真正的判断是:这个操作像不像账号主人在自己的设备上做的。
服务器(或者它背后的风控系统)会综合看这些信号:
| 维度 | 它想知道的事 |
|---|---|
| 设备 | 这台机器以前来过吗?装过什么、用过多久? |
| 历史 | 这个浏览器里有这个站点的 cookie、缓存、访问记录吗? |
| 身份延续 | 浏览器本身有没有登录过这个账号(比如 Chrome 的账号同步)? |
| 网络 | 出口 IP 和这个账号平时的活动区域一致吗?有没有突然跨洲? |
| 交互 | 是人在点,还是脚本在点?有鼠标轨迹、有停顿、有误操作吗? |
| 工具特征 | 这个浏览器是不是被调试器、自动化框架控制着? |
前五项,你日常用的浏览器全都"及格":用了两年、有几千条历史、cookie 攒了一大堆、Chrome 里还登着 Google 账号。所以验证码要么不弹,要么弹了也是一点就过。
而程序开的浏览器,这五项基本全挂。下面拆开说。
三、程序开的浏览器,天生自带四个"可疑"标记
1. 空 profile = 陌生设备
自动化框架为了不污染你的日常浏览器,几乎都会用一个独立的数据目录:
chrome --user-data-dir=/path/to/bot-profile
这个目录如果是刚 mkdir 出来的,那它就是一台全新的、没有任何历史的设备:没有 cookie,没有缓存,没有访问记录,浏览器本身也没登录过任何账号。
对风控来说,这就是"一个从没见过的设备,第一次登录这个账号"——按规矩就该验证身份。
这一步就已经注定要弹验证码了。 后面能不能过,取决于别的信号。
2. 调试端口 / CDP = 明确的工具特征
为了"接管"浏览器,脚本通常要开远程调试:
chrome --remote-debugging-port=9222
然后 Puppeteer / Playwright / Selenium 通过 CDP 连上去,新建标签页、导航、执行 JS。
问题在于:这是所有自动化脚本的共同特征。 全世界的爬虫、机器人、自动化测试,都是这么开浏览器的。风控厂商不会不知道这一点,页面里能观察到的相关痕迹(注入的脚本、被启用的调试域、异常的执行环境)也早就被写进了检测逻辑。
具体检测到什么程度,各家不公开,也没必要赌。结论很简单:需要"证明你是人"的页面上,别带调试端口。
3. 没有用户手势的导航 = 不像人
脚本打开页面通常是这样的:
page.goto("https://studio.youtube.com/")
一次跳转,零点击,零滚动,零停留。而真人会先看到地址栏、等加载、可能点一下 cookie 同意、滚两下、再点登录。
登录页对"这次导航是怎么来的"是有感知的(有没有用户激活、是不是脚本发起的)。这一项单独未必致命,但和 1、2 叠加起来,画像就很清晰了。
4. IP 漂移 = 雪上加霜
还有个经常被忽略的坑:你的脚本和你的浏览器,可能不是同一个出口 IP。
比如你日常靠系统代理 / TUN 模式 / 浏览器插件上网,而脚本启动的 Chrome 读的是环境变量 HTTPS_PROXY,或者干脆直连。两边 IP 不一样,就等于"设备是新的,位置还突然变了"。
对风控来说,这是风险分最高的组合之一。很多人换了一堆代理还是过不去,就是因为换 IP 并不能解决"设备不熟"这个问题,只是从一个陌生 IP 换到另一个陌生 IP。
四、一个常被忽略的事实:登录态是可以搬运的
到这儿,思路应该转一下了。
登录态不是一次"请求",而是一份"资产"。 它的物理形态就是浏览器 profile 目录里的一堆文件:cookie、Local Storage、IndexedDB、以及"这台设备已通过验证"之类的标记。
这意味着两件事可以彻底分开:
| 环节 | 需要什么 | 谁来做 |
|---|---|---|
| 获取登录态 | 一台看起来正常的、可信的浏览器 | 人(手动点几下) |
| 使用登录态 | 能自动导航、自动点击 | 程序(Playwright / CDP / Selenium) |
只要登录态已经躺在 profile 里,程序带着 CDP 接管这个 profile 时,服务器看到的是"这台设备已经登录过了",直接放行,根本不会再弹验证码。
这就是"登录一次,长期复用"能成立的全部原理。
五、正确姿势:一个 profile + 一次人工登录
第一步:用普通浏览器登录一次
不要用任何自动化框架,直接命令行启动系统 Chrome,指向那个独立的 profile:
:: Windows
"C:\Program Files\Google\Chrome\Application\chrome.exe" ^
--user-data-dir="D:\bots\yt-profile" ^
--profile-directory=Default
# macOS
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--user-data-dir="$HOME/bots/yt-profile" \
--profile-directory=Default
# Linux
google-chrome --user-data-dir="$HOME/bots/yt-profile" --profile-directory=Default
注意这里没有任何 --remote-debugging-port、没有 --headless、没有 --enable-automation。 它就是一台你亲手打开的浏览器。
在这个窗口里正常登录,进到目标站点,然后把整个窗口关掉(不是只关标签页)。profile 落盘,cookie 就在里面了。
小技巧:登录前先在这个窗口里逛一下目标站点、点掉 cookie 同意弹窗,让 profile 有一点"人味",通过率会更高。
第二步:让脚本复用这个 profile
Playwright(Python):
from playwright.sync_api import sync_playwright
PROFILE = r"D:\bots\yt-profile"
with sync_playwright() as p:
ctx = p.chromium.launch_persistent_context(
user_data_dir=PROFILE,
channel="chrome", # 用系统 Chrome,别用 Playwright 自带的 Chromium
headless=False, # 有头更稳;无头容易被判"不受支持的浏览器"
)
page = ctx.pages[0] if ctx.pages else ctx.new_page()
page.goto("https://studio.youtube.com/")
# 此时应该是直接进后台,不再要求登录
Selenium:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
opt = Options()
opt.add_argument(r"--user-data-dir=D:\bots\yt-profile")
opt.add_argument("--profile-directory=Default")
driver = webdriver.Chrome(options=opt)
driver.get("https://studio.youtube.com/")
第三步:接受一个前提 —— 登录和运行用的必须是同一个 profile
如果你登录时用的是 .chrome-bot,运行时用的是 .chrome-profile,那等于白登。两个目录,两套 cookie,服务器看就是两台机器。
同理,--profile-directory 也要对上:登录写在 Default,运行时别去开 Profile 1。
六、为什么 Chrome 136 之后"必须用独立 profile"
从 Chrome 136 开始,出于安全考虑,不允许对默认数据目录开启远程调试——也就是你没法用 --remote-debugging-port 去接管你日常那个 Chrome。
这个限制看起来是坏消息:想自动化,就必须用独立 profile;而独立 profile 是空的,登录就过不去。
但换个角度,它其实帮你把两件事理清了:
- 日常 profile 是你的身份,不该被脚本碰
- 独立 profile 是脚本的资产,干净、可备份、可搬迁、可一账号一份
- 两者之间只传递一件事:登录态
多账号场景下这个结构尤其舒服:一个账号一个 profile 目录,各自人工登一次,之后脚本轮着用,互不干扰。
七、那如果"必须"让程序登录呢?
有时候确实做不到人工。比如无人值守的服务器、批量账号、要跑在容器里。
先说结论:纯自动过验证码这条路,投入产出比很差,而且不稳定。 风控在持续升级,今天能过的技巧下个月就失效,还得跟着改。
更现实的三种做法:
1. 人机分工:程序开窗,人来点
程序只负责把窗口打开、导航到登录页,然后停下来等人。人过完验证码,程序检测到已经进入目标页面,再接手后续操作。
page.goto("https://accounts.google.com/")
input("请在打开的窗口里完成登录,完成后按回车…") # 人在这里干活
page.goto("https://studio.youtube.com/") # 程序接手
关键是:这个"等"的窗口,最好也是普通浏览器开的,不是 CDP 接管的。如果必须由脚本启动,就尽量别在登录阶段开调试端口。
2. 远程桌面:把"手"送过去
服务器没有桌面,但可以开个 RDP / VNC / 虚拟显示,在里面用普通浏览器登一次,profile 留在那台机器上。后续定时任务直接复用。这是最省心、最稳定的方案。
3. 把 cookie 当配置来管理
登录态既然是一个文件集合,就可以导出、加密保存、按需注入。适合"偶尔要换个账号跑一下"的场景。
要注意的是:cookie 是有有效期的,而且换设备、换 IP 使用会触发复验。别指望一份 cookie 能用一年。
八、五分钟定位:你到底卡在哪一环
遇到"登录过不去",按这个顺序做对照实验,比瞎试有效得多:
| 实验 | 结果 | 说明 |
|---|---|---|
| ① 用全新 profile 手动登一次 | 也弹验证码,但能过 | 说明"空 profile 必弹"是正常的,问题不在网络 |
| ② 同一台机器,日常浏览器登一次 | 一次过、甚至不弹 | 说明账号和网络都没问题,差的是设备信任 |
③ 同一 profile、同一台机器,去掉 --remote-debugging-port 再登 |
能过 | 那罪魁祸首就是调试端口 / CDP |
| ④ 关掉调试端口还是不过 | 还是循环 | 去看出口 IP、第三方 cookie、系统时间 |
四步做完,基本不用猜了。
九、反模式清单(踩过的人都懂)
- 用刚
mkdir出来的空 profile 登 Google —— 必弹,且大概率过不去 - 在登录环节带
--remote-debugging-port—— 相当于举着"我是机器人"的牌子 - 用
--headless登录 —— UA 里带HeadlessChrome,很多站点直接判"不受支持的浏览器" - 指望 stealth 插件"过验证码" —— 能提高一点通过率,但和风控是军备竞赛,不是解法
- 同一个验证码反复点 —— 越点风险分越低,失败两三次就换方式(手机验证、其他设备)
- 登录和运行用两个不同 profile —— 等于白登
- 登录完不关窗口就去跑脚本 —— profile 被锁,脚本启动的浏览器会"转交"给已有进程,调试端口永远不开
- 以为换 IP 能解决设备不熟 —— 新 IP 也是"陌生",只是从陌生 A 换到陌生 B
十、一句话原则
凡是需要证明"你是人"的环节,就别让机器去证明。
登录、二次验证、人机验证,这些地方的设计目的就是"确认屏幕前坐着一个真人"。你可以让机器把窗口打开、把页面准备好、把后续所有活干完,但那几下点击,最好真的是人点的。
一旦登录态落进 profile,剩下的事就都是机器擅长的了:导航、等待、提取、提交、重试。分工清楚,两边都舒服。
这套思路不只能治 Google。Microsoft、Amazon、Cloudflare 的盾、淘宝、银行、Steam——凡是"脚本登不上、手动一登就过"的地方,基本都是同一个病根,同一副药。
