Selenium自动化测试框架实战:从WebDriver到Pytest与POM设计
1. 项目概述从“点鼠标”到“写脚本”的思维跃迁干了这么多年软件测试我见过太多测试同行每天重复着“点点点”的工作也见过不少团队在引入自动化测试时一头雾水最后工具买了一堆脚本写了一堆维护成本却高得吓人ROI投资回报率惨不忍睹。今天我们不聊那些虚头巴脑的概念就从一个最经典、最经久不衰的工具——Selenium入手掰开揉碎了讲讲什么才是真正有价值的自动化测试以及围绕它构建的测试框架到底该怎么玩。Selenium这个名字对于测试工程师来说就像木匠手里的锤子厨师手里的炒勺是基础也是利器。但很多人对它的理解可能还停留在“一个能模拟浏览器操作的工具”上。这没错但太浅了。Selenium的本质是一套允许你通过编程语言来驱动浏览器、与Web元素进行交互的协议和工具集合。它把你从手动点击、输入、验证的体力劳动中解放出来让你能用代码的逻辑和速度去完成那些重复、繁琐的UI层验证工作。理解Selenium不仅仅是学会几个API调用更是理解如何将测试用例从自然语言描述转化为可执行、可维护、可报告的自动化脚本的完整思维过程。这篇文章就是带你完成这次从“手工测试员”到“自动化测试工程师”的思维升级我们会深入它的五脏六腑看看核心组件如何协作并动手搭建一个真正能用在项目里的、健壮的自动化测试框架。2. Selenium核心组件深度拆解不只是WebDriver很多人一提到Selenium脑子里蹦出来的就是WebDriver。这固然是核心但Selenium生态是一套组合拳理解每个组件的定位和演进历史能让你在技术选型和问题排查时心里更有底。2.1 Selenium IDE录制与回放的“入门拐杖”Selenium IDE是一个浏览器插件支持Chrome和Firefox它提供了录制用户操作并生成脚本的功能。你点点鼠标它就能生成对应的测试代码支持多种语言如Java、Python、C#。它的核心价值与局限性价值对于完全没代码基础的测试人员它是感受自动化魅力的最快途径。也适合快速生成一些简单操作的脚本片段作为学习参考。新版Selenium IDE还支持导出为Pytest等框架的脚本实用性有所提升。局限性绝对不要指望用它来构建企业级的自动化测试项目。它生成的脚本通常结构僵硬、充斥硬编码的等待和定位符如冗长的XPath缺乏编程逻辑如条件判断、循环、数据驱动维护成本极高。页面元素一变脚本基本就废了。注意我强烈建议将Selenium IDE仅作为学习辅助工具或探索性测试的临时记录工具。真正的自动化项目必须从手写代码开始。2.2 Selenium WebDriver真正的“大脑”与“遥控器”这是Selenium的绝对核心也是我们日常所说的“Selenium”通常指代的部分。WebDriver并非一个具体的工具而是一个面向对象的编程接口API和一套与浏览器通信的协议W3C WebDriver标准。它的工作原理可以类比为遥控车你的测试脚本遥控器你用Python、Java等语言写的代码调用WebDriver API如find_element,click,send_keys。语言绑定信号编码器Selenium为各种语言提供了官方“绑定”Client Library如selenium包Python、Selenium.WebDriverC#。它把你的API调用编码成符合WebDriver协议的标准HTTP请求。浏览器驱动信号接收器 执行器如chromedriverChrome、geckodriverFirefox。它是一个独立的可执行程序启动后会在本地开启一个HTTP服务。它接收来自语言绑定的协议命令并将其翻译成浏览器原生支持的操作通过浏览器的自动化接口如Chrome DevTools Protocol。真实浏览器赛车最终执行操作并渲染页面的载体。为什么是这个架构早期Selenium RCRemote Control时代需要注入一个JavaScript核心Selenium Core到浏览器中存在同源策略等限制。WebDriver的架构直接利用浏览器厂商提供的原生支持更稳定、更强大也最终成为了W3C推荐标准。这意味着浏览器厂商有义务实现这个协议保证了工具的长期生命力和兼容性。2.3 Selenium Grid分布式执行的“调度中心”当你的测试用例成百上千需要在不同浏览器Chrome, Firefox, Safari、不同操作系统Windows, macOS或不同版本上并行运行时一台机器就显得力不从心了。Selenium Grid应运而生。它的架构是经典的Hub-Node模式Hub中心调度器只有一个。它接收所有测试脚本发来的执行请求包含所需的浏览器和平台配置即DesiredCapabilities。Node执行节点可以有多个注册到Hub上。每个Node上配置了它能提供的“能力”例如“我这里有Windows 10 Chrome 110”。Hub根据脚本请求将任务分发给匹配的Node执行。实操心得搭建Grid对于提升测试效率至关重要尤其是在做兼容性测试时。你可以用Docker轻松部署一套Grid环境每个Node一个容器管理起来非常方便。记住你的测试脚本几乎不需要改动只需要将WebDriver指向Hub的地址即可这就是协议标准化的好处。3. 超越Selenium构建健壮的自动化测试框架只会用Selenium WebDriver写几个脚本离真正的自动化测试还有很远。单个脚本就像散兵游勇而一个测试框架则是正规军负责调度、补给、报告。下面我们以Pythonpytest为例拆解一个典型框架的核心组成部分。3.1 测试运行管理Pytest为何是首选早期很多人用unittest但pytest凭借其简洁的语法、强大的夹具Fixture系统和丰富的插件生态已成为Python自动化测试的事实标准。为什么选Pytest零样板代码不需要继承某个类函数以test_开头就是用例。强大的Fixture这是核心优势。Fixture用于提供测试所需的环境如初始化浏览器、登录和数据并支持作用域函数、类、模块、会话级实现优雅的资源复用和清理。参数化测试用pytest.mark.parametrize轻松实现数据驱动测试用一组数据驱动同一个测试逻辑。丰富的插件如pytest-html生成报告、pytest-xdist并行测试、pytest-rerunfailures失败重试能轻松扩展框架能力。断言更智能直接使用Python的assert语句失败时pytest会给出详细的差异对比。一个简单的Fixture示例import pytest from selenium import webdriver pytest.fixture(scopefunction) # 每个测试函数执行一次 def driver(): # 初始化浏览器这里以Chrome为例 options webdriver.ChromeOptions() options.add_argument(--headless) # 无头模式不显示UI适合CI环境 options.add_argument(--disable-gpu) options.add_argument(--no-sandbox) options.add_argument(--window-size1920,1080) driver webdriver.Chrome(optionsoptions) driver.implicitly_wait(10) # 隐式等待全局生效 yield driver # 将driver对象提供给测试用例 driver.quit() # 测试结束后执行清理工作 def test_login_success(driver): # 测试用例直接使用fixture driver.get(https://www.example.com/login) driver.find_element(id, username).send_keys(test_user) driver.find_element(id, password).send_keys(secure_pass) driver.find_element(id, login-btn).click() assert Dashboard in driver.title3.2 页面对象模型让代码抵御UI变化这是UI自动化测试中最重要的设计模式没有之一。POMPage Object Model的核心思想是将页面定位元素和操作元素的行为封装成一个独立的类页面对象测试脚本只调用这些行为方法不直接包含定位符和底层操作。为什么要用POM高可维护性当页面UI修改如一个按钮的id变了你只需要在一个地方对应的Page类里更新定位符和方法所有用到这个页面的测试用例都无需改动。高可读性测试用例读起来像业务文档login_page.login(username, password)而不是一堆find_element和click。减少代码重复页面的通用操作如导航栏点击被封装起来复用性高。POM实战示例# base_page.py - 基础页面类封装通用方法 from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class BasePage: def __init__(self, driver): self.driver driver self.wait WebDriverWait(driver, 10) # 显式等待对象 def find_element(self, locator): 查找单个元素加入显式等待 return self.wait.until(EC.presence_of_element_located(locator)) def click(self, locator): self.find_element(locator).click() # login_page.py - 登录页面对象 from .base_page import BasePage from selenium.webdriver.common.by import By class LoginPage(BasePage): # 定位符集中管理 USERNAME_INPUT (By.ID, username) PASSWORD_INPUT (By.ID, password) LOGIN_BUTTON (By.ID, login-btn) ERROR_MSG (By.CLASS_NAME, error-message) def enter_username(self, username): self.find_element(self.USERNAME_INPUT).send_keys(username) return self # 支持链式调用 def enter_password(self, password): self.find_element(self.PASSWORD_INPUT).send_keys(password) return self def click_login(self): self.click(self.LOGIN_BUTTON) def get_error_message(self): return self.find_element(self.ERROR_MSG).text def login(self, username, password): # 业务组合方法 self.enter_username(username).enter_password(password).click_login() # test_login.py - 测试用例变得极其简洁 def test_login_failure(driver): login_page LoginPage(driver) login_page.login(wrong_user, wrong_pass) assert Invalid credentials in login_page.get_error_message()3.3 数据驱动与配置管理让测试灵活起来硬编码的测试数据是自动化脚本的另一个“坏味道”。我们需要将测试数据用户名、密码、搜索关键词和环境配置测试URL、数据库连接、日志级别外部化。1. 数据驱动测试使用pytest的parametrize装饰器或者从外部文件JSON, YAML, Excel, CSV中读取数据。import pytest import json def load_test_data(): with open(test_data/login_data.json, r) as f: return json.load(f) pytest.mark.parametrize(username, password, expected, load_test_data()) def test_login_with_data(driver, username, password, expected): login_page LoginPage(driver) login_page.login(username, password) if expected success: assert Dashboard in driver.title else: assert expected in login_page.get_error_message()2. 配置管理使用配置文件如config.yaml或环境变量来管理不同环境测试、预生产、生产的配置。# config.yaml environments: test: base_url: https://test.example.com api_url: https://api.test.example.com db_host: test-db.local staging: base_url: https://staging.example.com api_url: https://api.staging.example.com db_host: staging-db.local在代码中根据一个环境变量如ENVtest来加载对应的配置。3.4 报告、日志与异常处理测试的“黑匣子”自动化测试跑完了是成是败必须一目了然。清晰的报告和日志是排查问题的生命线。1. 测试报告pytest-html插件生成结构清晰的HTML报告展示通过、失败、跳过的用例以及失败时的错误信息和截图。allure-pytest插件生成更强大、更美观的Allure报告支持步骤描述、附件截图、日志、分类、趋势图等是展示测试结果的利器。# 运行测试并生成Allure结果 pytest --alluredir./allure-results # 生成并打开HTML报告 allure serve ./allure-results2. 日志记录使用Python内置的logging模块在关键步骤如开始测试、执行操作、断言、结束测试记录信息。配置日志输出到文件和控制台方便实时查看和事后追溯。import logging logging.basicConfig(levellogging.INFO, format%(asctime)s - %(name)s - %(levelname)s - %(message)s, handlers[logging.FileHandler(test_run.log), logging.StreamHandler()]) logger logging.getLogger(__name__) def test_something(driver): logger.info(Starting test_something...) # ... 测试操作 logger.info(Assertion passed.)3. 失败截图这是UI自动化调试的“后悔药”。通过pytest的钩子函数或Fixture在测试失败时自动截取当前浏览器屏幕。pytest.hookimpl(tryfirstTrue, hookwrapperTrue) def pytest_runtest_makereport(item, call): outcome yield report outcome.get_result() if report.when call and report.failed: # 假设driver fixture在item中可用 if hasattr(item, funcargs) and driver in item.funcargs: driver item.funcargs[driver] screenshot_path f./screenshots/{item.name}_{datetime.now().strftime(%Y%m%d_%H%M%S)}.png driver.save_screenshot(screenshot_path) report.extra [pytest_html.extras.image(screenshot_path, Failure Screenshot)]4. 关键实操技巧与避坑指南理论懂了框架搭了真正写脚本和运行时下面这些经验能让你少走很多弯路。4.1 元素定位稳定性的基石不稳定的元素定位是UI自动化失败的首要原因。定位策略优先级从高到低ID唯一且稳定首选。Name通常也较稳定。CSS Selector性能好语法灵活。优先用id、class、属性组合。#username(ID选择器).btn-primary(类选择器)input[nameemail](属性选择器)XPath功能最强大但性能稍差且容易因页面结构微调而失效。慎用绝对路径以/开头尽量使用相对路径和属性结合。好的//button[idsubmit]或//div[classcontainer]//input坏的/html/body/div[3]/div[2]/form/button[1]实操心得与前端开发约定为关键的可测试元素如主要按钮、表单输入框添加唯一的id或>from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By # 等待元素可见并可点击 element WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.ID, dynamic-button)) ) element.click() # 等待元素消失 WebDriverWait(driver, 10).until( EC.invisibility_of_element_located((By.ID, loading-spinner)) )4.3 测试数据准备与清理自动化测试不应该污染线上数据也不应该依赖不稳定的测试数据。常用策略接口准备通过调用后台API接口来创建测试所需的数据如测试用户、订单。这是最干净、最快的方式。数据库操作直接操作测试数据库插入或恢复数据。需要小心处理数据依赖和事务。使用测试隔离工具如pytest的Fixture在测试开始时准备数据测试结束后通过yield或finalizer进行清理删除创建的数据。Mock与Stub对于依赖的外部服务如支付网关、短信服务使用Mock来模拟其行为保证测试的独立性和速度。5. 常见问题排查与框架优化方向即使框架搭好了脚本写好了在持续集成CI中运行还是会遇到各种“妖魔鬼怪”。这里记录一些典型问题和进阶思考。5.1 典型失败场景与排查清单问题现象可能原因排查步骤与解决方案NoSuchElementException1. 定位符错误或元素不存在。2. 页面未加载完成/元素在iframe或shadow DOM中。3. 页面跳转或弹窗遮挡。1. 使用浏览器开发者工具重新检查定位符。2. 增加合适的显式等待。3. 切换到正确的iframe (driver.switch_to.frame)。4. 检查是否有模态框需要先关闭。ElementNotInteractableException1. 元素不可见如被隐藏。2. 元素被禁用disabled属性。3. 元素被其他元素覆盖。1. 等待元素可见(EC.visibility_of)。2. 检查元素状态。3. 使用JavaScript直接点击driver.execute_script(arguments[0].click();, element)。StaleElementReferenceException你持有的元素对象所对应的DOM元素已经失效页面刷新、元素被重新渲染。重新查找元素。这是唯一解决办法。避免在页面可能刷新的操作后还使用旧的元素对象。测试在本地通过在CI上失败1. CI环境是无头模式渲染或行为可能有差异。2. CI环境资源CPU/内存不足运行慢。3. 网络、时区等环境差异。1. 本地也使用无头模式运行一遍 (--headless)。2. 增加等待超时时间优化CI机器配置。3. 在CI脚本中明确设置语言、时区等环境变量。测试执行速度慢1. 硬性等待 (time.sleep) 过多。2. 网络请求慢或依赖外部服务。3. 用例设计不合理依赖顺序执行。1.全部替换为显式等待。2. 对慢速依赖进行Mock。3. 使用pytest-xdist进行并行测试。5.2 框架的持续优化方向一个框架搭建起来只是开始如何让它更高效、更智能、更容易维护是持续的过程。1. 引入Page Factory模式对于超大型项目POM类中的find_element调用会显得冗余。可以使用PageFactory模式源自Selenium JavaPython可通过selenium.webdriver.support.PageFactory或第三方库如pom实现配合注解进一步简化页面对象的代码。2. 行为驱动开发集成考虑集成behave或pytest-bdd让测试用例用近乎自然语言的Gherkin语法Given-When-Then来编写提升与非技术成员如产品经理、业务分析师的沟通效率。3. 视觉回归测试对于UI样式要求极高的项目可以集成像Applitools Eyes或Selenium Screenshot Library这样的工具进行像素级的视觉对比自动检测UI上的意外变化。4. 测试用例标签化与筛选使用pytest的mark功能给用例打上标签如pytest.mark.smoke冒烟测试、pytest.mark.regression回归测试。运行时可以只执行特定标签的用例pytest -m smoke。5. 与CI/CD管道深度集成将你的自动化测试框架接入Jenkins、GitLab CI、GitHub Actions等。配置成代码推送后自动触发测试测试失败自动通知相关负责人并将Allure报告发布到内部站点形成质量反馈闭环。走到这一步你已经不仅仅是一个Selenium工具的使用者而是一个能够设计、构建并持续优化一套自动化测试解决方案的工程师。记住自动化的终极目标不是取代手工测试而是将人力从重复劳动中解放出来去从事更有价值的探索性测试、用户体验测试和复杂业务逻辑测试。你的框架越稳健脚本越智能整个团队的质量保障效率就越高。最后保持学习Web技术在变测试理念也在演进但扎实的编程基础、清晰的设计思维和对质量的执着是应对一切变化的根本。

相关新闻

Ubuntu系统下Mosek求解器的C++项目集成与性能调优指南

Ubuntu系统下Mosek求解器的C++项目集成与性能调优指南

1. 项目概述:为什么选择Mosek?如果你正在处理大规模的优化问题,比如投资组合优化、供应链调度或者机器学习中的模型训练,并且已经受够了开源求解器在求解速度、稳定性和对复杂约束支持上的局限,那么Mosek很可能就是你正…

2026/10/19 4:33:54 阅读更多 →
.NET 8企业级订单管理系统开发:从环境配置到生产部署

.NET 8企业级订单管理系统开发:从环境配置到生产部署

在实际企业级开发中,.NET 平台因其成熟的生态、稳定的性能和丰富的工具链,成为构建高可用后端服务的优选方案。但很多初学者在环境配置、项目结构设计和部署上线环节容易遇到版本冲突、依赖管理混乱、配置缺失等问题。本文将围绕一个完整的订单管理系统开…

2026/10/18 23:26:53 阅读更多 →
VSCode中高效运行与调试HTML的完整指南

VSCode中高效运行与调试HTML的完整指南

1. 为什么要在VSCode中运行HTML? 作为前端开发者,我们每天都要和HTML打交道。Visual Studio Code(简称VSCode)凭借其轻量级、高扩展性和丰富的插件生态,已经成为现代Web开发的标准工具之一。但很多新手在安装好VSCode…

2026/10/20 7:18:43 阅读更多 →

最新新闻

11维度交叉验证技术分析面板:从指标混乱到统一共识的实战框架

11维度交叉验证技术分析面板:从指标混乱到统一共识的实战框架

最近复盘一段时间的行情,发现一个挺有意思的现象:很多时候我们对走势的判断差之毫厘,但交易结果却谬以千里。根本原因往往不在于方向看没看对,而在于下单那一刻的犹豫和动摇。技术指标太多,信号相互打架,今…

2026/10/20 10:00:42 阅读更多 →
OneSearch-V2:语义检索中枢与五层协议栈架构解析

OneSearch-V2:语义检索中枢与五层协议栈架构解析

1. 项目概述:OneSearch-V2 不是“又一个搜索框”,而是一套可嵌入、可定制、可演进的语义检索中枢OneSearch-V2 这个名字乍看像某个科技公司的内部代号,但如果你在最近三个月里参与过至少两个中等规模的业务系统重构,或者负责过知识…

2026/10/20 10:00:42 阅读更多 →
OCR文字识别六步原理:从图像到译文的必经技术链路

OCR文字识别六步原理:从图像到译文的必经技术链路

1. 项目概述:一张图里的文字,为什么非得“先跑六步”?“先把六步跑完:翻译图片里的文字时,十条方法只是分叉”——这个标题乍看像句绕口令,但如果你最近处理过截图、PDF扫描件、手机拍的菜单、产品说明书&a…

2026/10/20 10:00:42 阅读更多 →
OneSearch-V2:面向工作流的轻量级统一搜索中间件

OneSearch-V2:面向工作流的轻量级统一搜索中间件

1. 项目概述:OneSearch-V2 不是“又一个搜索框”,而是一次底层交互范式的重定义OneSearch-V2 这个名字乍看平平无奇,甚至容易被误认为是某个旧工具的简单迭代版本。但我在过去三年里深度参与过多个跨平台统一检索系统的架构设计与落地实施&am…

2026/10/20 10:00:42 阅读更多 →
Blender与DeepSeek动画协同:FCurves驱动的语义翻译管道

Blender与DeepSeek动画协同:FCurves驱动的语义翻译管道

1. 这不是“AI动画教程”,而是一次对创作主权的重新确认最近在几个设计类社区里,反复看到有人发帖问:“用Blender加一个大模型,能不能三分钟做出电影级动画?”底下跟帖清一色是“能”“太简单了”“已出教程”。我点开…

2026/10/20 10:00:42 阅读更多 →
AI模型监控选型:Prometheus、Grafana、Datadog定位对比与落地实践

AI模型监控选型:Prometheus、Grafana、Datadog定位对比与落地实践

做AI模型部署监控的人,大概率都参与过这种讨论:“Prometheus、Grafana、Datadog到底选哪个?”每次这种话题都能吵上几十层楼,支持哪个的都有。我自己的观点是:这个问题从一开始就问偏了,因为这三样东西根本…

2026/10/20 9:59:41 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →