2. Python异常处理语法:try-except结构、捕获特定异常、else子句、finally子句
异常处理,说白了就是给代码穿上防弹衣。
我在RPA项目里见过太多因为一个小异常就导致整个流程崩溃的情况。比如读取Excel时文件被占用、网络请求超时、元素定位失败……这些在RPA开发中简直是家常便饭。所以,掌握Python的异常处理语法,是每个RPA工程师的必修课。
2.1 try-except基础结构
先看最基础的结构。try块里放可能出错的代码,except块里放出错后的补救措施。
try:
# 可能出错的代码
result = 10 / 0
except:
# 出错后执行的代码
print("哎呀,出错了!")
嗯,这里要注意:裸except会捕获所有异常。我个人习惯是尽量少用这种写法,因为它会吞掉你意想不到的错误,比如键盘中断(Ctrl+C)也会被捕获,导致程序无法正常退出。
2.2 捕获特定异常
RPA开发中,不同的异常需要不同的处理方式。比如文件找不到和网络超时,处理逻辑完全不一样。
try:
with open("config.json", "r") as f:
config = f.read()
except FileNotFoundError:
print("配置文件不存在,使用默认配置")
config = "{}"
except PermissionError:
print("没有权限读取配置文件,请检查文件权限")
config = "{}"
except Exception as e:
print(f"未知错误:{e}")
config = "{}"
你想想看,如果所有异常都用同一个except处理,你根本不知道到底是文件不存在还是权限不够。这在RPA流程里很要命——你不知道该重试还是该报错。
我在项目中遇到过这样的情况:一个RPA机器人每天早上8点准时跑,但偶尔会报错。后来发现是运维在凌晨更新了配置文件,导致文件被临时锁定。如果当时用了特定异常捕获,就能快速定位到是文件被占用,而不是去排查网络或数据库问题。
2.3 else子句
else子句可能很多人用得少,但我觉得它挺优雅的。它的作用是:当try块没有抛出异常时,执行else块。
try:
data = fetch_data_from_api()
except ConnectionError:
print("网络连接失败,稍后重试")
data = None
except TimeoutError:
print("请求超时,请检查网络")
data = None
else:
# 只有数据获取成功才执行
print("数据获取成功,开始处理")
process_data(data)
为什么要用else?直接把处理逻辑写在try块里不行吗?
其实,把正常逻辑和异常处理分开,代码更清晰。try块里只放可能出错的代码,else块里放成功后的逻辑。这样别人看代码时,一眼就能分清「正常流程」和「异常流程」。
2.4 finally子句
finally子句是异常处理里的「铁律」——无论是否发生异常,它都会执行。
try:
file = open("report.xlsx", "w")
file.write(data)
except IOError:
print("写入文件失败")
finally:
# 无论如何都要关闭文件
file.close()
print("文件已关闭")
这个在RPA里太重要了。你想想,RPA机器人经常要操作各种资源:打开浏览器、连接数据库、读取文件……如果中间出了异常,资源没释放,下次运行就会出问题。
我曾经犯过一个错误:在RPA流程里打开了一个Excel文件,中间代码报错了,但没写finally来关闭文件。结果第二天机器人跑的时候,Excel文件一直被昨天的进程锁着,导致整个流程卡死。从那以后,我所有涉及资源操作的代码,都会加上finally来确保释放。
2.5 综合示例:RPA中的异常处理
最后,我写一个RPA场景的完整示例,把上面几个知识点串起来。
def rpa_login_and_fetch_data():
"""
RPA登录系统并获取数据
"""
browser = None
try:
# 打开浏览器
browser = webdriver.Chrome()
browser.get("https://example.com/login")
# 输入账号密码
browser.find_element(By.ID, "username").send_keys("admin")
browser.find_element(By.ID, "password").send_keys("123456")
browser.find_element(By.ID, "login-btn").click()
# 等待页面加载
WebDriverWait(browser, 10).until(
EC.presence_of_element_located((By.ID, "data-table"))
)
except TimeoutException:
print("页面加载超时,可能是网络问题")
# 发送告警通知
send_alert("登录超时")
return None
except NoSuchElementException as e:
print(f"元素未找到:{e}")
# 截图保存现场
browser.save_screenshot("error_screenshot.png")
return None
except Exception as e:
print(f"未知错误:{e}")
return None
else:
# 登录成功,获取数据
print("登录成功,开始获取数据")
data = browser.find_element(By.ID, "data-table").text
return data
finally:
# 无论如何都要关闭浏览器
if browser:
browser.quit()
print("浏览器已关闭")
这个例子涵盖了:
- try:放可能出错的代码(打开浏览器、定位元素)
- except:捕获特定异常(超时、元素未找到)并做针对性处理
- else:登录成功后执行数据获取逻辑
- finally:确保浏览器被关闭,释放资源
说白了,异常处理就是给代码留后路。RPA机器人跑在无人值守的环境里,一个异常没处理好,可能整个流程就卡死了。所以,写异常处理时,多想想「如果这里出错了,我希望程序怎么反应?」