影刀RPA 异常处理的五种模式:try-catch之外的思路
影刀RPA 异常处理的五种模式try-catch之外的思路作者林焱什么情况用这个几乎所有影刀教程教你异常处理时都是同一句话“用try-catch包起来就行了”。但实际开发中try-catch只是异常处理的最后一步远不是全部。有些异常你应该让它直接中断流程不处理比错误处理更安全有些异常只要打个日志跳过就好有些异常需要重试还有些异常代表你的流程逻辑本身有问题。把所有的异常都用try-catch一把梭结果就是流程表面上不报错了但实际数据要么丢了要么错了——这是最危险的静默失败。这篇文章拆解五种异常处理模式让你知道面对不同类型的错误应该采取什么策略。怎么做模式一重试模式——临时故障自动恢复适用场景网络波动、服务暂时不可用、元素偶发加载慢。importtimeimportrandomdefretry_on_failure(func,max_retries3,base_delay1): 带退避的重试 func: 可能失败的操作函数 max_retries: 最大重试次数 base_delay: 基础延迟秒数 forattemptinrange(max_retries):try:returnfunc()exceptExceptionase:ifattemptmax_retries-1:# 最后一次重试也失败了抛出raiseException(f重试{max_retries}次后仍然失败:{e})# 指数退避1秒→2秒→4秒delaybase_delay*(2**attempt)# 加随机抖动避免惊群效应delayrandom.uniform(0,0.5)print(f第{attempt1}次失败{delay:.1f}秒后重试:{e})time.sleep(delay)# 使用示例defclick_submit_btn():btndriver.find_element(By.CSS_SELECTOR,#submit)btn.click()# 最多重试3次retry_on_failure(click_submit_btn,max_retries3)重试模式的陷阱重试前确认失败原因网络超时可以重试业务逻辑错误重试一百次也没用重试要设上限无限重试等于流程永远不结束耗时操作API调用、文件下载重试间隔要更长模式二跳过模式——非关键失败继续执行店群矩阵自动化突破运营极限适用场景批量处理中的个别数据异常、某条记录格式不对但不影响大局。defprocess_with_skip(items,process_func):逐个处理失败就跳过并记录results{success:[],skipped:[],total:len(items)}foridx,iteminenumerate(items):try:resultprocess_func(item)results[success].append({index:idx,data:item,result:result})exceptExceptionase:# 记录跳过的results[skipped].append({index:idx,data:item,error:str(e)})print(f⚠️ 跳过第{idx}条:{e})continue# 输出统计print(f处理完成:{len(results[success])}成功,{len(results[skipped])}跳过)# 如果有太多跳过可能需要告警skip_ratelen(results[skipped])/len(items)ifskip_rate0.1:# 超过10%异常print(f⚠️ 跳过率过高:{skip_rate:.1%}建议人工检查)returnresults跳过模式的关键一定要记录跳过了哪些、为什么跳过。下次运行前先检查上次的跳过记录。模式三降级模式——主方案失败启用备用方案适用场景有备选的操作路径。defclick_element_with_fallback(main_selector,fallback_selectors): 主选择器找不到时尝试备选选择器 按优先级从高到低排列 selectors[main_selector]fallback_selectors errors[]fori,selectorinenumerate(selectors):try:elementdriver.find_element(By.CSS_SELECTOR,selector)ifelement.is_displayed():element.click()returnTrueelse:errors.append(f{selector}: 不可见)exceptExceptionase:errors.append(f{selector}:{e})# 所有方案都失败了raiseException(f所有选择器都失败:{; .join(errors)})# 使用click_element_with_fallback(#new-submit-btn,# 首选新版按钮[#old-submit-btn,# 备选1旧版按钮button.submit]# 备选2通用选择器)降级的更多场景# 数据采集的降级API → DOM解析 → 截图OCRdefcollect_data_with_fallback():三级降级采集# 方案A调API最快try:returncollect_via_api()except:pass# 方案B解析DOM备选try:returncollect_via_dom()except:pass# 方案C截图OCR最后手段returncollect_via_ocr()模式四快死模式——严重错误立即中断适用场景数据完整性问题、关键依赖不可用、继续执行可能造成更大损失。classFatalError(Exception):致命错误——应该立即中断流程passdefcheck_prerequisites():流程前置条件检查——不满足则立即中断# 检查关键文件ifnotos.path.exists(D:/data/source.xlsx):raiseFatalError(源数据文件不存在无法继续)# 检查数据库连接try:connsqlite3.connect(D:/data/business.db)conn.close()except:raiseFatalError(数据库不可用无法继续)# 检查磁盘空间至少100MBimportshutil free_spaceshutil.disk_usage(D:/).freeiffree_space100*1024*1024:raiseFatalError(f磁盘空间不足:{free_space/1024/1024:.0f}MB)# 主流程入口try:check_prerequisites()exceptFatalErrorase:# 致命错误记录后通知不重试不降级send_alert(f流程中断:{e})raise# 让流程终止快速失败原则不满足前置条件立即失败比跑了半小时才失败要好一百倍。模式五防护模式——防御性编程在操作前先检查避免异常发生defsafe_divide(a,b,default0):防除零错误returna/bifb!0elsedefaultdefsafe_list_access(lst,index,defaultNone):防索引越界returnlst[index]if0indexlen(lst)elsedefaultdefsafe_dict_access(d,key,defaultNone):防键不存在returnd.get(key,default)defsafe_file_write(filepath,content):安全写文件——目录不存在先创建os.makedirs(os.path.dirname(filepath),exist_okTrue)# 先写到临时文件成功后再替换tmp_pathfilepath.tmpwithopen(tmp_path,w,encodingutf-8)asf:f.write(content)# 原子替换os.replace(tmp_path,filepath)defsafe_element_text(element,default):安全获取元素文本try:returnelement.text.strip()except:returndefault防御性编程的思想是把检查做在操作前面而不是等出了异常再去处理。有什么坑坑一try-catch包了太多代码# ❌ 一个try-catch包了所有逻辑try:open_page()login()collect_data()save_to_excel()send_email()exceptExceptionase:print(f出错了:{e})问题你不知道具体是哪一步出错也不知道当前的数据状态是什么。解决每个独立步骤有自己的异常处理粒度要合理open_page()# 这一步失败无法继续 → 不try直接中断login_with_retry()# 网络问题 → 重试模式collect_with_skip()# 个别数据问题 → 跳过模式save_to_excel()# 文件写入 → try-catch 备份send_email()# 通知 → 失败只记日志不中断主流程坑二静默吞掉所有异常# ❌ 最危险的写法try:critical_operation()except:pass# 啥也不做假装没出错后果流程表面上正常结束了实际上关键数据没处理。这是自动化中最隐蔽的Bug。temu店群自动化报活动案例解决每个except至少做三件事之一记录日志、重试、向上抛出。坑三在except中又抛出另一个异常掩盖了原始错误# ❌ 原始错误被掩盖try:process_data()exceptExceptionase:raiseException(数据处理失败)# 丢了原始错误信息e解决try:process_data()exceptExceptionase:raiseException(f数据处理失败:{e})frome# 保留原始异常链坑四重试中使用了固定延迟# ❌ 固定延迟重试foriinrange(3):try:api_call()breakexcept:time.sleep(2)# 每次都等2秒问题当大量流程同时重试时都用固定延迟会在同一时刻撞车。解决使用指数退避随机抖动。坑五没有区分异常类型# ❌ 笼统捕获所有异常try:do_something()exceptException:# 所有异常都按同一种方式处理pass解决区分处理fromselenium.common.exceptionsimportTimeoutException,NoSuchElementExceptiontry:do_something()exceptTimeoutException:# 超时 → 重试retry()exceptNoSuchElementException:# 元素不存在 → 可能是页面变了send_alert(页面结构可能变化)raiseexceptValueError:# 数据格式错误 → 跳过这条skip_and_log()exceptExceptionase:# 未知异常 → 记录并向上抛log_and_raise(e)总结异常处理的正确姿势不是把所有东西包在try-catch里而是分场景选择合适的处理模式。网络波动→重试个别数据异常→跳过关键依赖不可用→快死可选路径→降级操作前检查→防护。记住一个被静默吞掉的异常比你看到的报错危险100倍。