news 2026/7/4 15:15:12

Selenium WebDriver架构解析与Web自动化测试实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Selenium WebDriver架构解析与Web自动化测试实战指南

1. 项目概述:为什么Selenium依然是Web自动化的“定海神针”?

每次和测试开发团队的朋友聊天,只要提到Web自动化,Selenium这个名字几乎是绕不开的。从2010年前后开始接触它,到现在看着它从Selenium RC(Remote Control)进化到WebDriver,再到现在与W3C标准深度融合,它就像一位老朋友,始终站在Web自动化测试的舞台中央。很多人可能会问,现在有那么多新兴的框架和工具,比如Playwright、Cypress,Selenium是不是过时了?我的回答是:远没有。它更像是一个稳固的基石,一个生态,一个标准。理解Selenium,不仅仅是学会一个工具,更是理解Web自动化测试的底层逻辑和最佳实践。这篇内容,我想和你深入聊聊Selenium的“王者之路”,它凭什么能坐稳这个位置,以及我们如何在实际项目中,尤其是面对复杂、动态的现代Web应用时,真正用好它。

Selenium的核心价值,在于它的“协议层”定位。它不只是一个库,更是一套基于WebDriver协议的标准。这意味着,只要你遵循这套协议,你可以用Python、Java、JavaScript、C#等多种语言来编写脚本,去驱动Chrome、Firefox、Edge、Safari等几乎所有主流浏览器。这种跨语言、跨浏览器的能力,是很多后起之秀难以在短期内撼动的根基。对于企业级项目,技术栈可能多样,浏览器兼容性要求严格,Selenium提供的这种“统一接口”就显得至关重要。它解决的核心问题是:如何以编程方式,稳定、可靠地模拟真实用户在浏览器中的操作,并获取页面状态进行验证。无论是回归测试、数据抓取,还是日常的重复性Web操作任务,Selenium都是一个绕不开的强力选项。

2. Selenium WebDriver架构深度拆解:从协议到执行

要玩转Selenium,不能只停留在写find_elementclick的层面。理解其架构,才能在遇到诡异问题时,知道从哪里下手排查。

2.1 WebDriver协议:一切交互的基石

WebDriver协议本质上是一个基于HTTP的RESTful API,遵循W3C的WebDriver标准。这听起来有点抽象,我打个比方:你的自动化脚本(Client)就像一个指挥官,浏览器(Browser)就是你要指挥的士兵。但指挥官和士兵语言不通,怎么办?这时候就需要一个翻译官,这个翻译官就是浏览器驱动(Driver,如chromedriver、geckodriver)。指挥官(脚本)用WebDriver协议这种“国际通用语”下达命令(HTTP请求),翻译官(驱动)接收后,翻译成浏览器能听懂的“本地语言”(浏览器内部的调试协议,如Chrome DevTools Protocol),最终由士兵(浏览器)执行动作。

这个架构的精妙之处在于解耦。你的测试脚本完全不需要关心浏览器内部是如何实现点击、输入、执行JavaScript的。它只需要向一个固定的本地HTTP服务(通常是http://localhost:xxxx)发送格式化的JSON请求。例如,一个点击命令的请求体大致是这样的:

{ “script”: “return arguments[0].click();”, “args”: [{“element-6066-11e4-a52e-4f735466cecf”: “<element_id>”}] }

驱动收到后,会通过CDP等底层接口找到对应的DOM元素并触发点击事件。理解这一点,你就明白了为什么我们需要为每个浏览器下载对应的驱动,也明白了当浏览器升级后,驱动不匹配会导致各种奇怪错误的原因。

2.2 核心组件协作流程

一次典型的Selenium操作,其内部流程可以拆解为以下几步:

  1. 脚本初始化:在你的代码中,你实例化一个WebDriver对象,例如driver = webdriver.Chrome()。这行代码背后,会启动一个chromedriver.exe(或对应系统的可执行文件)进程。
  2. 驱动启动浏览器chromedriver进程会启动一个新的Chrome浏览器进程(或连接到已有的浏览器),并开启一个HTTP服务器,监听某个端口(如9515)。
  3. 建立会话:你的脚本向http://localhost:9515/session发送一个POST请求,携带创建新会话的配置(如浏览器选项chromeOptions)。驱动响应一个sessionId,后续所有针对这个浏览器窗口的操作,都会带上这个ID。
  4. 执行命令:当你调用driver.find_element(By.ID, “kw”).send_keys(“Selenium”)时,脚本库(如selenium包)会将这个调用转化为一个HTTP POST请求,发送到http://localhost:9515/session/{sessionId}/element(查找元素)和http://localhost:9515/session/{sessionId}/element/{elementId}/value(输入文本)。
  5. 驱动翻译与执行:驱动接收到请求,将其转化为浏览器内核能理解的底层命令,通过调试接口发送给浏览器。
  6. 响应返回:浏览器执行完毕,将结果(成功或失败,以及可能的返回值如元素ID)通过驱动返回给脚本。

这个过程是同步阻塞的。也就是说,send_keys方法会一直等待,直到收到HTTP响应确认输入完成,才会执行下一行代码。这保证了脚本步骤的顺序性,但也引出了异步操作和等待机制的重要性。

注意:很多人混淆了Selenium IDE(录制回放工具)、Selenium Grid(分布式执行)和Selenium WebDriver。我们通常说的“用Selenium做自动化”,核心指的是WebDriver。IDE适合快速生成简单脚本或学习,Grid用于大规模并发测试,而WebDriver是这一切的编程基础。

3. 元素定位策略进阶与稳定等待机制

元素定位是自动化脚本的“眼睛”,而等待机制则是保证“眼睛”在正确时间看到东西的“节奏控制器”。这两者做不好,脚本就会变得脆弱不堪。

3.1 超越基础的定位策略

By.ID,By.NAME,By.CLASS_NAME这些是基础。但在现代前端框架(如React, Vue)构建的应用中,ID可能动态生成,Name可能重复,Class可能是一长串哈希值。我们必须掌握更高级的策略:

  • CSS Selector:这是我最推荐的主力定位方式,功能强大且性能通常优于XPath。它足够应对大多数场景。
    • driver.find_element(By.CSS_SELECTOR, “button.primary[data-testid=’submit’]”)定位一个具有primary类且>from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 等待元素可见并可点击 element = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.ID, “dynamicButton”)) ) element.click() # 等待元素包含特定文本 WebDriverWait(driver, 10).until( EC.text_to_be_present_in_element((By.CLASS_NAME, “status”), “加载完成”) ) # 等待页面标题包含某个词 WebDriverWait(driver, 10).until( EC.title_contains(“订单详情”) )

      expected_conditions模块提供了大量预置条件,如元素存在、可见、可点击、被选中、窗口数量等。核心技巧:根据你的实际等待目标选择最精确的条件。等“可点击”比等“存在”更好,因为元素可能存在但被遮挡或禁用。

    • 流畅等待(Fluent Wait):这是显式等待的增强版,可以自定义轮询频率和忽略的异常类型。在Python中,通过自定义WebDriverWaitpoll_frequencyignored_exceptions参数实现。

      wait = WebDriverWait(driver, timeout=30, poll_frequency=1, ignored_exceptions=[StaleElementReferenceException]) element = wait.until(EC.presence_of_element_located((By.ID, “slow-element”)))

      这对于加载特别慢或偶尔会抛出无关紧要异常的元素非常有用。

    • 一个黄金实践永远不要使用time.sleep(),除非是在调试或者等待一个与DOM无关的外部事件(如等待文件上传完成)。time.sleep()是固定死等,无论页面是否就绪,它都会阻塞指定的时间,这会导致测试效率极低且不稳定。

      4. 高级交互与复杂场景实战

      掌握了定位和等待,我们就可以挑战更复杂的用户交互场景了。这些是让脚本从“能跑”到“健壮”的关键。

      4.1 处理弹窗、Alert和多个窗口/标签页

      • JavaScript Alert/Confirm/Prompt

        # 切换到alert alert = driver.switch_to.alert # 获取文本 print(alert.text) # 接受(点击“确定”) alert.accept() # 驳回(点击“取消”) # alert.dismiss() # 对于Prompt,可以输入文本 # alert.send_keys(“输入内容”)

        关键点:操作alert后,焦点会自动回到主页面。如果后续操作还需要处理其他alert,需要重新获取。

      • 新窗口/标签页

        # 点击一个会打开新窗口的链接 main_window = driver.current_window_handle # 保存当前窗口句柄 driver.find_element(By.LINK_TEXT, “在新窗口打开”).click() # 获取所有窗口句柄 all_windows = driver.window_handles new_window = [window for window in all_windows if window != main_window][0] # 切换到新窗口 driver.switch_to.window(new_window) # 在新窗口进行操作... # 操作完毕后,切换回主窗口 driver.switch_to.window(main_window)

        常见坑:新窗口可能加载较慢,切换后最好加上显式等待,确保新页面元素加载完成再操作。

      4.2 文件上传与下载

      • 文件上传:对于<input type=”file”>元素,直接使用send_keys传入文件绝对路径即可。千万不要尝试用click()去触发系统文件选择框,那是操作系统级别的对话框,Selenium无法控制。

        upload_element = driver.find_element(By.ID, “file-upload”) upload_element.send_keys(“/Users/yourname/Desktop/test_image.jpg”)
      • 文件下载:这需要配置浏览器选项。以下以Chrome为例,设置下载路径并禁止下载弹窗:

        from selenium import webdriver from selenium.webdriver.chrome.options import Options chrome_options = Options() prefs = { “download.default_directory”: “/path/to/your/download/folder”, # 设置下载路径 “download.prompt_for_download”: False, # 禁止下载弹窗 “download.directory_upgrade”: True, “safebrowsing.enabled”: True } chrome_options.add_experimental_option(“prefs”, prefs) driver = webdriver.Chrome(options=chrome_options)

        下载后,你可以通过检查下载目录下的文件是否存在、文件名是否正确来验证。

      4.3 执行JavaScript与处理Shadow DOM

      • 执行JavaScript:这是Selenium的“王牌”功能之一,可以完成WebDriver API无法直接实现的操作。

        # 滚动到页面底部 driver.execute_script(“window.scrollTo(0, document.body.scrollHeight);”) # 滚动到某个元素 element = driver.find_element(By.ID, “target”) driver.execute_script(“arguments[0].scrollIntoView(true);”, element) # 修改元素属性(例如,让一个隐藏的元素可见,用于测试) driver.execute_script(“document.getElementById(‘hidden’).style.display = ‘block’;”) # 获取页面性能数据 load_time = driver.execute_script(“return performance.timing.loadEventEnd - performance.timing.navigationStart;”) print(f”页面加载时间:{load_time}ms”)

        注意execute_script是异步的,但它会返回JavaScript执行的结果。对于需要等待JS执行完毕的场景,可以结合显式等待。

      • 处理Shadow DOM:Web组件技术会创建Shadow DOM,其中的元素无法用普通选择器直接定位。你需要通过JavaScript“穿透”Shadow Root。

        # 假设有一个自定义组件 <my-component> host_element = driver.find_element(By.TAG_NAME, “my-component”) # 获取shadow root shadow_root = driver.execute_script(“return arguments[0].shadowRoot”, host_element) # 现在可以在shadow root内查找元素(注意:这里不能再用driver.find_element,而是用shadow_root作为起点) # 但Selenium的WebElement没有直接的shadowRoot属性访问方法,所以通常需要继续用JS inner_button = driver.execute_script(“return arguments[0].shadowRoot.querySelector(‘button.primary’)”, host_element) inner_button.click()

        处理Shadow DOM相对复杂,如果你的项目大量使用Web组件,可能需要封装一些工具函数来简化操作。

      5. 框架集成与最佳工程实践

      单个脚本跑起来不难,难的是如何将成千上万个自动化用例组织好、执行好、维护好。这就需要引入测试框架和工程化思维。

      5.1 与单元测试框架结合(以Python+Pytest为例)

      单纯用脚本写if...else做断言是原始的。集成pytestunittest可以享受到用例管理、夹具(Fixture)、参数化、报告等强大功能。

      • 基本结构

        # conftest.py - 定义全局夹具 import pytest from selenium import webdriver @pytest.fixture(scope=”function”) # 每个测试函数一个浏览器实例 def driver(): driver = webdriver.Chrome() driver.implicitly_wait(3) yield driver # 测试函数执行时使用这个driver driver.quit() # 测试函数执行完毕后退出浏览器 # test_login.py class TestLogin: def test_login_success(self, driver): # 注入driver夹具 driver.get(“https://example.com/login”) driver.find_element(By.ID, “username”).send_keys(“valid_user”) driver.find_element(By.ID, “password”).send_keys(“valid_pass”) driver.find_element(By.ID, “submit”).click() # 使用pytest的assert assert “Dashboard” in driver.title assert driver.current_url == “https://example.com/dashboard” @pytest.mark.parametrize(“username, password, expected_error”, [ (“”, “pass”, “用户名不能为空”), (“user”, “”, “密码不能为空”), (“wrong”, “wrong”, “用户名或密码错误”), ]) def test_login_failure(self, driver, username, password, expected_error): driver.get(“https://example.com/login”) # ... 执行登录操作 error_msg = driver.find_element(By.CLASS_NAME, “error”).text assert error_msg == expected_error

        pytest的夹具系统能优雅地管理浏览器的生命周期,参数化可以极大地减少重复代码。

      • 页面对象模型(Page Object Model, POM):这是UI自动化必须掌握的设计模式。将每个页面或重要组件封装成一个类,页面的元素定位和基本操作作为类的方法。测试脚本只调用这些方法,不直接包含定位符和底层交互。

        # pages/login_page.py from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class LoginPage: def __init__(self, driver): self.driver = driver self.wait = WebDriverWait(driver, 10) # 定位器 USERNAME_INPUT = (By.ID, “username”) PASSWORD_INPUT = (By.ID, “password”) SUBMIT_BUTTON = (By.ID, “submit”) ERROR_MSG = (By.CLASS_NAME, “error”) # 页面操作方法 def enter_username(self, username): self.wait.until(EC.visibility_of_element_located(self.USERNAME_INPUT)).send_keys(username) def enter_password(self, password): self.driver.find_element(*self.PASSWORD_INPUT).send_keys(password) def click_submit(self): self.driver.find_element(*self.SUBMIT_BUTTON).click() def get_error_message(self): return self.wait.until(EC.visibility_of_element_located(self.ERROR_MSG)).text def login(self, username, password): # 业务流组合方法 self.enter_username(username) self.enter_password(password) self.click_submit() # test_login.py from pages.login_page import LoginPage def test_login_success(driver): login_page = LoginPage(driver) driver.get(“https://example.com/login”) login_page.login(“valid_user”, “valid_pass”) assert “Dashboard” in driver.title

        POM的优势:当页面UI变更时(比如登录按钮的ID变了),你只需要修改LoginPage类中的一处定位符,所有用到这个按钮的测试用例都无需修改,极大提升了可维护性。

      5.2 配置管理、日志与报告

      • 配置管理:不要将浏览器类型、基础URL、超时时间、账号密码等硬编码在脚本里。使用配置文件(如config.yaml.env)或命令行参数。
        # config.yaml base_url: “https://staging.example.com” browser: “chrome” headless: true implicit_wait: 3 explicit_wait: 10 credentials: admin: username: “admin_user” password: ${ADMIN_PASS} # 可以从环境变量读取
      • 日志记录:使用Python的logging模块记录关键操作、错误和警告。这比单纯用print更专业,便于调试和问题追溯。
        import logging logging.basicConfig(level=logging.INFO, format=‘%(asctime)s - %(name)s - %(levelname)s - %(message)s’) logger = logging.getLogger(__name__) def click_element(driver, locator): try: element = WebDriverWait(driver, 10).until(EC.element_to_be_clickable(locator)) element.click() logger.info(f”成功点击元素:{locator}”) except TimeoutException: logger.error(f”等待元素可点击超时:{locator}”) raise
      • 测试报告pytest可以生成JUnit XML格式的报告,方便与Jenkins等CI/CD工具集成。也可以使用更美观的插件,如pytest-html生成HTML报告,或allure-pytest生成功能强大的Allure报告。

      6. 常见疑难杂症与性能优化实战

      即使遵循了所有最佳实践,在实际项目中你还是会碰到各种“坑”。这里记录一些典型问题和优化思路。

      6.1 典型异常与排查思路

      异常/问题可能原因排查与解决思路
      NoSuchElementException1. 元素定位符写错。
      2. 页面未加载完成(最常见)。
      3. 元素在iframe或Shadow DOM内。
      4. 元素是动态生成的,DOM已变化。
      1. 在浏览器开发者工具中验证定位符。
      2.增加合适的显式等待,等元素可见、可交互。
      3. 使用driver.switch_to.frame()切换到iframe,或用JS处理Shadow DOM。
      4. 使用更稳定的相对定位,或与开发约定添加测试属性。
      ElementNotInteractableException1. 元素被其他元素遮挡(如弹窗、遮罩层)。
      2. 元素不可见(display: nonevisibility: hidden)。
      3. 元素处于禁用状态(disabled属性)。
      1. 等待遮挡元素消失或将其关闭。
      2. 检查元素样式,或尝试用JS直接修改属性后操作(仅用于测试)。
      3. 检查业务逻辑,确认当前状态是否允许操作。
      StaleElementReferenceException你获取到的元素对象所对应的DOM节点已经失效(页面刷新、元素被重新渲染)。黄金法则不要长时间缓存WebElement对象。对于动态页面,尽量在需要操作前重新查找元素。如果必须在循环中使用,尝试在每次迭代内重新定位。
      TimeoutException显式等待的条件在指定时间内未满足。1. 增加超时时间(需权衡)。
      2. 检查等待条件是否准确(例如,等“可点击”而不是“存在”)。
      3. 检查页面逻辑或网络是否有问题。
      脚本执行慢1. 使用了time.sleep或过长的隐式等待。
      2. 网络环境差,页面资源加载慢。
      3. 定位策略效率低(如复杂XPath)。
      4. 浏览器未启用无头模式。
      1.用显式等待替代所有固定等待
      2. 考虑在稳定的测试环境执行,或模拟网络限速进行测试。
      3. 优化定位符,优先用ID、CSS Selector。
      4. 在不需要观察UI的测试中,使用无头模式。

      6.2 性能优化与稳定性提升技巧

      1. 启用无头模式(Headless):在CI/CD管道或不需要观察浏览器界面的场景下,无头模式能节省大量资源和时间。

        from selenium.webdriver.chrome.options import Options chrome_options = Options() chrome_options.add_argument(“--headless=new”) # Chrome较新版本的推荐写法 chrome_options.add_argument(“--disable-gpu”) # 在Windows上可能需要 chrome_options.add_argument(“--no-sandbox”) # 在某些Linux环境可能需要 driver = webdriver.Chrome(options=chrome_options)
      2. 复用浏览器会话:对于需要登录的测试套件,可以考虑先启动一个浏览器完成登录,并将会话信息(Cookies)保存下来,后续测试直接加载Cookies,避免每个用例都重复登录。这能极大缩短测试执行时间。

      3. 使用ActionChains处理复杂鼠标操作:对于悬停、拖放、右键菜单等操作,ActionChains是标准解决方案。

        from selenium.webdriver.common.action_chains import ActionChains menu = driver.find_element(By.ID, “menu”) submenu = driver.find_element(By.ID, “submenu”) actions = ActionChains(driver) actions.move_to_element(menu).pause(1).click(submenu).perform()
      4. 处理验证码:这是一个常见难题。完全自动化解法通常不可靠。实践中,有以下几种策略:

        • 测试环境屏蔽验证码:这是最推荐的方式,让开发在测试环境提供一个万能验证码或直接关闭验证码功能。
        • 使用OCR库(如Tesseract)识别简单图形验证码:成功率不高,且容易被反爬机制识别。
        • 人工干预:在遇到验证码时暂停脚本,手动输入后继续。这仅适用于少量、低频的测试场景。
        • 使用第三方打码平台API:需要付费,且响应时间和成功率受平台影响。
      5. 使用Selenium Grid进行分布式测试:当你的测试用例成百上千,需要在多种浏览器、多种操作系统上运行时,单机执行会非常耗时。Selenium Grid允许你将测试脚本分发到多个节点(Node)上并行执行。一个典型的Grid Hub+Node架构可以显著缩短整体反馈时间。结合Docker可以更方便地管理不同环境的Node节点。

      7. 持续集成与DevOps中的Selenium

      自动化测试只有融入到开发流程中,才能发挥最大价值。将Selenium测试集成到CI/CD管道(如Jenkins, GitLab CI, GitHub Actions)是标准操作。

      一个基本的GitHub Actions工作流示例:

      name: UI Automation Test on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: ‘3.10’ - name: Install dependencies run: | pip install -r requirements.txt # 安装浏览器驱动,可以使用第三方Action,如 ‘nanasess/setup-chromedriver’ - name: Run UI Tests run: | pytest tests/ --html=report.html --self-contained-html env: BASE_URL: ${{ secrets.BASE_URL }} TEST_USER: ${{ secrets.TEST_USER }} TEST_PASS: ${{ secrets.TEST_PASS }} - name: Upload test report uses: actions/upload-artifact@v3 if: always() # 即使测试失败也上传报告 with: name: ui-test-report path: report.html

      在这个流程中,每次代码推送或发起拉取请求,都会自动在一个干净的Ubuntu环境中安装依赖、运行Selenium测试用例,并生成HTML测试报告作为产物保存。这样,开发者在合并代码前就能快速获知UI功能是否被破坏。

      最后一点体会:Selenium的强大在于它的生态和标准性,但它的“笨重”和“不稳定”也常被诟病。我的经验是,对于核心业务流程、关键用户路径的回归测试,Selenium依然是可靠的选择。而对于需要极快执行速度、与前端框架深度绑定的组件级测试,可以考虑像Cypress、Playwright这样的现代工具作为补充。技术选型没有银弹,理解Selenium的深度,能帮助你在合适的场景做出最合适的选择,这才是“王者之路”的真谛。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/4 15:14:00

基于YOLOv8的扑克牌识别系统开发全解析

## 1. 项目概述&#xff1a;当计算机视觉遇上扑克牌去年在拉斯维加斯的一次技术交流会上&#xff0c;我看到赌场工作人员手工清点扑克牌的繁琐操作&#xff0c;萌生了开发这套系统的想法。这个基于YOLOv8的扑克牌识别系统&#xff0c;不仅能实时检测牌面花色点数&#xff0c;还…

作者头像 李华
网站建设 2026/7/4 15:13:29

基于YOLOv3与深度学习的多目标跟踪系统实现

1. 项目概述 这个毕业设计项目实现了一个基于深度学习的多目标跟踪系统&#xff0c;采用"检测跟踪"(Tracking By Detecting)的方法来实时追踪视频中的多个目标。作为计算机视觉领域的经典问题&#xff0c;目标跟踪在智能监控、自动驾驶、人机交互等场景都有广泛应用。…

作者头像 李华
网站建设 2026/7/4 15:12:01

国产大模型三巨头技术解析:GLM-5、M2.5与DeepSeek实战选型指南

1. 这不是发布会&#xff0c;是AI圈的春节联欢晚会大年初三凌晨三点&#xff0c;我泡了第三杯浓茶&#xff0c;盯着终端里刚跑完的GLM-5本地推理日志——vLLM吞吐量稳定在38 tokens/s&#xff0c;显存占用比预期低12%&#xff0c;而屏幕上那个用它生成的自动售货机经营模拟报告…

作者头像 李华
网站建设 2026/7/4 15:11:07

基于深度学习的鲜花识别系统设计与实现

1. 项目概述这个基于深度学习的鲜花识别系统是一个典型的计算机视觉应用项目&#xff0c;它使用Python作为主要开发语言&#xff0c;结合深度学习框架实现对不同种类鲜花的自动识别和分类。作为一名长期从事AI项目开发的工程师&#xff0c;我认为这类项目非常适合作为计算机相关…

作者头像 李华
网站建设 2026/7/4 15:09:47

CVE-2022-23366漏洞深度剖析:HMS医疗系统SQL注入实战复现与防御

1. 项目概述&#xff1a;一次针对特定医疗管理系统的安全审计之旅最近在整理一些历史CVE漏洞的复现笔记&#xff0c;翻到了CVE-2022-23366这个编号。这是一个关于HMS v1.0的SQL注入漏洞。HMS&#xff0c;全称Hospital Management System&#xff0c;即医院管理系统&#xff0c;…

作者头像 李华
网站建设 2026/7/4 15:08:22

LLM 题解去幻觉:证明链比漂亮解释更重要

LLM 题解去幻觉&#xff1a;证明链比漂亮解释更重要 一、题解幻觉通常很有迷惑性 LLM 写算法题解时&#xff0c;最危险的不是语气不自信&#xff0c;而是解释非常顺&#xff0c;却在关键逻辑上错了。它可能把贪心条件说得像定理&#xff0c;却没有交换论证&#xff1b;也可能给…

作者头像 李华