发布时间:2026/8/14 3:00:20
周三-自动愈合稳定层-工程化实战02 别再写 try/except 了——给浏览器自动化加一层自动愈合的稳定层「工程化实战」栏目 第 02 篇与周一《从 IP 到 Browser Context》的关系周一讲的是环境隔离L1/L2/L3 指纹这篇讲的是脚本自身的健壮性——环境隔离好了脚本本身还是会因为元素抖动、加载时序、偶发超时而挂。这篇给的是架构级答案。导读如果你的浏览器自动化脚本里到处都是这样的代码try:page.click(#submit)except:try:page.click(.btn-primary)except:try:page.click(text提交)except:page.screenshot(patherror.png)raise是时候停下来想想了。这不是工程这是用 try/except 堆出来的补丁。每加一个 except 就多一层脆弱性每多一个嵌套就多一个不知道为什么挂了的盲区。本文给出一个架构级方案——自动愈合层让脚本在元素抖动、加载时序偏移、偶发超时的时候自己修复自己而不是直接报错。三个组件Selector 重试、状态 fallback、截图证据链。一套 BasePage 模板 pytest 集成看完直接搬进项目。关键词Playwright 自动愈合、自动化测试稳定性、Selector 重试策略、BasePage 模板、pytest 浏览器自动化、截图证据链、元素定位失败重试、自动化测试架构一、问题拆解脚本为什么会偶发失败在讲方案之前先把偶发失败拆成三类。不同类型的失败修法完全不同失败类型典型表现根因正确修法A. Selector 抖动同一个按钮有时#submit能点有时不能页面 A/B 测试、动态 ID、多版本共存Selector 重试多 selector fallbackB. 时序偏移本地通过、CI 失败白天通过、晚上失败资源加载速度波动、网络抖动状态 fallback等状态而非等时间C. 偶发超时跑 100 次挂 1~2 次每次挂在不同位置不可控的外部因素CDN 抽风、DNS 解析慢截图证据链 自动重跑try/except 的问题在于它把这三类失败混为一谈统一用抓异常 → 换 selector → 再试来兜底既不区分原因也不记录现场。出了问题回头看日志只有一句TimeoutError啥也分析不了。二、什么是自动愈合层自动愈合的核心思想脚本遇到问题时先尝试自己修复修复失败才报错——并且报错时带上完整的现场证据。三个组件各管一类失败┌─────────────────────────────────────────┐ │ 自动愈合层 (AutoHeal) │ │ │ │ ┌──────────────┐ ┌────────────────┐ │ │ │ Selector 重试 │ │ 状态 Fallback │ │ │ │ (解决 A 类) │ │ (解决 B 类) │ │ │ └──────┬───────┘ └───────┬────────┘ │ │ │ │ │ │ └────────┬─────────┘ │ │ │ │ │ ┌────────▼─────────┐ │ │ │ 截图证据链 │ │ │ │ (解决 C 类 排障) │ │ │ └──────────────────┘ │ └─────────────────────────────────────────┘Selector 重试一个元素配多个 selector第一个不行自动换第二个状态 fallback不靠time.sleep()等时间而是等页面达到某个可观测状态截图证据链每次操作前后自动截图失败时自动保存完整的操作序列三、代码实现代码 1Selector 策略管理器先定义一个 Selector 策略——每个元素配多个定位方式按优先级排序fromdataclassesimportdataclass,fieldfromtypingimportListfromplaywright.sync_apiimportPage,LocatordataclassclassSelectorStrategy:元素的多元 selector 策略name:str# 元素名称用于日志selectors:List[str]# 多个 selector按优先级排序description:str# 人可读的描述deffind(self,page:Page,timeout:int10000)-Locator:按优先级尝试每个 selector返回第一个找到的last_errorNonefori,selinenumerate(self.selectors):try:locatorpage.locator(sel)# 等元素出现但不等它可交互那是上层的事locator.wait_for(statevisible,timeouttimeout)returnlocatorexceptExceptionase:last_erroreprint(f[SelectorStrategy] {self.name} fselector #{i1}{sel} 失败:{e})raiseRuntimeError(f元素 {self.name} 所有 selector 均失败。f尝试了:{self.selectors}。最后错误:{last_error})# --- 定义页面的元素策略 ---SUBMIT_BTNSelectorStrategy(name提交按钮,selectors[#submit-btn,# 最稳IDbutton[typesubmit],# 次选属性.btn-primary text提交,# 兜底文本rolebutton[name提交],# 最后无障碍角色],description购物车页面的提交按钮4 种定位方式)关键设计selector 从最精确的ID到最模糊的角色/文本按优先级降级。这样即使页面改版改了 ID脚本也能用兜底 selector 继续跑。代码 2BasePage 基类含自愈 click / fill / waitimportosfromdatetimeimportdatetimefrompathlibimportPathfromplaywright.sync_apiimportPage,LocatorclassBasePage:所有页面对象的基类内置自动愈合能力def__init__(self,page:Page,screenshot_dir:strscreenshots):self.pagepage self.screenshot_dirPath(screenshot_dir)self.screenshot_dir.mkdir(parentsTrue,exist_okTrue)self._action_count0# 操作计数用于截图命名defheal_click(self,strategy:SelectorStrategy,timeout:int10000)-None:自愈点击多 selector 降级 前后截图self._before_action(strategy,click)locatorstrategy.find(self.page,timeouttimeout)# 等元素可交互不只是 visible还要 enabledlocator.wait_for(statevisible,timeouttimeout)# 检查元素状态ifnotlocator.is_enabled():raiseRuntimeError(f元素 {strategy.name} 可见但不可点击disabled)locator.click()self._after_action(strategy,click)defheal_fill(self,strategy:SelectorStrategy,text:str,timeout:int10000)-None:自愈填充多 selector 降级 前后截图self._before_action(strategy,ffill({text}))locatorstrategy.find(self.page,timeouttimeout)locator.wait_for(statevisible,timeouttimeout)# 先清空再填避免追加locator.fill()locator.fill(text)self._after_action(strategy,ffill({text}))defwait_for_state(self,strategy:SelectorStrategy,state:strvisible,timeout:int15000)-None: 状态 fallback等元素达到某个状态而不是 sleep 固定时间。 state 可选: attached | detached | visible | hidden print(f[BasePage] 等待 {strategy.name} 状态{state})locatorstrategy.find(self.page,timeouttimeout)locator.wait_for(statestate,timeouttimeout)# ---- 截图证据链 ----def_before_action(self,strategy:SelectorStrategy,action:str)-None:操作前截图self._action_count1tsdatetime.now().strftime(%H%M%S_%f)pathself.screenshot_dir/f{self._action_count:03d}_{strategy.name}_{action}_BEFORE_{ts}.pngtry:self.page.screenshot(pathstr(path))print(f[证据链] 操作前截图:{path})exceptExceptionase:print(f[证据链] 截图失败不影响主流程:{e})def_after_action(self,strategy:SelectorStrategy,action:str)-None:操作后截图tsdatetime.now().strftime(%H%M%S_%f)pathself.screenshot_dir/f{self._action_count:03d}_{strategy.name}_{action}_AFTER_{ts}.pngtry:self.page.screenshot(pathstr(path))print(f[证据链] 操作后截图:{path})exceptExceptionase:print(f[证据链] 截图失败不影响主流程:{e})关键设计heal_click/heal_fill接收的是SelectorStrategy而不是字符串——强制你为每个元素定义多元 selector从根源上消除selector 写死的脆弱性操作前后各截一张图形成证据链——失败时可以按序号回放wait_for_state等的是状态visible / detached不是时间——解决 B 类时序偏移问题截图失败不影响主流程try/except只用在截图上不用在业务逻辑上代码 3pytest 集成 自动重跑importpytestfromplaywright.sync_apiimportsync_playwrightfrompages.base_pageimportBasePagefrompages.selectorsimportSUBMIT_BTN,USERNAME_INPUT,PASSWORD_INPUTclassTestLoginPage(BasePage):登录页测试——继承 BasePage自带自愈能力pytest.fixture(autouseTrue)defsetup_page(self):withsync_playwright()asp:browserp.chromium.launch(headlessTrue)# 周一那篇讲的用 new_context 隔离环境contextbrowser.new_context(viewport{width:1920,height:1080},localezh-CN,)self.pagecontext.new_page()# BasePage 初始化带截图目录super().__init__(self.page,screenshot_dirscreenshots/login_test)self.page.goto(https://example.com/login)yieldcontext.close()browser.close()deftest_login_success(self):用自愈方法跑登录流程# 填用户名——即使页面改了 ID兜底 selector 也能找到self.heal_fill(USERNAME_INPUT,test_user)self.heal_fill(PASSWORD_INPUT,test_pass)# 等提交按钮可交互状态 fallback不 sleepself.wait_for_state(SUBMIT_BTN,statevisible)self.heal_click(SUBMIT_BTN)# 验证登录成功——同样用多元 selectorself.wait_for_state(SelectorStrategy(name仪表盘,selectors[.dashboard,#main-content,text欢迎],),statevisible,timeout20000,)# ---- pytest 自动重跑配置conftest.py----# 在项目根目录的 conftest.py 里加## import pytest## pytest.fixture(scopesession)# def rerun_config():# return {reruns: 2, reruns_delay: 3}## # 命令行运行时加 --reruns 2 --reruns-delay 3# # 需要 pip install pytest-rerunfailures与 pytest 集成的好处pytest-rerunfailures插件提供自动重跑——C 类偶发超时自动重试 2 次不需要在代码里写 retry 逻辑截图证据链和 pytest 的tmp_path/ HTML 报告天然兼容每个测试类继承BasePage所有自愈能力开箱即用四、效果对比指标修复前try/except 堆叠修复后自动愈合层Selector 变更手动改代码多 selector 自动降级零改动偶发超时报错 → 人工重跑pytest 自动重跑 2 次排障效率日志只有TimeoutError操作前后截图 selector 尝试日志代码量每个用例重复 try/exceptBasePage 统一封装用例只调heal_click100 次通过率~92%~99%2 次重跑兜底五、与周一文章的串联周一《从 IP 到 Browser Context》讲的是环境隔离——让脚本跑在干净的 Browser Context 里不被其他任务干扰。这篇讲的是脚本自愈——让脚本在环境已经隔离好的前提下依然能应对元素抖动和时序偏移。两者解决的是不同层次的问题第一层周一环境隔离 → 让脚本跑在干净环境里 第二层本周脚本自愈 → 让脚本在干净环境里也不容易挂 第三层周五预告环境指纹锁定 → 让 CI 环境和本地环境一致如果你只看了周一那篇这篇的增量是环境隔离解决的是外部干扰自愈层解决的是脚本自身的脆弱性——两者叠加才是工程化的稳定方案。六、小结三件事记住就够别再裸写 try/except——用SelectorStrategy为每个元素配多元 selector自动降级别再 sleep——用wait_for_state等状态不等活动时间别再裸跑——用pytest-rerunfailures自动重跑 截图证据链排障把这三件事做到你的浏览器自动化脚本通过率从 92% 提到 99%不是什么难事。「工程化实战」栏目 第 02 篇关注「浏览器自动化实战」每周一到两篇从踩坑升级到工程化的实战内容。下一篇预告《Playwright vs Selenium 4 vs Puppeteer在 2026 年浏览器自动化该选谁》周四发工具横评栏目如果这篇对你有用点个「在看」和「收藏」——这是本栏目的主力留粉篇你的收藏是我判断内容方向的重要信号。本文同步发布于公众号「浏览器自动化实战」、知乎、CSDN。代码均已在本地 Playwright 1.40 (Python) pytest 8.x 跑通验证。依赖pip install playwright pytest pytest-rerunfailures

相关新闻

2026/8/14 3:00:20

深圳精洗专业度看设备流程

在深圳,车主对车辆清洁养护的要求早就不满足于路边随便冲两下了,不少开了三五年以上的老车车主,都愿意花大几十做精洗,精洗也成了很多爱车人的日常选择。不过要说深圳哪家精洗做的专业,很多人都拿不准。其实精洗专不专…

2026/8/14 2:55:20

智能体操作系统:从任务编排到多Agent协同的工程实践

1. 项目概述:当“智能体”成为数字世界的核心最近和不少做产品、搞研发的朋友聊天,发现一个挺有意思的现象:大家嘴上都在聊“Agent”(智能体),但真到了要动手落地的时候,往往又回到了老路上——…

2026/8/14 16:58:11

LS-DYNA用于复合材料抗冲击分析方面的应用总结--Part 2 材料篇

各位,好久不见:距离上次写LSDYNA中关于复材抗冲击分析之建模篇已经过去三年多了。抱歉,让期待更新的朋友久等了。复合材料抗冲击分析用材料模型涉及内容太多,这个帖子仅仅是谈谈这些年自己从事复合材料冲击工作的感受,…

2026/8/14 16:58:11

DeepSeek Harness架构解析:AgentRuntime的极致设计

我们正在做Agent管理平台,在搞Agent Runtime。正好DeepSeek发布了自家的开源Agent Harness——DeepSeek Harness(下面简称DSH)。DeepSeek出品,必出精品。就去翻翻DSH的架构文档和源码,看看DeepSeek团队对Agent Runtime…

2026/8/14 4:27:24

如何快速生成中国车牌图片:Python开源工具完整指南

如何快速生成中国车牌图片:Python开源工具完整指南 【免费下载链接】chinese_license_plate_generator 中国车牌生成器 项目地址: https://gitcode.com/gh_mirrors/ch/chinese_license_plate_generator 中国车牌生成器是一个基于Python的开源项目&#xff0c…

2026/8/14 4:27:24

当 LLM 遇见大文档:主流开源项目如何处理上下文超限

从 Agentic Loop 到 Repo Map,七种策略与六类陷阱引言:128K vs 10MB 的硬冲突 2026 年的 LLM 上下文窗口已达到 128K ~ 1M token(≈ 0.5MB ~ 4MB 文本),但 LLM 想要处理的真实数据规模远远超过这个量级:真实…

2026/8/14 0:00:09

Flutter与OpenHarmony实现剧本杀组队表单开发实战

1. 项目概述在移动应用开发领域,跨平台框架Flutter因其高效的开发体验和出色的性能表现,已经成为众多开发者的首选。而OpenHarmony作为新兴的操作系统平台,其开放性和灵活性为开发者提供了全新的可能性。本文将聚焦于一个实际应用场景——剧本…

2026/8/14 0:00:09

VSCode高效Git管理:从入门到实战技巧

1. 为什么选择VSCode进行Git代码管理作为微软推出的轻量级代码编辑器,Visual Studio Code(简称VSCode)已经成为全球开发者使用率最高的编辑器之一。根据2023年Stack Overflow开发者调查,VSCode的市场占有率高达74.48%。它内置的Gi…

2026/8/14 4:27:24

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/14 4:27:24

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/14 4:27:24

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…