免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

Selenium绕过验证码:Python与Cookie复用实现自动化登录态保持

Selenium绕过验证码:Python与Cookie复用实现自动化登录态保持 简介面向 Web 自动化测试初学者的实战文档以 TPshop 商城为被测项目重点讲解如何通过 cookies 绕过验证码从而顺利执行登录后的搜索、选品、加购与结算等自动化流程。文档采用 Python Selenium Chrome 技术栈详细覆盖浏览器驱动实例化、add_cookie 注入会话信息、刷新页面验证会话、元素定位与鼠标键盘操作、隐式等待以及处理 alert 弹窗和切换 iframe 等高频实践点并配有可直接跟练的完整代码片段。资源总量为 1 个 PDF压缩包大小约 92KB内容紧凑但步骤清晰适合 Selenium 入门者对照学习也可供测试人员借鉴到电商类 Web 项目。已有 1435 人学习浏览在自动化测试实战类资源中具有一定参考热度。通过该文档可掌握真实商城场景下验证码绕过、复杂交互模拟和购物车结算链路自动化的实现思路对提升 Web UI 自动化测试的编码能力和排错能力都有实际帮助。 跑自动化测试最烦的不是脚本不稳定也不是定位不到元素而是你每天早上打开电脑准备跑 TPshop 回归用例时脚本停在登录页那个图形验证码上。TPshop 这套开源商城用 ThinkPHP 框架搭建登录页的验证码看起来就是四位数字字母对真人来说是几秒钟的事但 Selenium 要硬识别它等于你平白无故先写一套 OCR 流程。我在系列第一篇里解决了环境安装和元素定位的问题到了第二篇真正挡住批量执行的第一座山就是验证码。这篇我重点聊聊怎么用 python selenium chrome 的 cookie 机制绕过验证码让脚本启动后直接带着已登录状态跑业务流。1. TPshop自动化跑不过去八成卡在验证码这一关1.1 验证码卡住的不只是脚本是整个回归效率先想清楚一个问题验证码到底是什么它本质上是服务端设置的一道“人机判断题”TPshop 生成一张带有干扰线的图片把答案存进 session。你提交登录表单时服务端拿表单里的验证码和 session 里存的值做比对一致才允许继续认证账号密码。问题就在这里验证码是否通过不是由浏览器决定的而是由服务端 session 决定的。Selenium 每启动一个全新的 Chrome 实例它的 session 是全新的自然没有“已验证过验证码”的标记。所以脚本一旦走到登录页就必须面对这个验证码。识别它干扰线、噪点、字体扭曲每套模板都要单独调参改一次验证码样式你的脚本就废一次。打码平台花钱是一回事每次请求还要等平台返回批量跑的时候延迟和费用都不可忽略。最务实的做法是绕过验证码本身直接复用你人工登录成功后拿到的那一份 session 凭证。这份凭证长什么样就是 cookie 里的 PHPSESSID 等关键字段。说白了验证码这道门你已经用真人身份打开过一次了把开门后发的那串钥匙保存下来以后每次启动脚本直接把钥匙递上去服务端就默认你是已经通过验证码的人。1.2 三条路对比识别、打码、绕道我把自己试过的方案整理了一张表方便你对着选方案原理优点缺点适用场景图像识别pytesseract OCR OpenCV 预处理零外部依赖纯本地识别率 60%~80%模板一改就失效验证码极其简单的临时脚本打码平台第三方接口识别并返回识别率高接入简单按次收费有网络延迟验证码复杂且无法绕过的场景cookies 复用保存登录成功后的 cookie注入新会话零成本、秒级生效有有效期需要保存和刷新机制自有系统的接口回归、UI 回归三条路里cookies 方案是在“自有测试环境”里性价比最高的而且它不依赖验证码长得什么样。哪怕 TPshop 把验证码从四位数字升级成滑块只要登录入口还吃 session这套思路就依然成立。2. 从真实浏览器里顺出cookie两种取证姿势2.1 姿势一DevTools手动导出救急够用最快捷的方法是纯手工操作。你先用 Chrome 打开 TPshop 登录页人工输入账号密码识别验证码并登录成功。然后按 F12 打开 DevTools切到 Application 面板左侧选 Cookies点击你的站点域名右边就能看到这个域名下的完整 cookie 列表。这时候需要把 Name、Value、Domain、Expires 这些字段一个一个复制出来。说实话这种方式挺笨的因为 cookie 数量一多你就容易漏字段、复制错值整个过程大概要花五六分钟。它适合你只是临时想调试一下脚本能不能用 cookie 维持登录态不适合需要反复取证的场景。2.2 姿势二Selenium半自动取证批量准备账号就靠它我更推荐的是第二种方式写一个取证脚本启动浏览器后你自己手动登录一次脚本在后台等你等你登录完成它自动把 cookie 拉下来存成 JSON 文件。import json from selenium import webdriver from selenium.webdriver.chrome.options import Options options Options() # detach 让浏览器窗口在脚本执行完毕后不自动关闭方便人工操作 options.add_experimental_option(detach, True) driver webdriver.Chrome(optionsoptions) driver.get(http://tp-shop.local/index.php/Home/User/login.html) # 阻塞等待人工登录 input(请在浏览器里完成登录操作成功后回到终端按回车键继续...) # 获取登录后的 cookie 列表 cookies driver.get_cookies() with open(tpshop_cookies.json, w, encodingutf-8) as f: json.dump(cookies, f, ensure_asciiFalse, indent2) print(f已保存 {len(cookies)} 个 cookie) driver.quit()这段代码里值得注意的有两点一是input()作为阻塞等待虽然朴素但极其稳你登录排查问题的时候不会被一个隐藏的隐式等待坑到二是get_cookies()会把该域名下当前会话里的所有 cookie 全部返回包括 HttpOnly 类型的 PHPSESSID也就是说它比document.cookie拿到的信息量更大。序列化到 JSON 后字段是完整的后续加载直接用就行。如果你希望连人工按回车都省掉可以把input()换成 Selenium 的显式等待等待登录后页面某一个“退出登录”按钮或“用户中心”链接出现一旦出现说明登录成功自动进入get_cookies()流程。两种做法都可以看你的耐心和场景。2.3 两份方案各自的适用场景我的习惯是临时调试用 DevTools需要正式跑回归前用 Selenium 半自动方式把各套测试账号的 cookie 都取一遍存成cookies_admin.json、cookies_user.json这类文件。这样后续脚本启动时按账号加载既绕过了验证码又实现了不同角色之间的切换一举两得。3. 往Selenium会话里种cookie代码拆解与三个关键动作3.1 第一个动作先get域名再add_cookie这是新手最容易踩的坑也是我要反复强调的一点add_cookie 之前必须先让浏览器访问过一次目标域名。你启动一个新的 Chrome 实例这个浏览器完全没有访问过 TPshop 的域名它不知道 cookie 该映射到哪个 host 上。如果你一上来就driver.add_cookie()大概率会抛InvalidCookieDomainException。正确顺序是driver.get(http://tp-shop.local/) # 先建立域名上下文 # 再 add_cookie哪怕这个 GET 请求得到的是一个 404 页面只要域名是对的它就给浏览器建立了“当前位于哪个域名”的认知。这一点不解决后面所有工作都白做。3.2 第二个动作expiry类型转换与多余字段清理从 JSON 文件里读回来的 cookie有一批字段需要处理。直接json.load()出来的字段expiry可能是浮点数但 Selenium 要求expiry是整数不少人会在这一步踩到TypeError。另外部分版本的 ChromeDriver 不认sameSite字段也需要兜底删掉。我总结了一份稳妥的加载代码import json import time from selenium import webdriver from selenium.webdriver.chrome.options import Options options Options() driver webdriver.Chrome(optionsoptions) # 1. 先建立域名上下文 driver.get(http://tp-shop.local/) # 2. 读取 cookie 文件 with open(tpshop_cookies.json, r, encodingutf-8) as f: cookies json.load(f) # 3. 清洗并写入 cookie for cookie in cookies: if expiry in cookie and cookie[expiry] is not None: cookie[expiry] int(cookie[expiry]) # 不兼容字段直接移除 cookie.pop(sameSite, None) cookie.pop(storeId, None) try: driver.add_cookie(cookie) except Exception as e: print(f写入 cookie 失败: {cookie.get(name)}, 原因: {e}) # 4. 刷新页面让服务端收到带 cookie 的请求 driver.refresh() time.sleep(2)其中storeId是我在调试某个系统时遇到过的多余字段Selenium 会报警告所以我习惯统一弹掉。你本地如果没遇到不删也没关系关键是expiry的整型转换不能省。3.3 第三个动作refresh触发服务端会话校验有人会问add_cookie 都加进去了为什么页面还是没登录状态因为add_cookie只是把 cookie 写进浏览器服务端根本不知道你干了什么。你必须再发起一次 HTTP 请求让浏览器把这些 cookie 带上服务端看到 PHPSESSID 后才会返回已登录的页面内容。driver.refresh()就是干这事的。刷完页面浏览器带着一整包 cookie 重新请求 TPshop服务端从 session 里找到对应的登录态登录后的页头才会出现用户信息、退出按钮这些东西。如果刷完还是登录页说明 cookie 里的 PHPSESSID 在服务端已经失效需要重新取证。4. cookie总有过期的一天登录态保活与复用策略4.1 先搞明白session和cookie的锁钥关系cookie 不是内存里的数据它是浏览器帮你保存的钥匙串。TPshop 那边PHP 会为每个会话生成一个 PHPSESSID服务器把这个 id 和对应的 session 数据存起来可能落在文件里也可能落在 Redis 里。你在浏览器里完成登录后服务端就会在这个 session 里写入“已登录、用户 id 是多少”这些状态。所以 cookie 里的 PHPSESSID 只是一把钥匙门后面的锁和服务端 session 数据是有生命周期的。PHP 的 session 过期时间默认不算长可能几十分钟也可能几个小时取决于gc_maxlifetime配置。钥匙再好锁被自动销毁了这把钥匙也就成了一块废铁。这也是为什么“存一次 cookie 永久使用”这种想法不现实。4.2 实操保活策略定时取证、失效重试、cookie池我在项目里实践下来有三条保活路线可以参考第一条是定时取证。每天早上跑回归前先执行一次第二小节的半自动取证脚本人工登录一次把 cookie 刷新到最新。费用最低、思路最简单适合个人开发环境。第二条是脚本内建失效重试。每个用例开始前先访问一个首页检查“退出”按钮是否出现。没出现就说明登录态失效了这时候自动跳回登录页弹出一个浏览器窗口让你人工补一次验证码补完自动重新保存 cookie 并继续执行之前的用例。它把人工介入压缩到了“只有失效时才需要人工”而且对团队内的非技术人员也很友好。第三条是 cookie 池。测试往往需要多个角色管理员、普通会员、分销商等等。在项目下建一个cookies/目录把不同账号的 cookie 文件分门别类存放。写一个通用函数传入站点地址和账号标识即可加载对应 cookie跨账号场景比如“A用户下单B用户审核”就能很方便地跑起来。4.3 跨账号场景下cookie池怎么组织这里给一个简单的目录和加载函数参考cookies/ admin.json buyer.json seller.jsondef load_cookies(driver, site, role): file_path fcookies/{role}.json driver.get(site) with open(file_path, r, encodingutf-8) as f: cookies json.load(f) for cookie in cookies: if expiry in cookie and cookie[expiry] is not None: cookie[expiry] int(cookie[expiry]) cookie.pop(sameSite, None) driver.add_cookie(cookie) driver.refresh()配合这个函数业务用例里直接调用比如跑一个“搜索商品-加入购物车-提交订单”的链路开头先加载 buyer 角色 cookie就能全程跳过验证码直接操作下单流程。整夜回归时脚本如果碰上 session 失效也会被提前拦截不至于半夜卡死在登录页。5. 踩坑实录cookie带上了却进不去排查链路5.1 现象一add_cookie直接抛InvalidCookieDomainException这个前面已经讲了原因这里说说排查动作。你看到这个异常时先确认当前 driver 实例是否已经get()过目标域名。如果没有补上这一步如果有再检查你拿到的 cookie 是不是确实属于当前这个域名。比如你在http://localhost取的 cookie换到http://127.0.0.1或http://tp-shop.local去加载就是两套完全不同的域名规则浏览器不认。5.2 现象二cookie加完刷新又跳回登录页这是 session 过期或关键 cookie 丢失的典型表现。首先检查 PHPSESSID 是否成功写入了浏览器可以在 add_cookie 之后打印一下driver.get_cookies()看看里面有没有 PHPSESSID。我之前排查过一个问题导出 cookie 时 PHPSESSID 的 expiry 字段是None重新组装 JSON 时被我不小心处理成空字符串导致 add_cookie 时报错或者压根没写进去。所以序列化和反序列化时对这个字段要做None判断别一刀切处理。5.3 现象三页面变了验证码还在如果加载 cookie 后不报错但刷新后依然停在登录页且验证码图片还在大概率有两种可能一种是你访问的 URL 和取证的 URL 不是同一套域名cookie 的 domain 匹配不上另一种是服务端把 session 给清了比如本地 PHP 配置的 session 目录权限有问题或者你切换了 TPshop 的应用环境。排查手段也很直接在浏览器里再人工登录一次登录成功后立刻用脚本读取当前 cookie人工保存一份到 JSON然后对比两次 JSON 的字段差异看看是少了 PHPSESSID还是多了其他校验字段。5.4 验证登录态的硬指标不要只靠“退出”两个字判断是否登录。我之前就被骗过一次“退出”按钮字样已经出现在首页 HTML 里但实际点击后才发现是未登录状态下的默认页脚链接。更稳妥的做法是访问一个必须登录才能看到的页面比如用户中心driver.get(http://tp-shop.local/index.php/Home/User/index.html) assert 我的订单 in driver.page_source只有这类断言通过才能确认服务端真的拿你当已登录用户看待。这套验证逻辑也建议封装成独立的is_logged_in(driver)函数供所有用例共用。5.5 逼急了还能上CDP极少数情况下get_cookies()拿到的 cookie 不够完整比如部分子域名的 cookie 或前端 HTTP-only 之外的特殊凭据这时可以借助 Chrome DevTools ProtocolCDP直接向浏览器发送Network.getCookies命令拿到更底层的 cookie 数据。Selenium 4 支持通过driver.execute_cdp_cmd调用这类命令。不过 TPshop 这种常规 PHP 应用的场景get_cookies()已经完全够用CDP 属于救火用的进阶手段。我自己把整套跑通之后最大的感触是绕过验证码这件事不能只当“写一段 add_cookie 的代码”来看它是一条完整的工程链路——取证、序列化、注入、保活、失效重试一环扣一环。哪一环断了都会把你打回登录页。按照这套思路跑起来TPshop 的回归用例基本能整夜挂着跑第二天早上只看结果报表就行。你要是把这个思路迁移到其他后台系统改动量也就是把登录 URL 和 cookie 文件路径换一下套路完全通用。本文还有配套的精品资源点击获取
返回列表