问1:什么是异常处理
异常处理(Try-Except)------给程序穿上防弹衣。
在真实环境里,你经常会遇到:某个子文件夹没权限访问(PermissionError)、某个文件刚好被同事删了(FileNotFoundError)、或者网络突然断了。
如果不加异常处理,只要有一个文件报错,整个扫描程序就会瞬间崩溃,直接闪退。
📌 知识点:怎么接住"报错"?
Python 的报错(Exception)是可以被"接住"的,语法结构长这样:
python
try:
# 把可能报错的代码放这里
result = 10 / 0
except ZeroDivisionError:
# 如果真的报了除零错误,就会跳到这里执行,程序不会崩
print("你不能除以零!")
except Exception as e:
# 兜底方案:所有你没预料到的错误,都会掉进这里
print(f"发生了未知错误:{e}")
实战中最常用的就是 except Exception as e: 。它是一个大网,能网住所有的报错,并用变量 e 记录报错的具体原因。
🎮 怎么测试防弹衣有没有用?
你可以故意造一个错误:把 file.stat().st_size 改成 file.stat().st_size / 0(制造一个除法错误)。
运行一下,你会发现程序不会崩溃 ,而是控制台打印出一堆红色的 ERROR 日志,然后继续扫描下一个文件。这就是防弹衣的作用。
问2:try except Exception as e: 这个我用的还是比较多的,但是这个有没有比较有经验的用法,比如什么地方适合用,总不能每一行都用吧
答:"总不能每一行都用吧"------完全正确。 如果每行代码都包个 try...except,代码会变成一坨屎山,不仅难看,而且一旦真的出了严重 Bug,程序会悄悄吞掉错误,让你根本找不到原因。
在真正的工程开发中,老手用 try...except 有一个黄金法则:
只为"你无法控制的外部环境"兜底,不为"你自己的逻辑错误"擦屁股。
具体来说,有四个场景 适合用,两个场景绝对不要用:
✅ 适合用 try...except 的四个场景
1. 跟外部资源打交道时(最常用)
只要是去碰你的程序管不到的东西,就一定要包上:
-
文件读写:文件被删了、权限不够、路径超长。
-
网络请求:断网了、接口超时、服务器返回 500 错误。
-
数据库:连接断了、唯一键冲突。
-
硬件/外部设备:摄像头打不开、共享盘掉线。
2. 数据类型转换/解析时(防脏数据)
你从 Excel、CSV 或用户输入里拿到的数据,永远是脏的。比如你拿到的 "12.5%",你想转成 12.5:
python
text = "12.5%"
try:
num = float(text) # 如果遇到 "未填写",这里直接崩
except ValueError:
num = 0.0 # 兜底
3. 调用别人的第三方库时
有些库写得很烂,动不动就抛个奇怪的异常,你如果不接住,整个程序直接死。你只能把调用的那几行包起来,防止它炸到你自己。
4. 多线程/多进程/批量处理时
就像你现在的 scanner.py,你正在批量扫 1000 个文件。第 500 个文件坏了,你不能让整个任务停下来。这时候的 try 是放在循环里面的,保证"死一个没关系,其他的继续跑"。
❌ 绝对不要用 try...except 的两个场景
1. 你自己的逻辑写错了,不要用
比如你写个函数,明确要求传 int,结果你手滑传了个 str。
你不要 用 try...except 去掩盖它。你应该让它直接报错(崩溃),然后去把传参的地方改对。
2. 可以用 if...else 提前判断的,不要用 try
比如,你要除以一个数:
python
# ❌ 错误示范(用异常当流程控制)
try:
result = a / b
except ZeroDivisionError:
result = 0
# ✅ 正确示范(提前判断)
if b != 0:
result = a / b
else:
result = 0
"能预判的错误用 if,无法预判的意外用 try。"
🚀 老手的"高级用法"(避坑指南)
1. 尽量别用 except Exception as e 一把抓
虽然 Exception 能接住所有错,但太笼统了 。
如果代码里有拼写错误(NameError)或语法错误,你一把抓之后,错误被悄悄吞了,你还以为程序在正常跑。
老手会按顺序捕获具体的错误:
python
try:
file.stat()
except FileNotFoundError:
logging.warning("文件消失了")
except PermissionError:
logging.warning("没权限")
except Exception as e:
# 实在不知道是啥错,兜底并记录,然后最好再抛出去
logging.error(f"未知错误: {e}")
raise # 把错误重新扔出去,让上层知道这里死了
2. 千万别写"静默吞掉"的代码
python
# ❌ 极其恶劣的写法
try:
do_something()
except:
pass # 出了错,当没看见,啥也不干
这叫"掩耳盗铃"。哪怕你什么处理都不做,也至少要在 except 里打个日志:
logging.error(f"出错了: {e}")。
3. 学会用 finally 清理战场
只要你打开了文件、连接了数据库、拿了锁,出了错也必须关上,否则会造成"资源泄漏"。用 finally 永远能执行最后一步:
python
f = open("data.txt")
try:
# 这里可能报错
process(f)
finally:
f.close() # 不管上面错没错,必定关文件
(当然,用 with open 会更好,自动帮你做这一步)
这就是老手的"边界感":精准包裹高危区域,剩下的交给主程序自由奔跑。
问3:但是在涉及自动化的交互里,try怎么用呢,因为随时都在与外部资源打交道
在**自动化(RPA、爬虫、UI自动化)**里,几乎每一步都在和外部环境打交道:网页可能加载慢、按钮可能还没出来、API可能突然超时。
如果全用 try 包起来,代码确实会变成一座"屎山"。在自动化实战中,老手处理这种"随时可能出错"的场景,有三套非常经典的组合拳:
套路一:包装成"重试机制"(Retry)
自动化最怕网络抖动或页面卡顿。遇到这种错,不要直接跳过,而是"等几秒再试一次" 。
老手会写一个重试装饰器,或者直接用现成的库(比如 tenacity)。
python
import time
def click_button_with_retry(driver, locator, max_retries=3):
for attempt in range(max_retries):
try:
# 尝试找按钮并点击
element = driver.find_element(*locator)
element.click()
return True # 成功了,直接结束函数
except Exception as e:
# 报错了,说明没找到或者点不动
if attempt == max_retries - 1:
# 最后一次也失败了,抛出错误,记录截图
logging.error(f"重试 {max_retries} 次依然失败: {e}")
raise
# 没到最后一次,等 2 秒再试
time.sleep(2)
经验 :只把 try 放在这种**"尝试执行某个动作"**的局部函数里,而不是把整个流程包起来。
套路二:用于"分支判断"(元素存在性检测)
在 UI 自动化里,你经常会遇到弹窗。你不知道它什么时候弹,如果你直接用 find_element 去找,没找到就会直接抛异常崩溃。
这时 try 用来做**"静默探测"**:
python
def close_ad_if_exists(driver):
try:
# 尝试找广告关闭按钮
close_btn = driver.find_element(By.ID, "close-ad")
close_btn.click()
logging.info("发现广告,已关闭")
except NoSuchElementException:
# 找不到?说明没广告,不用管,当没看见
pass # 这里的 pass 是合理的,因为找不到是预期内的事
套路三:阶梯兜底(降级策略)
比如你要抓一个网页的价格,你可以用 try 准备两套方案:
python
try:
# 方案A:尝试用精确的 CSS 选择器
price = driver.find_element(By.CSS_SELECTOR, ".price-exact").text
except NoSuchElementException:
# 方案B:方案A失败了,尝试用更宽泛的 XPath 兜底
try:
price = driver.find_element(By.XPATH, "//div[contains(@class, 'price')]").text
except NoSuchElementException:
# 彻底没救了,给个默认值,并且报警
price = "0.00"
logging.error("价格抓取失败,页面结构可能变了!")
🚨 自动化避坑铁律:绝不能"静默吞掉"
因为自动化是无人值守的(比如半夜跑脚本),所以老手绝不会在 except 里只写个 pass 就完事。
在自动化里,报错的 except 里必须做三件事:
-
打日志 :
logging.error("xxx步骤出错")。 -
存现场 :如果是网页自动化,赶紧
driver.save_screenshot("error.png");如果是接口,把 response 存下来。 -
决定死活 :如果这一步极其关键(比如"登录"),失败了整个脚本跑下去也没意义,就直接
raise抛出异常,终止任务;如果这一步无关紧要(比如"关闭广告"),就记个 log 继续往下走。
总结自动化的 try 核心哲学:
不要把它当成"避免崩溃"的万能药,把它当成"延长等待时间"和"准备备用方案"的工具。
只要你在 except 里能清楚回答:"失败了怎么办?是等几秒重试?是换条路走?还是截图报警?" ,你就不会乱用 try 了。