Selenium获取Cookie实现接口自动化登录:破解验证码与JS加密

发布时间:2026/8/5 16:40:52
Selenium获取Cookie实现接口自动化登录:破解验证码与JS加密 1. 项目概述当接口自动化遇上Selenium的Cookie在接口自动化测试的圈子里提到Selenium很多人的第一反应是UI自动化。确实Selenium以其强大的浏览器操控能力在模拟用户点击、输入、滚动等操作上无可替代。但今天我想聊的是Selenium在接口自动化领域一个非常巧妙且实用的“副业”——获取和管理Cookie。这听起来可能有点“跨界”但当你面临需要处理复杂登录态、验证码绕不过去或者目标接口的鉴权逻辑严重依赖浏览器环境时这个“跨界”组合拳往往能打出奇效。简单来说我们利用Selenium模拟真人登录拿到关键的登录凭证Cookie再将这些Cookie无缝注入到接口请求中从而实现自动化脚本的持久化登录和状态保持。这不仅仅是获取几个字符串而是打通UI与API两个世界构建更健壮、更真实的自动化流程的关键一步。2. 核心思路为什么用Selenium拿Cookie在深入代码之前我们必须先理清一个核心问题为什么放着专门的HTTP客户端库如requests,httpx不用要“大费周章”地启动一个浏览器答案在于登录流程的复杂性和真实性。2.1 传统接口登录的瓶颈对于简单的用户名密码登录用requests直接POST一个登录接口拿到返回的Cookie或Token是最直接高效的方式。然而现实中的登录系统远比这复杂图形验证码这是最直接的壁垒。纯接口请求无法解析图片内容虽然存在OCR方案但识别率、稳定性和反爬对抗都是问题。动态令牌/短信验证码需要二次交互逻辑复杂。复杂的JS加密登录请求的密码或关键参数可能被前端JavaScript进行非对称加密如RSA、混淆或生成动态签名。逆向JS代码成本高且对方一更新就可能失效。OAuth/SSO单点登录流程涉及多次重定向和状态传递纯接口模拟容易在重定向链条中丢失关键信息。登录行为校验一些风控严格的系统会检测鼠标移动轨迹、点击速度、浏览器指纹等纯接口请求缺乏这些“人性化”特征。2.2 Selenium的破局之道Selenium的核心价值在于它提供了一个真实、完整、可编程的浏览器环境。在这个环境里验证码虽然不能自动识别但我们可以让脚本暂停等待人工识别并输入。获取到Cookie后后续的接口请求就不再需要验证码了。JS加密与执行浏览器会自动加载并执行所有JavaScript代码完成密码加密、参数生成等操作。我们无需关心加密逻辑只需定位元素并操作。完整流程模拟无论是OAuth跳转还是多步表单Selenium都能像真人一样完整地走完整个登录流程并最终停留在登录后的页面此时所有的登录态Cookie都已就绪。行为真实性配合ActionChains等工具可以模拟更自然的操作一定程度上绕过简单的行为检测。因此我们的核心思路就变成了“UI自动化负责攻坚登录接口自动化负责扫荡后续业务请求”。用Selenium这个“特种兵”打开城门获取Cookie然后让轻量级的接口自动化“大部队”长驱直入。2.3 Cookie、Session与Token的简要辨析在操作前明确我们获取的对象很重要。这里简单厘清三者的关系便于理解后续操作Cookie本质是浏览器端存储的一小段文本数据由服务器通过Set-Cookie响应头下发浏览器随后在每次请求同类域名时自动通过Cookie请求头携带。它是实现有状态会话最经典、最基础的机制。我们通过Selenium获取的正是这个。Session更多是服务器端的概念。服务器为每个会话创建一个存储空间Session对象并用一个唯一的Session ID来标识。这个Session ID通常就是通过Cookie名为JSESSIONID,PHPSESSID等传递给浏览器的。所以获取了Cookie往往就拿到了通往服务器端Session的“钥匙”。Token一种更现代的鉴权方式如JWT。Token通常也通过响应体如JSON或响应头如Authorization返回可能也会被前端存储在localStorage或sessionStorage然后由前端代码手动添加到后续请求的Header里。Selenium也能执行JS来获取这些存储的内容。注意我们的主要目标是Cookie因为它是浏览器自动管理的获取后可直接用于requests等库。如果目标系统使用Token且存在Cookie中例如auth_token那我们的方法同样适用。如果Token存在localStorage则需要通过Selenium执行JavaScript来获取。3. 环境准备与核心工具链工欲善其事必先利其器。这个方案需要一套明确的工具组合。3.1 Python环境与核心库首先确保你的Python环境建议3.8已安装以下库pip install selenium requests webdriver-managerselenium核心浏览器自动化库。requests轻量级HTTP库用于后续的接口请求。webdriver-manager一个非常实用的工具可以自动下载和管理不同浏览器的驱动如ChromeDriver省去手动下载和配置PATH的麻烦。3.2 浏览器与驱动选择浏览器推荐使用Chrome或Edge。它们用户基数大兼容性好Selenium支持最完善。Firefox亦可。驱动管理强烈推荐使用webdriver-manager。以前我们需要根据浏览器版本去官网找对应的chromedriver现在一行代码就能搞定它会自动匹配版本。from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager from webdriver_manager.core.os_manager import ChromeType # 自动下载并使用ChromeDriver service Service(ChromeDriverManager().install()) driver webdriver.Chrome(serviceservice)对于Microsoft Edge (Chromium版)from webdriver_manager.microsoft import EdgeChromiumDriverManager service Service(EdgeChromiumDriverManager().install()) driver webdriver.Edge(serviceservice)3.3 项目结构规划一个清晰的项目结构有助于维护。建议如下your_project/ ├── config/ │ └── settings.py # 配置文件存放URL、账号密码、超时时间等 ├── core/ │ ├── cookie_manager.py # Cookie获取与管理的核心类 │ └── api_client.py # 封装了带Cookie的requests请求类 ├── test_cases/ # 接口测试用例 ├── utils/ │ └── common.py # 通用工具函数 ├── logs/ # 日志目录 ├── main.py # 主执行入口 └── requirements.txt # 依赖列表4. 实战Selenium获取Cookie全流程解析现在我们进入最核心的实操环节。我将以一个需要登录的虚构网站example.com为例演示完整流程。4.1 初始化浏览器并执行登录第一步是启动浏览器并完成登录操作。这里的关键是稳定性和容错性。from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException, NoSuchElementException import time import json class CookieManager: def __init__(self, headlessFalse): 初始化Cookie管理器 :param headless: 是否使用无头模式不显示浏览器界面 self.driver None self.headless headless self.wait_timeout 30 # 显式等待超时时间 def init_driver(self): 初始化WebDriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.chrome.options import Options chrome_options Options() if self.headless: chrome_options.add_argument(--headless) # 无头模式适合服务器环境 chrome_options.add_argument(--disable-gpu) chrome_options.add_argument(--no-sandbox) # Linux环境有时需要 chrome_options.add_argument(--disable-dev-shm-usage) # 解决共享内存问题 chrome_options.add_argument(--window-size1920,1080) # 可选禁用图片加载以加速 # prefs {profile.managed_default_content_settings.images: 2} # chrome_options.add_experimental_option(prefs, prefs) service Service(ChromeDriverManager().install()) self.driver webdriver.Chrome(serviceservice, optionschrome_options) self.driver.implicitly_wait(10) # 隐式等待全局生效但不如显式等待精准 print(浏览器驱动初始化成功。) def login_and_get_cookie(self, login_url, username, password): 执行登录并获取Cookie :param login_url: 登录页面URL :param username: 用户名 :param password: 密码 :return: 获取到的Cookie字典列表或None if not self.driver: self.init_driver() try: print(f正在访问登录页面: {login_url}) self.driver.get(login_url) # 使用显式等待确保关键元素加载完成 wait WebDriverWait(self.driver, self.wait_timeout) # 示例定位用户名、密码输入框和登录按钮需根据实际网站修改 # 这里以常见的name属性为例实际应用中请使用浏览器的开发者工具F12查看元素 username_input wait.until( EC.presence_of_element_located((By.NAME, username)) ) password_input self.driver.find_element(By.NAME, password) login_button self.driver.find_element(By.XPATH, //button[typesubmit]) # 清空输入框并输入信息 username_input.clear() username_input.send_keys(username) time.sleep(0.5) # 短暂等待模拟真人输入间隔 password_input.clear() password_input.send_keys(password) time.sleep(0.5) # 处理可能的验证码这里需要人工干预或集成OCR本例暂停等待手动输入 # 你可以在这里添加一个input()暂停或者调用你的OCR识别函数 # input(如果页面有验证码请手动输入后按回车继续...) print(正在点击登录按钮...) login_button.click() # 等待登录成功后的页面加载或某个成功标识出现 # 例如等待用户头像或登录后的特定元素出现 success_element wait.until( EC.presence_of_element_located((By.ID, user-avatar)) # 假设登录后头像ID为user-avatar ) print(登录成功) # 关键步骤获取当前所有Cookie cookies self.driver.get_cookies() print(f成功获取到 {len(cookies)} 个Cookie。) return cookies except TimeoutException as e: print(f登录过程中元素等待超时: {e}) # 可以在这里截图便于排查问题 self.driver.save_screenshot(login_timeout.png) return None except NoSuchElementException as e: print(f未找到页面元素: {e}) self.driver.save_screenshot(element_not_found.png) return None except Exception as e: print(f登录过程发生未知错误: {e}) return None实操心得与注意事项元素定位是成败关键By.NAME,By.ID,By.XPATH,By.CSS_SELECTOR都是常用方法。优先使用ID和NAME因为它们通常最稳定。XPATH功能强大但可能随页面结构微调而失效。务必使用浏览器的开发者工具F12 - Elements仔细检查元素属性。显式等待优于隐式等待和time.sleepWebDriverWait配合expected_conditions是更智能的等待方式它只在条件满足时才继续避免了固定时间睡眠的低效和不稳定。time.sleep仅建议在模拟人类操作间隔或等待非元素条件时少量使用。无头模式Headless在服务器或CI/CD环境中运行时可以启用无头模式。但有些网站会检测无头浏览器并拒绝服务此时可能需要添加一些参数来“伪装”chrome_options.add_argument(--disable-blink-featuresAutomationControlled) chrome_options.add_experimental_option(excludeSwitches, [enable-automation]) chrome_options.add_experimental_option(useAutomationExtension, False)登录成功判定等待一个登录后才会出现的元素如用户菜单、欢迎语而不是简单地等待几秒钟。这是判断登录是否真正成功的可靠方法。4.2 Cookie的解析、筛选与持久化存储driver.get_cookies()返回的是一个列表列表中的每个元素是一个字典包含了Cookie的name,value,domain,path,expiry等信息。我们通常需要将其转换为requests库能直接使用的字典格式并可能进行筛选和保存。def process_cookies_for_requests(self, selenium_cookies): 将Selenium获取的Cookie列表转换为requests库可用的字典格式。 同时可以根据domain和path进行筛选。 :param selenium_cookies: driver.get_cookies()返回的列表 :return: 用于requests的Cookie字典 requests_cookies {} target_domain .example.com # 你的目标域名 for cookie in selenium_cookies: # 筛选特定域名的Cookie可选但推荐 if target_domain in cookie.get(domain, ): requests_cookies[cookie[name]] cookie[value] # 如果不筛选直接全部加入 # requests_cookies[cookie[name]] cookie[value] print(f已处理并筛选出 {len(requests_cookies)} 个用于请求的Cookie。) return requests_cookies def save_cookies_to_file(self, cookies, file_pathcookies.json): 将Cookie列表保存到JSON文件 with open(file_path, w, encodingutf-8) as f: # 注意直接保存selenium获取的完整cookie信息包含过期时间等 json.dump(cookies, f, indent2, ensure_asciiFalse) print(fCookie已保存至文件: {file_path}) def load_cookies_from_file(self, file_pathcookies.json): 从JSON文件加载Cookie列表 try: with open(file_path, r, encodingutf-8) as f: cookies json.load(f) print(f从文件 {file_path} 加载了 {len(cookies)} 个Cookie。) return cookies except FileNotFoundError: print(fCookie文件 {file_path} 不存在。) return None def add_cookies_to_browser(self, cookies): 将保存的Cookie添加到当前浏览器会话用于恢复登录状态 # 注意添加Cookie前必须先访问该Cookie所属的域名 if cookies and self.driver: # 通常取第一个cookie的domain或使用一个基础域名 base_domain cookies[0].get(domain, ).lstrip(.) self.driver.get(fhttps://{base_domain}) for cookie in cookies: # 删除可能引起问题的httpOnly等字段因为add_cookie时不接受 cookie_to_add {k: v for k, v in cookie.items() if k in [name, value, domain, path, expiry, secure, sameSite]} try: self.driver.add_cookie(cookie_to_add) except Exception as e: print(f添加Cookie {cookie.get(name)} 时出错: {e}) print(Cookie已添加到浏览器。) # 刷新页面使Cookie生效 self.driver.refresh()关键点解析格式转换requests发送请求时cookies参数需要一个简单的{‘name’: ‘value’}字典。而Selenium的Cookie字典包含更多元数据如expiry,httpOnly。我们的process_cookies_for_requests函数完成了这个提取工作。持久化存储将Cookie保存为JSON文件可以避免每次运行脚本都重新登录。特别是对于登录会话有效期expiry很长的网站可以极大提升自动化效率。Cookie过期与刷新保存Cookie时一定要保存expiry过期时间戳。在使用前需要检查Cookie是否过期。如果过期则需要重新执行登录流程。可以写一个简单的检查函数def is_cookie_valid(cookie): expiry cookie.get(expiry) if expiry: return expiry time.time() # expiry是Unix时间戳 return True # 如果没有expiry认为是会话Cookie浏览器关闭即失效文件存储意义不大4.3 将Cookie注入接口请求拿到处理好的Cookie字典后将其用于接口请求就非常简单了。import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry class ApiClient: def __init__(self, base_url, cookiesNone): self.base_url base_url.rstrip(/) self.session requests.Session() # 配置重试策略提升请求稳定性 retry_strategy Retry( total3, # 总重试次数 backoff_factor1, # 退避因子 status_forcelist[429, 500, 502, 503, 504], # 遇到这些状态码重试 ) adapter HTTPAdapter(max_retriesretry_strategy) self.session.mount(http://, adapter) self.session.mount(https://, adapter) # 设置通用请求头模拟浏览器 self.session.headers.update({ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Accept: application/json, text/plain, */*, Accept-Language: zh-CN,zh;q0.9,en;q0.8, }) if cookies: self.update_cookies(cookies) def update_cookies(self, cookies_dict): 更新会话的Cookie # requests的Session.cookies是一个RequestsCookieJar对象可以用update方法合并 self.session.cookies.update(cookies_dict) print(API客户端Cookie已更新。) def get(self, endpoint, **kwargs): url f{self.base_url}{endpoint} return self._request(GET, url, **kwargs) def post(self, endpoint, dataNone, jsonNone, **kwargs): url f{self.base_url}{endpoint} return self._request(POST, url, datadata, jsonjson, **kwargs) def _request(self, method, url, **kwargs): try: response self.session.request(method, url, **kwargs) response.raise_for_status() # 如果状态码不是200抛出HTTPError异常 return response except requests.exceptions.RequestException as e: print(f请求失败: {method} {url} - {e}) # 这里可以添加更详细的日志记录 raise使用示例# 主程序流程 if __name__ __main__: # 1. 初始化Cookie管理器并登录 cm CookieManager(headlessFalse) # 调试时可关闭无头模式 fresh_cookies cm.login_and_get_cookie( login_urlhttps://example.com/login, usernameyour_username, passwordyour_password ) if fresh_cookies: # 2. 处理Cookie并保存 usable_cookies cm.process_cookies_for_requests(fresh_cookies) cm.save_cookies_to_file(fresh_cookies, cookies.json) # 3. 初始化API客户端并使用Cookie client ApiClient(base_urlhttps://api.example.com, cookiesusable_cookies) # 4. 执行需要登录态的接口请求 try: # 获取用户信息 resp client.get(/user/profile) print(f用户信息: {resp.json()}) # 提交某项操作 data {action: like, item_id: 123} resp client.post(/action/submit, jsondata) print(f操作结果: {resp.status_code}) except Exception as e: print(f接口请求过程中出错: {e}) else: print(登录失败无法获取Cookie。) # 5. 记得关闭浏览器 if cm.driver: cm.driver.quit()5. 高级技巧与疑难问题排查掌握了基础流程后我们来看看一些进阶场景和常见坑点。5.1 处理动态加载与SPA应用现代前端多为单页应用SPA页面内容通过JavaScript动态加载。这可能导致问题使用presence_of_element_located等待的元素可能早已存在于DOM但内容为空或未绑定事件。解决方案使用更精确的等待条件如element_to_be_clickable等待元素可点击或visibility_of_element_located等待元素可见。对于数据由API异步加载的情况可能需要等待某个特定文本内容出现。# 等待元素可见并可交互 wait.until(EC.visibility_of_element_located((By.ID, dynamic-content))) # 等待元素包含特定文本 wait.until(EC.text_to_be_present_in_element((By.CLASS_NAME, status), 加载完成))5.2 Cookie的作用域与SameSite属性这是最容易出错的地方之一。Domain PathCookie有作用域。domain.example.com的Cookie对api.example.com和www.example.com都有效而domainapi.example.com的Cookie对www.example.com无效。我们的筛选和处理逻辑需要根据实际请求的域名来定。SameSite属性这是现代浏览器为了安全引入的。SameSiteLax默认或Strict的Cookie在跨站请求如从你的脚本直接发起到API的请求时不会被发送。如果接口请求失败且Cookie看似已设置检查浏览器开发者工具Application - Cookies中Cookie的SameSite值。影响通过Selenium在浏览器中生成的Cookie如果被标记为SameSiteLax/Strict当你用requests从另一个“源”origin发起请求时浏览器不会携带这些Cookie。但在我们这个场景下requests是直接发送HTTP请求不经过浏览器所以不受SameSite限制。这是一个重要的优势然而如果服务器端校验请求来源Origin或Referer头则需要确保requests发送的请求头与之匹配。5.3 登录状态维持与Cookie刷新会话Cookie没有设置expiry的Cookie是会话Cookie浏览器关闭即失效。保存这类Cookie到文件意义不大因为下次启动新浏览器会话时它已无效。定期刷新实现一个CookiePool类管理多个账号的Cookie并定期检查有效性。对于失效的Cookie自动触发重新登录流程。多账号处理如果需要模拟多个用户可以为每个用户创建独立的Selenium WebDriver实例和Cookie文件。注意资源管理及时quit()不再使用的driver。5.4 常见问题排查速查表问题现象可能原因排查步骤与解决方案登录成功但获取的Cookie为空或很少1. 页面未完全加载。2. 登录实际未成功如跳转错误。3. Cookie是HttpOnly且对JavaScript不可见driver.get_cookies()能获取。1. 增加显式等待确保登录后页面元素出现。2. 登录后截图并打印当前URL确认跳转正确。3. 使用driver.get_cookies()是能获取HttpOnly Cookie的此原因概率低。保存的Cookie下次无法使用1. Cookie已过期。2. 保存的是会话Cookie。3. 添加Cookie前未访问正确域名。1. 检查保存的expiry时间戳实现过期校验逻辑。2. 优先使用持久化Cookie有expiry。3. 使用add_cookies_to_browser时务必先get到对应域名下的页面。requests使用Cookie后接口仍返回未登录1. Cookie作用域domain/path不匹配。2. 接口鉴权方式不是Cookie可能是Token在Header。3. 服务器校验了其他信息如User-Agent, Referer。1. 核对requests请求的URL域名与Cookie的domain是否匹配。2. 登录后用浏览器开发者工具的Network面板查看真正的API请求看鉴权信息放在哪里Cookie头还是Authorization头。3. 在ApiClient中模拟完整的请求头特别是User-Agent和Referer如果需要。Selenium操作被网站检测到网站使用了反爬措施检测自动化工具。1. 使用undetected-chromedriver等更隐蔽的驱动。2. 添加更多Chrome选项进行伪装如禁用enable-automation。3. 在操作中增加随机延迟模拟人类行为。无头模式下登录失败某些网站屏蔽或无头模式。1. 尝试禁用无头模式(headlessFalse)看是否成功。2. 在无头模式下添加额外的chrome_options进行伪装见4.1节。6. 集成到自动化测试框架最后我们将这套机制集成到更专业的自动化测试框架中例如pytest。# conftest.py (pytest配置文件) import pytest from core.cookie_manager import CookieManager from core.api_client import ApiClient import json import os pytest.fixture(scopesession) def global_cookies(): 会话级Fixture全局只登录一次并获取Cookie cookie_file cookies.json cm CookieManager(headlessTrue) # 测试环境通常用无头 # 尝试从文件加载 saved_cookies cm.load_cookies_from_file(cookie_file) usable_cookies None if saved_cookies: # 简单检查第一个Cookie是否过期这里应实现更完整的检查 if saved_cookies[0].get(expiry, 0) time.time(): print(使用已保存的有效Cookie。) usable_cookies cm.process_cookies_for_requests(saved_cookies) else: print(保存的Cookie已过期需要重新登录。) if not usable_cookies: print(开始执行登录流程获取新Cookie...) cm.init_driver() fresh_cookies cm.login_and_get_cookie( login_urlos.getenv(LOGIN_URL), usernameos.getenv(USERNAME), passwordos.getenv(PASSWORD) ) if fresh_cookies: cm.save_cookies_to_file(fresh_cookies, cookie_file) usable_cookies cm.process_cookies_for_requests(fresh_cookies) else: pytest.fail(无法获取登录Cookie测试终止。) cm.driver.quit() yield usable_cookies # 将可用的Cookie字典提供给测试用例 # 测试会话结束后的清理工作如果需要 pytest.fixture def api_client(global_cookies): 为每个测试用例提供一个已携带Cookie的API客户端 client ApiClient(base_urlos.getenv(BASE_API_URL), cookiesglobal_cookies) yield client # 用例执行后的清理如重置测试数据 # test_user_profile.py def test_get_user_profile(api_client): 测试获取用户信息接口 response api_client.get(/user/profile) assert response.status_code 200 data response.json() assert username in data assert email in data print(f用户 {data[username]} 信息获取成功。) def test_update_user_preferences(api_client): 测试更新用户偏好设置 update_data {theme: dark, language: zh-CN} response api_client.post(/user/preferences, jsonupdate_data) assert response.status_code 200 assert response.json().get(message) Preferences updated successfully通过pytest的fixture机制我们将Cookie获取和客户端初始化封装起来实现了一次登录多次复用所有测试用例都能在一个稳定的登录态下运行大大提升了测试套件的执行效率和可靠性。

相关新闻