UI自动化测试框架搭建:Selenium+Python+Pytest从零到一实践指南

很多做测试和开发的朋友,早晚都会遇到同一个问题:手动回归测试做到想吐。一个功能改完,要把主流程从头点一遍,上午改代码,下午点页面,精力全耗在重复劳动上。越来越多项目开始尝试用UI自动化来解决这类问题,但真到自己动手搭一套框架时,又会发现资料很多、五花八门,真正能落地的却不多。这篇教程就是围绕“UI自动化搭建”来写的,我会从最常用的Selenium加Python加Pytest组合出发,把一个能跑、能出报告、能稳定维护的框架从零到一搭起来,也把选型理由、等待机制、PO模式这些关键点讲明白。适合手里有Web项目想提效、刚接触自动化又不想绕弯路的同学参考。

1. 技术选型与框架整体设计:为什么是Selenium + Python + Pytest

1.1 工具选型的取舍逻辑

先说说我为什么最终选了这套组合。市面上的选择其实挺多,有Java加Selenium加TestNG、有Python加Selenium加Pytest、也有这两年很火的Playwright和Cypress。我自己在实际带项目时,很多团队连基础脚本都还没跑通,第一步的需求并不是“最新最酷”,而是“资料全、上手快、坑有前人填过”。Selenium是Web自动化里生态最老、踩坑记录最多的一代工具,覆盖率也足够,像某后台管理系统、某跨平台系统的核心流程,它都能稳定驱动真实浏览器执行。

Python作为绑定语言,语法没有Java那么繁琐,写测试脚本时尤其省力。Pytest对比同类测试框架,优势又很直接:fixture机制管理资源非常顺手,参数化做数据驱动是原生支持,断言直接用assert就行,插件体系也成熟。再配合pytest-html或Allure做报告,整个过程零额外开发成本。性能上有人说Python慢,但UI自动化的瓶颈根本不在语言,而在浏览器渲染和网络等待,选Python完全撑得住。

1.2 项目目录结构与PO模式的价值

框架设计上,我强烈建议按照Page Object模式来组织项目。PO模式的核心思想,是把页面上的元素定位和操作细节封装到独立的页面类里,测试用例里只描述业务动作,不出现find_element这种底层方法。这样做最大的好处是:当页面结构变动时,只需修改对应页面类中的一个定位器,而不用翻遍所有用例去改几十个碎片化的locator。

我用过一段时间“脚本即用例”的写法,当时所有测试代码直接Selenium API平铺直叙,最初几天跑得欢,但等到页面第二次改版时,光改定位就改了半个多小时,后来再也不敢省这一步。目前我习惯的目录结构是这样的:

ui_automation_demo/ ├── config/ # 配置文件 │ └── settings.yaml ├── pages/ # PO页面对象 │ ├── base_page.py │ ├── login_page.py │ └── home_page.py ├── test_cases/ # 测试用例 │ ├── conftest.py │ ├── test_login.py │ └── test_home.py ├── utils/ # 公共方法 │ ├── driver_factory.py │ └── screenshot.py ├── reports/ # 测试报告与截图 └── requirements.txt

这套结构的逻辑很清晰:config管环境相关的数据,pages管页面元素和操作,test_cases管业务场景和数据断言,utils提供驱动和辅助函数。从一个“能跑”的脚本升级成一个“能长期维护”的框架,这一步的架构投入是必须的。

2. 环境搭建与第一个可运行脚本

2.1 依赖安装与浏览器驱动版本匹配

环境搭建本身不难,但有一个细节几乎所有新手都踩过:浏览器驱动版本和浏览器版本对不上。Chrome浏览器一升级,ChromeDriver不跟着升,启动时就直接报session not created异常。我的习惯是关闭浏览器的自动更新,或者固定使用某个版本,让驱动版本始终精确匹配。

Python环境建议用虚拟环境,避免把依赖装乱。创建好venv后,直接安装依赖:

pip install selenium pytest pytest-html pyyaml

装完用pip show selenium确认一下版本。Selenium从4.x开始,一个比较好的点是它内部集成了Selenium Manager,可以自动下载驱动,第一次启动时会自动拉取匹配的浏览器驱动,省去了不少手动配置时间。不过在一些内网环境或驱动版本特殊的情况下,还是建议手动下载并配置到环境变量里,或者放在项目目录下用Service指定路径,这样的可预测性更高。

验证环境是否通的关键一步,是先把浏览器启动起来,而不是急着写用例。你可以用一段最简单的代码测一下:

from selenium import webdriver driver = webdriver.Chrome() driver.get("https://example.com") print(driver.title) driver.quit()

这种“最小验证”的习惯很好用。环境问题在一开始暴露,比写到一半再排查要省力得多。页面标题能打印出来,说明驱动、浏览器、网络链路全部正常,后面的工作才可以继续。

2.2 第一个自动化脚本:启动浏览器、完成一次登录

环境通了之后,我们直接写一个最原始、但完整体现自动化流程的脚本。假设要自动化的是一个后台登录场景,目标是打开登录页、输入用户名和密码、点击登录按钮、验证登录成功后的提示信息。

import time from selenium import webdriver from selenium.webdriver.common.by import By driver = webdriver.Chrome() driver.maximize_window() driver.get("https://admin.demo.local/login") driver.find_element(By.ID, "username").send_keys("admin") driver.find_element(By.ID, "password").send_keys("123456") driver.find_element(By.ID, "loginBtn").click() time.sleep(3) welcome_text = driver.find_element(By.ID, "welcomeMsg").text assert "欢迎回来" in welcome_text driver.quit()

这段脚本能跑通,但离工程化还有距离。其中最明显的问题是time.sleep(3),代码里用固定等待非常脆弱——网络快一点的时候白白等三秒,慢一点的时候三秒根本不够。别急,这个点在后面“显式等待”一节会重点讲。现在先把整个链路跑通,让自己有体感,再一步步优化。

脚本跑通之后,把浏览器驱动、依赖包、目标系统地址这些内容整理好,写进README。团队协作时,这句话往往能让新同事少浪费一整天:“依赖装好,运行某路径下的脚本,看到打印出欢迎信息就算环境OK。”

3. 核心机制拆解:定位、等待与断言

3.1 元素定位策略:优先选什么、避开什么

元素定位是UI自动化里出问题最多的环节。定位方式有id、name、class name、tag name、link text、xpath、css selector这几种,我的选择优先级很明确:优先id,其次data-testid这类专为自动化预留的属性,然后是用css selector,最后才用xpath。

id简单直接、通常唯一,页面结构一变只要id不变就能继续用。很多现代前端框架会在组件上内置data-testid或者data-cy属性,这是为了自动化测试专门设计的钩子,比xpath稳得多。xpath虽然功能最全,但写绝对路径的xpath,比如/html/body/div[1]/div[2]/form/div[3]/input,页面多一个DOM层级就废掉了,维护成本极高。如果必须用xpath,也尽量写成相对定位,并且结合元素的稳定特征,比如:

# 不推荐:依赖绝对DOM层级 driver.find_element(By.XPATH, "/html/body/div[2]/form/input[1]") # 适合:结合文本和属性,语义清晰 driver.find_element(By.XPATH, "//button[contains(@class, 'submit') and text()='登录']")

还有一个容易忽略的问题:元素定位写错不会在定位那一刻马上报错,而是在找不到时抛异常。所以高手会在封装层统一处理“定位不到”这件事,而不是让异常散落在用例中间。后面PO模式里的BasePage就是干这个的。

3.2 显式等待与隐式等待的配合逻辑

现代Web应用大量使用Ajax和异步渲染,页面元素出现的时机极不稳定。直接使用sleep定时等待,是对执行时间最大的浪费;而只用隐式等待,很多复杂异步场景又等不到正确时机。所以我的做法是“全局设置隐式等待做兜底,关键路径用显式等待做精确控制”。

隐式等待的作用是:在find_element找不到元素时,轮询一段时间再抛出异常,全局统一生效。设置方式很简单:

driver.implicitly_wait(10)

但隐式等待解决不了“元素已出现但还不可点击”“元素正在加载中”这类更细腻的状态。这时就得靠显式等待加expected_conditions,也就是大家常说的WebDriverWait。

from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC wait = WebDriverWait(driver, 10, poll_frequency=0.5) submit_btn = wait.until(EC.element_to_be_clickable((By.ID, "submitBtn")))

这段代码的实际意思很直白:最多等10秒,每0.5秒检查一次,直到按钮可点击才继续。对比sleep的固定等待,显式等待让脚本执行时间自适应页面实际加载速度,既不会傻等,也不会误判。那种“代码是对的但偶尔失败”的情况,百分之八十都是等待时机没处理好。

配合等待时,我还会区分两类条件:element_to_be_clickable和visibility_of_element_located。click前用前者,输入文本前用后者,读到一段异步加载的文本内容时,再用text_to_be_present_in_element。不同动作对应不同状态,等到正确的状态,脚本稳定性就上一个台阶。

3.3 断言怎么写才有意义

断言是UI自动化最容易“形式主义”的部分。很多人跑完脚本全靠print确认结果,这其实不是真正的自动化——自动化必须由程序判断用例通过还是失败。我当时踩过的一个典型误区是,登录成功后只断言“登录按钮不见了”,结果页面明明弹了个错误弹窗挡住了按钮,脚本依然判定成功。现在我的断言标准是:必须验证业务结果,而不是仅验证某个中间动作。

比如一个登录用例,合理的断言至少包含几层思路:关键元素是否出现、页面URL是否跳转、某个代表登录状态的文案或数据是否呈现。多写一条断言看起来多花几秒,但能挡住大量“假通过”。

Pytest里用原生assert就能完成这件事,例如:

def test_login_success(): ... assert wait.until(EC.text_to_be_present_in_element((By.ID, "userName"), "admin"))

其他常见的断言场景还有表单校验报错、列表数据条数和排序结果。总的来说,断言的最终价值是让测试结果可信,宁可稍微冗余,也不能让它形同虚设。

4. PO模式重构:从脚本到可维护框架

4.1 页面类封装的实际重构过程

在脚本跑通、断言思路清晰之后,下一步就是重构为PO模式。重构的意义不是让代码变好看,而是降低后续每一次改动的成本。我用之前那个登录脚本做一遍,展示具体的落地过程。

首先写一个BasePage基类,把find、click、input这些最常用动作统一封装,并且内置显式等待:

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, poll_frequency=0.5) def find_element(self, locator): return self.wait.until(EC.visibility_of_element_located(locator)) def find_clickable(self, locator): return self.wait.until(EC.element_to_be_clickable(locator)) def click(self, locator): self.find_clickable(locator).click() def input_text(self, locator, text): element = self.find_element(locator) element.clear() element.send_keys(text)

注意input_text里的clear操作,很多真实表单输入框会默认带一段提示文本或上一次输入残留,不清理直接send_keys,轻则文本拼接出错,重则触发输入校验。这种底层细节写在基类里,所有页面都能自动继承。

然后写LoginPage,继承BasePage,把登录页的元素和操作封装起来:

from selenium.webdriver.common.by import By from pages.base_page import BasePage class LoginPage(BasePage): username_input = (By.ID, "username") password_input = (By.ID, "password") login_button = (By.ID, "loginBtn") welcome_msg = (By.ID, "welcomeMsg") def login(self, username, password): self.input_text(self.username_input, username) self.input_text(self.password_input, password) self.click(self.login_button) def get_welcome_text(self): return self.find_element(self.welcome_msg).text

这样写的好处,看一个具体场景就清楚了:有一天开发把用户名的id从username改成了userName,在脚本式的写法里,所有用例里凡是写过username的代码全得改;在PO模式里,只需要修改LoginPage顶部这一个元组。几十条用例可能只改一行代码,维护成本完全不在一个量级。

4.2 用fixture管理浏览器生命周期

用例变成PO模式之后,下一步要解决浏览器实例的管理问题。Pytest的fixture机制可以很干净地处理“每个用例前启动浏览器、用例结束后关闭浏览器”这件事。我把fixture放在test_cases目录下的conftest.py里,它会被同目录及子目录的用例自动发现。

直接看实现:

import pytest from selenium import webdriver from pages.login_page import LoginPage @pytest.fixture(scope="class") def login_page(): driver = webdriver.Chrome() driver.maximize_window() driver.get("https://admin.demo.local/login") page = LoginPage(driver) yield page driver.quit()

这段代码里scope=“class”的意思,是在一个测试类中复用同一个浏览器实例。如果你更希望每个用例都独立开一个新的浏览器,设置scope="function"就行。两种方式各有取舍:function级别隔离更彻底,但执行时间明显变长;class级别速度快很多,不过用例之间可能残留状态。我自己的经验是,业务用例尽量用function级别保证干净,界面冒烟用例用class级别提升效率。

用例中直接接收fixture参数,代码非常清爽:

def test_login_success(login_page): login_page.login("admin", "123456") assert "欢迎回来" in login_page.get_welcome_text()

到这里,一个基础的框架雏形已经出现了:页面对象负责操作细节,fixture负责生命周期,用例只描述业务意图。代码量没少多少,但可读性和可维护性已经完全不同。

5. 数据驱动:一份用例跑多组数据

5.1 参数化的思路与实现

UI自动化做到一定量级后,会遇到大量“流程一模一样,只是输入数据不同”的场景。比如登录用例,要测账号正确密码错误、账号不存在、密码为空、账号锁定等一堆情况,如果为每种情况复制一个函数,用例代码会膨胀得很快。比较好的方式是把数据从用例代码里剥离出来,用数据驱动的方式让一份逻辑跑多组数据。

Pytest的parametrize就是专门干这个的:

import pytest @pytest.mark.parametrize("username,password,expected_msg", [ ("admin", "wrong_password", "用户名或密码错误"), ("nonexist_user", "123456", "用户不存在"), ("", "", "请输入账号"), ]) def test_login_invalid(login_page, username, password, expected_msg): login_page.login(username, password) assert expected_msg in login_page.get_error_tip()

这个写法的价值不止是省行数,更重要的是,新增一组测试数据时完全不用改用例逻辑,只是往列表里加一行。数据量和逻辑复杂度彻底解耦,这是自动化用例发展到一定规模后必须做的一件事。

5.2 用例组织与报告产出

数据驱动之后,用例报告的质量也得跟上。我最常用的组合是pytest-html加allure-pytest。pytest-html配置简单,适合小团队快速看结果;Allure的用例历史记录、失败截图关联、步骤日志分组能力强,适合用例量较多、需要长期追踪稳定性的团队。

先看pytest-html的快速用法,在项目根目录执行:

pytest test_cases --html=reports/report.html --self-contained-html

--self-contained-html参数会把所有样式、脚本打包进一个HTML文件,发给别人时不会出现样式丢失的问题。从实用性角度,这个输出已经可以满足大部分日常回归汇报了。

如果项目需要更完整的报告形态,我会加装allure-pytest:

pip install allure-pytest

运行用例时生成结果目录,再通过Allure命令行渲染成精美的报告站点:

pytest test_cases --alluredir=reports/allure_results allure serve reports/allure_results

报告的最终目的是让每个人都能快速看出“失败在哪一步、当时的页面是什么”。所以和报告配套的,还需要把关键状态快照下来,这部分下一章单独展开。

6. 稳定性提升:从能跑到跑得稳

6.1 异步加载场景的等待策略组合

UI自动化做得越深入,越会意识到“能跑”和“跑得稳”是两回事。真实项目的页面有大量表格、弹窗、下拉框,都是由JavaScript异步渲染出来的。同一个列表接口返回后,表格要经过重新渲染、样式重置、滚动加载多个步骤,如果固定等待不到位,元素定位就忽好忽坏。

我常用的等待策略组合可以总结为三条:

  1. 全局设置一个合理的隐式等待作为兜底,通常5到10秒。
  2. 页面对象的关键动作,统一走BasePage里的显式等待,不直接使用find_element。
  3. 遇到数据加载或重新渲染的场景,用wait.until等待目标条件达成,优先等待业务数据出现,例如表格中某行文本、某个加载动画消失。

例如,等待一个数据表格加载完成的写法是:

from selenium.webdriver.support import expected_conditions as EC wait.until(EC.visibility_of_element_located((By.XPATH, "//table/tbody/tr/td[contains(text(), '目标订单号')]")))

等待加载动画消失的写法是:

wait.until(EC.invisibility_of_element_located((By.CLASS_NAME, "loading-mask")))

把若干个这种细颗粒度等待组合起来,即使页面加载速度波动明显,脚本也能跑得很稳定。这里我再强调一次,尽量避免sleep。sleep一旦写进代码,执行时间就完全不可控,而且偶发问题也很难排查——你根本无法判断是页面真的慢,还是sleep写短了。

6.2 失败截图与关键信息落盘

自动化用例不可能是100%稳定的,跑挂了之后最重要的不是“报一个失败”,而是留下足够的信息让开发者快速定位。我的一次实际经历很能说明问题:某次任务执行失败,报告里只有“定位元素失败”这一行错误,开发者完全不知道页面当时是什么状态,只能手动复现,来回折腾了半个多小时。后来我养成了一个习惯,在用例失败时自动截图并保存整个页面的HTML快照,再附上当前浏览器URL,排障时间立刻缩短了一半以上。

结合Pytest注入失败截图的最简单方式是,在conftest.py里监听用例结束时的结果,如果失败则触发截图:

import pytest from datetime import datetime @pytest.hookimpl(tryfirst=True, hookwrapper=True) def pytest_runtest_makereport(item, call): outcome = yield report = outcome.get_result() if report.when == "call" and report.failed: page = item.funcargs.get("login_page") or item.funcargs.get("driver") if page: timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") screenshot_name = f"reports/screenshots/{item.name}_{timestamp}.png" page.driver.save_screenshot(screenshot_name) html_name = f"reports/screenshots/{item.name}_{timestamp}.html" with open(html_name, "w", encoding="utf-8") as f: f.write(page.driver.page_source) report.user_properties.append(("screenshot", screenshot_name))

截图保存路径最好和项目运行目录相对固定,避免跑一次换一个位置,否则后续想按用例名批量查找历史截图时会很麻烦。HTML快照的价值在于,哪怕页面被跳转走了,快照仍然保留了出问题时那一刻的DOM结构,能从代码层面直接定位到出错的元素。

6.3 特殊场景处理:iframe、多窗口、文件上传

业务系统总免不了遇到一些特殊场景,比如内嵌报表的iframe、点击后弹出新窗口的审批、上传附件的弹窗。这三个场景任何新手都会卡一阵子,我直接给出处理框架。

iframe要先切进去再操作元素,操作完记得切回来:

frame = wait.until(EC.frame_to_be_available_and_switch_to_it((By.ID, "reportIframe"))) driver.switch_to.frame(frame) # 此时才能定位iframe内部的元素 driver.find_element(By.ID, "exportBtn").click() # 操作完成切回主文档 driver.switch_to.default_content()

多窗口要用“句柄”来切换:

main_window = driver.current_window_handle driver.find_element(By.ID, "approveLink").click() wait.until(EC.number_of_windows_to_be(2)) for handle in driver.window_handles: if handle != main_window: driver.switch_to.window(handle) break # 新窗口操作完成后再切回主窗口 driver.switch_to.window(main_window)

文件上传最快的方式是用send_keys直接发送本地文件路径,这个方法在很多场景下可以绕过系统的弹窗:

driver.find_element(By.ID, "uploadInput").send_keys("/path/to/file.xlsx")

这三个场景的共同点,都是“上下文”变了。iframe和窗口切换本质上是让WebDriver明白当前操作的对象在哪个文档或窗口里,只要记住“用完切回来”的原则,基本不会出大问题。

7. 常见问题排查与避坑记录

7.1 元素找不到的排查思路

在任何一个UI自动化项目里,元素找不到绝对是最常见的报错。很多同学一到这一步就慌了,其实排查路径很固定,我整理过一张顺序排查表,照着走基本都能解决。

常见现象可能原因排查方向
NoSuchElementException定位器写错或页面结构变了先在浏览器DevTools中用对应selector确认元素存在
元素存在但定位失败元素在iframe或shadow DOM内检查页面DOM树,确认上下文切换
定位器正确但偶发失败异步加载未完成改用显式等待,等待元素可操作状态
元素刚出现但不可点击被弹窗或遮罩层覆盖检查是否有loading蒙层,等待其消失
浏览器升级后全部脚本失败驱动版本与浏览器不匹配对照版本表,重新下载匹配的ChromeDriver

排查方法的顺序,是从“元素是否存在”开始,再到“上下文是否正确”,再到“时机是否成熟”,最后才去看是否有覆盖物遮挡。这个顺序能帮你把最玄学的问题变成线性排查,很大程度上消除“它刚才明明还能跑”的情况。遇到偶发失败时,一定记得配合失败截图和页面HTML快照来分析,而不是凭记忆猜。

7.2 网络延迟与资源加载导致的偶发失败

即使在稳定的内网环境,脚本也会偶发失败,原因往往出在资源加载和网络延迟。有些页面本身依赖第三方CDN资源,加载慢时页面核心功能未初始化完,此时去定位元素,即使使用了元素等待,也可能命中“元素已出现在DOM中但事件绑定尚未完成”的状态。点击下去没有反应,误报也就随之而来了。

处理这类问题,我的习惯是加一层“业务条件等待”,不只是等元素可见,而是等与业务相关的结果状态出现。比如一个搜索页面,点击搜索按钮后可能有一个“搜索结果加载完成”的提示;一个报表页面,可能有“已生成时间”的标志。等待这些业务条件达成,比等待某个元素可见要可靠得多。在框架里可以这样写:

from selenium.webdriver.support import expected_conditions as EC wait.until(EC.text_to_be_present_in_element((By.CLASS_NAME, "search-status"), "查询完成"))

如果业务条件本身不稳定,再考虑对偶现失败做一次自动重试。重试不是逃避问题,而是给环境波动留出恢复窗口。但注意,脚本本身逻辑错误的重试意义不大,重试次数控制在一到两次就好,还得在日志里留下明显标记。

7.3 一些让日常维护轻松很多的小习惯

做到这一步,框架已经能稳定运行了。但根据我自己的长期观察,框架搭起来只是开始,能长期维护下去才是价值所在。这里有三个很小的习惯,非常想分享给正在做UI自动化的朋友。

第一个习惯是:元素定位的id和class尽量以页面功能语义来命名。虽然这话更像给前端同事的建议,但作为自动化测试方,我会主动与前端约定,可测试的元素尽量加上data-testid,并且名称保持稳定。这样可以彻底摆脱“页面一改样式,自动化脚本就崩”的困局。

第二个习惯是:每一条用例都模拟真实用户的关键操作路径,而不是专注于点击每一个角落。很多新手喜欢把用例写得很长,恨不得把整个页面所有操作都放在一条用例里,一旦中间出错,后面全部跳过,排查成本极高。我的原则是把业务路径按场景拆开,一条用例只负责一个清晰的目标,失败了定位问题只需要看对应场景,效率完全不一样。

第三个习惯是:把测试数据和测试环境配置外置到config/settings.yaml,脚本里不写死环境地址和数据。不同的部署环境(某测试环境、某预发环境)通过读取配置切换执行目标,一套用例就可以复用在多个环境里。不同环境跑同样的用例,一旦相互环境配置有差异,还能提前暴露部署问题,这是测试脚本之外额外的收益。

分享一个我最常用到的实际经验:当UI自动化的执行结果里出现大面积失败时,不要急着怀疑代码,先去看环境是否正常、数据库是否有脏数据。很多“今天跑挂、明天跑过”的用例,根因都在测试数据上。为每条用例准备独立的、可重复使用的测试数据,让用例互不影响,这一步做好,稳定性往往立刻回升。

框架搭建这件事,越到后期越会体会到,真正复杂的从来不是Selenium的API,而是对业务的理解和工程化的组织。把这些基础的等待、封装、数据驱动和排查手段梳理清楚,后续无论切换到什么新工具,思路都能很快迁移过去。

相关新闻

大模型开发入门:从API调用到RAG知识库实战

大模型开发入门:从API调用到RAG知识库实战

第一次听到“大模型开发”的时候,我的第一反应是:这跟我有什么关系?如果你是个零基础的小白程序员,想入门大模型开发,先听我一句劝:你现在缺的不是显卡,不是数学,而是一套把模型用起…

2026/10/20 17:39:19 阅读更多 →
AI重塑芯片设计流程:工程师的护城河从操作转向判断力

AI重塑芯片设计流程:工程师的护城河从操作转向判断力

1. 从“画版图”到“写约束”:芯片设计流程正在被重新切分 过去两年,我陆续参与过几个把机器学习塞进芯片设计流程的项目,有做布局布线的,有做参数调优的,也有做验证用例生成的。说实话,第一次看到工具自动…

2026/10/20 17:39:19 阅读更多 →
凸优化学习心得:从数学判定到代码求解的工程实践

凸优化学习心得:从数学判定到代码求解的工程实践

简介:这份资源是凸优化理论学习的心得笔记配套代码包,面向正在入门或希望系统梳理凸优化知识体系的机器学习、数据分析方向学习者。内容围绕陆吾生、方述诚教授课程的学习路径展开,推荐《Optimization Model》作为入门读物,并细致…

2026/10/20 17:39:19 阅读更多 →

最新新闻

CStatic控件实现GDI双缓冲实时曲线绘制

CStatic控件实现GDI双缓冲实时曲线绘制

简介:本资源是一份面向VC初学者与MFC界面开发者的实战教程,聚焦于利用Static控件实现轻量级曲线绘制,解决Windows桌面应用中无须第三方库即可动态展示波形、趋势图等图形数据的常见需求。压缩包共41个文件,涵盖10个头文件&#xf…

2026/10/20 18:31:25 阅读更多 →
PHP zval内存机制与__debugInfo漏洞原理剖析

PHP zval内存机制与__debugInfo漏洞原理剖析

1. 这道题不是考PHP语法,是考你有没有真正“看见”PHP的内存 刚看到“[蓝帽杯 2021]One Pointer PHP”这个标题时,我下意识点开题目页面,扫了一眼源码就准备关掉——又是一道“PHP弱类型反序列化”的老套路?结果在本地搭环境复现…

2026/10/20 18:31:25 阅读更多 →
手机号反查QQ号合规指南:三步通过官方流程确认绑定关系

手机号反查QQ号合规指南:三步通过官方流程确认绑定关系

手机号反查QQ号,听着像是个挺玄的功能,其实在官方体系里说穿了就是一条标准的“找回账号”路径。大多数人都不知道,是因为搜索时总被各种收费工具页面截胡,反而把官方入口给忽略了。这篇文章我把边界说清楚:什么情况下…

2026/10/20 18:31:25 阅读更多 →
AutoRemesher 自适应网格密度实战:如何按曲率控制四边形分布

AutoRemesher 自适应网格密度实战:如何按曲率控制四边形分布

AutoRemesher 自适应网格密度实战:如何按曲率控制四边形分布 【免费下载链接】autoremesher Automatic quad remeshing tool 项目地址: https://gitcode.com/GitHub_Trending/au/autoremesher 拿到一个扫描或雕刻出来的高模 obj,动辄几百万三角形…

2026/10/20 18:31:25 阅读更多 →
VOS3000操作手册实战:软交换计费配置与呼叫排障全指南

VOS3000操作手册实战:软交换计费配置与呼叫排障全指南

简介:VOS3000操作手册是一份面向运营商、呼叫中心运维人员及通信技术学习者的系统化文档,完整覆盖从入门到精通的软交换平台操作路径。手册围绕系统登录、费率与套餐管理、账户体系、呼叫跟踪、业务管理、语音管理、数据查询与报表、话单分析等核心模块展…

2026/10/20 18:31:25 阅读更多 →
Claude Code实战:宠物健康管理系统全栈开发复盘

Claude Code实战:宠物健康管理系统全栈开发复盘

1. 项目概述:这不是一个“AI写代码”的演示,而是一次真实产品级开发的完整复盘“Claude Code从零搭建新项目全流程AI实现-宠物生命周期管理App”——这个标题里藏着三个关键信息点:Claude Code是核心工具链,从零搭建强调过程完整性…

2026/10/20 18:30:19 阅读更多 →

日新闻

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/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出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/20 10:02:08 阅读更多 →
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 阅读更多 →