复用浏览器跳过扫码登录:Playwright与Selenium实操指南

发布时间:2026/9/16 10:21:20
复用浏览器跳过扫码登录:Playwright与Selenium实操指南 扫码登录这东西单独用一次还好可一旦落到自动化脚本里就成了噩梦。每次跑用例都要掏手机、解锁、对准二维码运气好三秒通过运气不好等半天二维码过期整套流程卡死在登录这一步后面全是白等。我早期做Web自动化测试时被这个环节折磨得不轻后来转向“复用浏览器”的思路才算把扫码登录这关真正绕过去。所谓复用浏览器说白了就是让自动化脚本去接手一个已经登录过的浏览器实例或者直接恢复上一次的登录缓存这样就不需要每次从零开始扫码。这个思路解决的核心问题只有一个——登录状态的重用。今天我把这套方法从思路、工具选型到实操步骤完整拆开讲包含我在实际项目中踩过的坑和总结的排查技巧希望能帮你彻底告别扫码登录的自动化噩梦。1. 复用浏览器的核心思路与适用场景1.1 为什么扫码登录会成为自动化痛点扫码登录本身是安全设计上的进步不暴露密码、一次一码、支持二次验证。但在自动化场景里它恰恰成了最不友好的环节。自动化脚本本质上是一个没有手机的程序你没法让脚本去“看”二维码更没法让它去模拟手指点击确认。常见的绕行方案有哪些有的人走接口直接伪造登录态但很多网站的接口有签名、加密参数和风控校验伪造成本极高有的人用OCR识别二维码内容再配合逆向协议这个方案对安全防护较强的站点基本行不通还有的人设置超长cookie有效期但一旦cookie过期或换设备又得重新扫码。这些方案要么维护成本高要么不稳定尤其在批量跑用例、多账号切换、夜间定时任务等场景下扫码这一步就是整个链路里最脆弱的单点。复用浏览器的思路其实非常朴素既然扫码的是浏览器里的人那我就让这个浏览器保持不变或者把登录后的状态完整保存下来下次直接恢复。这样扫码只需要一次之后无论脚本跑多少轮都不需要再碰手机。1.2 复用浏览器到底复用了什么很多人一听到“复用浏览器”第一反应是“我是不是要把整个浏览器窗口一直开着不关”。这其实是两个层级的概念需要区分清楚。第一个层级是“复用浏览器进程”。就是说你手动打开一个浏览器窗口登录好网站然后让自动化工具去接管这个已经打开的实例。这个方案适合一次性的调试任务比如说你临时要做一个数据采集脚本不想重新走一遍登录逻辑那就手动登录完直接让脚本连上这个浏览器干活。第二个层级是“复用登录态数据”。这才是真正适合长期自动化任务的方案。它的思路是先把登录成功后浏览器里的cookie、localStorage、sessionStorage等状态数据持久化到本地文件然后每次启动自动化时让新的浏览器实例加载这些数据模拟出一个“已经登录过”的浏览器。用户感知上就是脚本一启动就是登录状态扫码这步被完全跳过。这两个层级在实际项目中往往会组合使用。先用第一个层级手动登录一次然后把登录态导出来之后所有自动化任务都用第二个层级去恢复状态。1.3 哪些场景适合用复用浏览器方案不是所有自动化场景都需要复用浏览器但它在我经手的以下场景里确实非常有效数据采集类脚本需要登录后才能抓取数据且目标网站没有现成开放API每次启动脚本都扫码不现实。UI自动化回归测试测试用例本身不关心登录流程只关心登录后的业务功能复用浏览器能节省大量执行时间。多账号批量操作比如同时运营多个店铺账号每个账号都要保持登录态用复用浏览器做账号隔离和状态管理非常顺手。定时任务和无人值守任务凌晨或者周末跑的任务不可能每次都有人守在旁边扫码复用浏览器是唯一可行的路子。反过来如果你正在测试的恰恰是登录功能本身或者你的脚本运行环境是完全隔离的一次性容器那复用浏览器就不太合适。这种场景下应该老老实实走登录流程或者用测试环境提供的专用测试账号。2. 工具选型Playwright与Selenium的对比2.1 Playwright的持久化上下文在目前主流的自动化工具里Playwright对“复用登录态”的支持可以说是最直接的。它提供了两个关键能力。第一个是storageState你可以把当前浏览器上下文的登录状态保存成一个JSON文件里面包含了cookie、localStorage、sessionStorage的所有数据。下次启动时直接在创建浏览器上下文时加载这个文件就能恢复登录状态。第二个是browserContext的持久化你可以指定一个userDataDir作为用户数据目录相当于把整个浏览器的用户数据包括登录态、扩展、页面数据都保存在本地磁盘上。浏览器关闭后下次用同一个userDataDir启动就跟你上次关掉浏览器之前一模一样很多需要保持登录的站点都能直接恢复。这两个能力和我们前面说的两个层级完全对应storageState对应“复用登录态数据”userDataDir对应“复用浏览器进程状态”。我个人在多数场景下更推荐storageState因为它是纯数据干净、轻量、便于多环境复制而userDataDir虽然更接近真实用户环境但体积大且偶尔会因为浏览器崩溃导致数据损坏。2.2 Selenium的调试端口复用Selenium是更传统的自动化工具它本身没有直接提供保存登录态的方法但可以通过“调试端口”的方式复用浏览器。具体思路是以带远程调试端口的模式启动Chrome浏览器手动在这个浏览器里完成扫码登录之后Selenium通过WebDriver协议连接这个已有的浏览器实例。这样Selenium用的就是你已经登录好的浏览器不需要再走登录流程。这种方式的好处是直观、侵入性低适合调试类任务。坏处是它依赖一个“已经打开的浏览器实例”如果浏览器进程异常退出脚本也就断了。而且多并发场景下每个实例都要单独管理端口稍显笨重。Selenium还有一种更常规的登录态复用方式就是手动导出cookie然后在脚本启动时用add_cookie加回去。这个方法能做到和Playwright的storageState类似的效果但操作上更麻烦一点而且对localStorage的处理没有原生支持。2.3 我的选型建议我在实际项目中会按任务类型做选型如果是临时调试、一次性的数据采集优先用Selenium的调试端口复用因为启动快、不用写额外的登录态保存代码手动扫码登录后就够了。如果是长期跑的自动化任务需要无人值守、重复执行优先用Playwright的storageState因为登录态数据落盘后可以反复加载稳定性高。如果项目里已经深度使用Selenium不想引入新工具那就用Selenium的cookie导出恢复方案虽然麻烦一点但也能达到目的。说实话如果你和我的场景类似——大部分时间在写爬虫和自动化脚本那么Playwright应该是更顺手的工具。它性能好API设计现代对登录态的处理几乎是开箱即用的。Selenium则更适合团队中已经存在大量基于它的历史代码的情况。3. 实操Playwright复用浏览器跳过扫码登录3.1 第一步手动登录并保存存储状态这个步骤的核心是先手动把登录流程走一遍让登录态写进浏览器上下文然后把上下文保存下来。我通常用一段临时脚本用Playwright启动一个带界面的Chromium浏览器然后打开目标网站停在登录页面等你自己扫码登录。登录成功后脚本会把当前上下文的存储状态写到文件中。from playwright.sync_api import sync_playwright with sync_playwright() as p: browser p.chromium.launch(headlessFalse) context browser.new_context() page context.new_page() page.goto(https://example.com/login) # 等你自己扫码登录手动按回车确认 input(扫码登录完成后按 Enter 键继续...) # 保存存储状态到本地文件 context.storage_state(pathstate.json) browser.close()这里有几个细节要注意。context.storage_state(pathstate.json)只保存cookie、localStorage和sessionStorage不保存其它浏览器数据比如IndexedDB或者Service Worker如果你的目标网站依赖这些数据只靠storageState可能不够这时候就要用launch_persistent_context配合user_data_dir。另外保存时确保没有开启无痕模式否则localStorage和cookie可能无法正常落盘。扫码之前最好先确认一下目标网站的登录态是否完全写入有些网站登录成功后还会异步刷新一次token如果你按回车太快可能保存的是登录前的状态。稳妥的做法是扫码后在页面上停留几秒或者随便点击一个需要登录才能访问的页面确认状态已经稳定。3.2 第二步加载存储状态启动上下文保存好state.json之后后续的自动化脚本就可以直接加载它启动即登录态。from playwright.sync_api import sync_playwright with sync_playwright() as p: browser p.chromium.launch(headlessTrue) context browser.new_context(storage_statestate.json) page context.new_page() page.goto(https://example.com/dashboard) # 如果正常跳转到个人中心说明登录态恢复成功 print(page.title()) browser.close()关键点在于browser.new_context(storage_statestate.json)这里。一行代码就把之前保存的所有登录态数据灌进了新的上下文。启动后页面就会自动带上登录凭证直接访问需要鉴权的页面。这里有一个容易被忽略的问题state.json文件里保存的cookie是有域名的只能在对应的域名下生效。如果你在脚本里访问的是https://example.com但登录操作发生在https://www.example.com那cookie可能不会完整匹配。遇到这种情况检查一下登录前后的URL确认它们属于同一个cookie domain或者保存状态时统一用主域名访问。另外如果你的网站登录态是依赖IP或者设备指纹的比如某些风控严格的银行类站点仅仅恢复cookie可能仍被判定为异常登录。这种场景下用launch_persistent_context恢复整个用户数据目录会更保险因为它连浏览器的指纹信息也一并恢复了。3.3 第三步处理登录态过期与自动续期任何登录态都有有效期复用浏览器方案不是一劳永逸的。几天不跑、接口改动、服务端session失效都会导致state.json里的状态变成废纸。我的做法是给登录态加一个“健康检查”和“自动重新登录”的包装逻辑。简单说就是每次启动脚本时先尝试访问一个只有登录才能访问的页面如果页面没有跳转去登录页说明登录态有效直接执行任务如果被重定向到登录页就先用非headless模式打开浏览器手动扫码一次重新生成state.json然后继续执行任务。def is_logged_in(page): page.goto(https://example.com/profile) return page.url https://example.com/profile def refresh_state(): with sync_playwright() as p: browser p.chromium.launch(headlessFalse) context browser.new_context() page context.new_page() page.goto(https://example.com/login) input(请扫码登录完成后按 Enter...) context.storage_state(pathstate.json) browser.close()我个人会把state.json当作缓存来处理不会在项目里硬编码它的路径。后续如果有多套环境比如测试环境和生产环境就用不同的文件路径来区分。另外一个非常实用的技巧是在扫码后检查一下服务端设置的cookie有效期。有的网站cookie有效期只有一小时有的却是三十天知道有效期之后你就可以预估什么时候需要重新扫码提前在调度系统里安排一次手动登录任务避免半夜任务因为登录态过期而全部失败。4. 实操Selenium复用已打开浏览器4.1 使用debug端口启动浏览器如果你还在用Selenium或者因为某些历史原因必须继续用Selenium也别急着推翻重来。Selenium复用浏览器的核心是通过Chrome的远程调试端口。第一步先以调试模式启动Chrome。在命令行里执行google-chrome --remote-debugging-port9222 --user-data-dir/tmp/chrome-profile注意--user-data-dir必须指定一个独立的目录不能用默认的用户目录否则Chrome会报错。这个目录相当于整个浏览器的“记忆”会保存你的登录态、扩展、设置。启动之后在浏览器里手动访问目标网站完成扫码登录。此时浏览器的所有状态都保存在/tmp/chrome-profile里。第二步用Selenium连接这个已打开的Chrome实例。这时不能直接用webdriver.Chrome()而是要通过ChromeOptions设置调试地址from selenium import webdriver options webdriver.ChromeOptions() options.debugger_address 127.0.0.1:9222 driver webdriver.Chrome(optionsoptions) driver.get(https://example.com/dashboard)只要连接成功driver控制的就是你已经登录的那个浏览器标签页直接就能访问需要鉴权的页面完全跳过扫码。4.2 通过WebDriver连接已有实例上面连接的过程很简单但有一个容易踩的坑是Selenium版本和Chrome版本的兼容性。如果chromedriver版本和Chrome版本不匹配连接调试端口时可能会报SessionNotCreatedException。我的建议是无论用Selenium 3还是Selenium 4都确保chromedriver和浏览器主版本号完全一致。还有一个细节是用调试端口方式连接时Selenium拿到的其实是一个已经存在的浏览器实例它不会帮你自动创建新的tab而是复用现有的页面。如果你希望在网站上打开多个标签页可以直接通过driver.switch_to.new_window()创建新窗口这些窗口依然继承同一个实例的登录态。如果你需要长期使用这个方案我还建议把启动Chrome调试模式和连接Selenium的步骤封装成两个独立的小模块。一个负责管理浏览器的生命周期一个负责执行具体业务脚本。这样即使浏览器意外崩溃你可以快速重启调试模式的Chrome而不需要重启整个自动化任务。4.3 注意事项与隐患Selenium调试端口复用方案虽然方便但有几个天然的隐患需要提前知道。首先是并发限制。同一个调试端口的浏览器实例同一时间只能被一个Selenium客户端连接。如果团队里多人共用一台机器的调试端口或者多个任务试图同时控制同一个浏览器会直接导致session冲突表现就是命令超时或自动断开。解决方案是每套任务用独立的端口和独立的--user-data-dir。其次是防火墙和权限问题。远程调试端口如果暴露在公网任何人都可以连上这个浏览器提交命令这是相当大的安全隐患。所以我强烈建议只在本机或内网使用并且不要在外网暴露9222端口。最后是浏览器进程的稳定性。手动打开的Chrome窗口要保证在整个自动化任务运行期间不被关掉。如果有人在物理机上不小心点了关闭按钮所有依赖这个实例的脚本都会瞬间失败。比较稳妥的做法是在服务器或专用的无人值守机器上跑这个方案并且设置屏保锁定防止人工误操作。5. 常见问题与排查技巧实录5.1 登录态丢失、过期怎么办这个问题几乎每个人都会遇到。很多人保存了state.json第二天跑脚本发现还是跳转到登录页了第一反应是“复用浏览器没用”。但大多数时候不是复用方案的问题而是目标网站的登录态本身就有效期限。排查思路是先确认一下state.json文件里cookies的数量和内容。如果cookies还在但里面某些关键字段比如sessionid、csrf_token、auth_token已经过期那说明是服务端把会话废掉了这不是保存格式的锅。常用的解决办法有三个一是缩短登录态的使用周期比如每天早上任务开始前用非headless模式重新扫码一次二是使用两个账号轮换一个失效自动切到另一个给运维留出人工处理的时间三是分析过期原因看看是否有接口在登录后异步刷新token如果有可以考虑在任务开始前先访问一次主页让站点把新token种进cookie再执行核心操作。5.2 复用失败、端口冲突或权限异常如果是Selenium调试端口方案最常见的错误是端口被占用或拒绝连接。先用netstat -ano | grep 9222看端口是否处于监听状态确认Chrome确实以调试模式启动了。如果端口正常但Selenium依然连接不上检查浏览器是否开启了代理或者有插件拦截网络请求这些都会干扰WebDriver与Chrome之间的管道通信。还有一点值得注意Selenium 4.x和3.x在debugger_address的写法上也有细微区别Selenium 4推荐直接在options.debugger_address里设置Selenium 3可能会识别不到这个属性需要额外传参。如果上述都排查了还是报错直接重启Chrome和脚本。有时候就是浏览器进程卡死了这种玄学问题优先用重启解决。5.3 多账号与多环境隔离如果你同时在管理多个账号千万别把所有账号的登录态都塞在同一个state.json里。那样的话账号A的登录会把账号B的登录顶掉而且不同账号的cookie混在一起会出现权限错乱的诡异问题。我的做法是强行按“账号”维度拆隔离。使用state_账号A.json、state_账号B.json这样的命名规范。每个任务启动前用独立的browser.new_context(storage_state对应的账号文件)。如果用的是Selenium调试端口方案每个账号分配独立的--user-data-dir和独立的端口。这样多账号批量操作时互相之间不会串数据调试排错也容易。5.4 安全与合规提醒这里单独说一句复用登录态虽然便利但也意味着你实际上是在“代理”一个真实用户的身份。如果你操作的是自己的账号那问题不大但如果操作的是他人的账号或者账号权限较高请务必遵守目标网站的用户协议和相关法律法规。不要用复用浏览器的能力去做任何踩红线的操作比如绕过访问控制、批量获取他人隐私数据、干扰正常业务流程等。我在项目中一直坚持的原则是复用登录态只用于自己有权访问的数据并且所有自动化操作都在合理频率范围内不要对目标站点造成压力。合理使用这套技术它是提效工具滥用它则是给自己挖坑。6. 我的实操体会与进一步扩展6.1 踩过最多的坑反而是“保存状态太早”有一次我写一套爬虫为了省事扫完码之后立刻保存state.json结果第二天脚本起来访问首页正常访问列表页却一直403。排查了半天发现登录后站点还会在后台发起一次鉴权请求把真正有效的access_token更新到localStorage里而我保存的状态是扫码后过早就保存的token还是旧值自然被服务端拒了。从那以后我养成了一个习惯扫码登录成功之后强制访问一次需要登录的页面等待1-2秒再执行storage_state导出。这个动作相当于把登录后的“会话成熟期”给等过去确保保存的是稳定可用的状态。6.2 每一步都可以继续自动化复用浏览器跳过扫码登录只是一个起点。当你掌握登录态持久化之后还可以继续把整条链路自动化登录态检测、登录态刷新、多账号调度、失败重试、数据上报完全可以组装成一套无人值守的自动化工作流。我目前在项目里就是把这个方案嵌入到定时任务系统中每天通过API接口触发采集任务任务启动时检查登录态有效期如果即将过期自动调用一个需要人工介入一次的刷新流程刷新完成后继续跑任务。最后再分享一个小技巧保存的state.json文件里很多cookie带有expires字段你可以写一个小脚本定期扫描所有cookie的过期时间在登录态失效前提前预警。这样你就不用等到脚本报错时才去重扫而是能提前规划人工扫码窗口让整个自动化体系看起来就像“永续在线”一样顺畅。