写自动化脚本很多人觉得很简单把几个命令串起来能跑就万事大吉。可当脚本开始频繁报错、参数改来改去、换台机器就崩溃时你才明白那些能扛住时间考验的脚本靠的不是运气而是一套被反复验证的技巧。今天这篇不聊语法基础只讲那些能让你少加班、少掉头发的实战细节。从文件路径到并发控制从日志到重试每一条都是我在生产环境里趟过雷之后的心头好。路径处理别再用字符串拼路径了绝大多数自动化脚本的死因都是路径处理不当。你还在用os.path.join和字符串拼接吗字符串拼接在Windows和Linux之间会炸带空格会炸特殊字符更炸。用pathlib是脚本可移植性的底线。它把路径当成对象来处理Path(data) / input / file.txt干干净净读文件、写文件、遍历目录都是直接方法调用。更妙的是Path.resolve()能自动处理相对路径和符号链接帮你省掉一大半脑细胞。遇到“脚本今天跑得好好的明天换了台机器就找不到文件”的灵异事件多半是在代码里写死了绝对路径。让你的脚本永远基于自身位置去定位资源——用Path(__file__).parent拿到脚本所在目录再往上回溯或往下深入。这样无论你移到哪里脚本都像个自带GPS的背包客永远不会迷路。参数解析脚本也需要一个“遥控器”拿键盘敲几个数字当参数那是给自己找罪受。一个正经的自动化脚本必须能接受命令行参数、环境变量甚至配置文件。Python标准库里的argparse足够强大但click更优雅。参数解析不是锦上添花而是一个脚本是否值得长期复用的分水岭。当你把脚本交给别人用对方不需要改代码只需要敲python script.py --input data/ --output out/ --workers 4这个脚本才算真正活了起来。别忽略--help自动生成的功能。写参数时给每个参数加上清晰的帮助文本三个月后你自己回来看着参数说明就能想起来当初的设计意图。好的参数设计本身就是一种文档。还有一个小技巧用nargs接收一个列表参数比如文件集合这样你就不用写循环调用了。日志别再用print了求求你用print调试一时爽脚本一跑就抓瞎。你想看看哪里出错结果输出被同事的终端刷屏连个时间戳都没有更别说分级别了。日志是脚本的命脉不是可有可无的装饰。用标准库logging配上RotatingFileHandler把信息输出到文件还要给日志加上时间、级别、函数名。这样凌晨三点脚本报错你翻开日志就能定位到具体行。有个容易忽略的点日志级别要设置好。平时跑用INFO就行出了诡异问题调成DEBUG就能看到每一步的细节。日志驱动的开发比调试器好用一百倍。我还喜欢给日志加一条exc_infoTrue在捕获异常时把完整堆栈打出来省得再手痒去改代码加print。重试机制好方法不怕重复网络请求、文件锁、数据库连接——这些外部资源总有抽风的时候。一次失败就终止脚本那叫脆弱一失败就无限重试那叫愚蠢。重试不是简单的循环而是一种策略。你要设置重试次数、重试间隔还要考虑退避因子。第一次失败等1秒第二次等2秒第三次等4秒这样的指数退避才是礼貌的。tenacity这个库把你的重试逻辑写成装饰器几行代码搞定。你甚至可以定制“只在某些异常下重试”比如ConnectionError而不是所有异常。把“暂时失败”和“永久失败”区分开来脚本的职业素养就体现在这里。别为了省事把所有异常都重试一遍否则配置文件写错了你会傻等半天然后看到一百行相同的报错。并发要快也要可控自动化脚本经常要处理一堆文件或请求逐个处理太慢。有的同学一上来就开几百个线程结果电脑卡死接口被封。并发要克制不要盲目地开线程。concurrent.futures里的ThreadPoolExecutor和ProcessPoolExecutor是正解。IO密集型用线程网络请求、文件读写CPU密集型用进程循环计算、图片压缩。给线程池设个合理的max_workers别贪心。用as_completed或map拿结果记得处理Future里的异常。一个能优雅退出的脚本比一个飞速但总崩溃的脚本珍贵得多。另外可以给每个任务加上一条超时时间防止某个“懒汉”任务卡住整个池子。如果你需要更复杂的并行流水线试试concurrent.futures加queue.Queue组合那才叫游刃有余。异常别用裸的 except写脚本时最容易犯的错就是except Exception:一兜到底。表面上看起来很安全实际上掩盖了所有问题包括你程序里的逻辑缺陷。异常处理的目标是失败得明明白白而不是失败得无声无息。区分KeyError、FileNotFoundError、PermissionError分别给出不同的处理策略和日志信息。对于你自己命名的业务异常定义个ScriptError(Exception)再继承出几个子类让错误一抛出来就带着语境。还有一个反直觉的技巧在try块里放尽量少的代码只包住真正可能出错的那一行。千万不要把整个函数都塞进try里否则一旦出错你都不确定是哪个变量是空的哪个资源没关。用with管理文件、锁、数据库连接让异常发生时的资源泄漏概率降到零。超时脚本的保命符自动化脚本里最危险的毒药就是“永远等下去”。网络请求迟迟不响应子进程卡死文件锁拿不到——没有超时控制你的脚本就会变成僵尸在服务器上耗尽内存和CPU。给每一个外部调用都加一个超时时间是脚本成熟的第一步。requests要传timeoutsubprocess要加timeout参数连queue.get()都要带上timeout。有时候超时了还不够脚本还得真正退出。比如你启动了一个ThreadPoolExecutor主线程想退出但工作线程还在阻塞。那就用executor.shutdown(waitFalse)或者干脆用daemon线程。更进一步你能用signal模块处理SIGTERM和SIGINT让脚本在按下 CtrlC 时清理资源、保存中间状态然后体面地离开。配置与代码分离别让脚本成为“硬编码博物馆”你把数据库地址写死在脚本里下次换个环境就要改代码。你把文件夹路径写死别人复制到另一台机器就要改代码。硬编码是脚本丑陋的根源配置分离是脚本运维的第一步。最简单的方式是用os.getenv加默认值复杂一点用configparser读.ini再现代一点用pydantic-settings定义带类型和校验的配置模型。别小看配置文件它可以给脚本提供多套环境开发、测试、生产切换环境只需换一个参数。好的脚本应该做到“一份代码随处运行”。同时把敏感信息密码、密钥放到环境变量或.env文件中千万别提交到 git 仓库。配置有了版本脚本的移植成本就趋近于零。临时文件与清理雁过也要留痕自动化脚本常常需要生成中间文件下载到一半的压缩包、临时拼接的数据、导出的批次结果。这些文件用完不清理会慢慢撑爆磁盘。可如果你手动删一旦脚本中途崩溃残尸还在。用tempfile.TemporaryDirectory来自动处理临时目录是最高效的——上下文管理器退出时目录连同其中所有文件一起被销毁无论中途有没有异常。如果需要保留结果文件那就在完成时用shutil.move把它移出临时目录再放到目标位置。把临时状态与正式结果严格区分脚本才是可预测的。另外给临时文件命名时加上进程号或时间戳避免并发运行同一个脚本时互相踩踏。打包分发脚本的终极形态你写了一个超级好用的脚本结果同事跑过来说“我没装 Python帮我跑一下”。这时候把脚本打包成可执行文件才是真正的交付。PyInstaller是最常用的工具一行命令就能生成一个exe或可执行二进制。打包不是锦上添花是让脚本摆脱解释器依赖的唯一出路。你可以用--onefile生成单文件方便分发但要注意单文件启动时会解压到临时目录首次运行可能有点慢。打包前记得用--hidden-import把动态导入的模块也拉进来。另外把配置文件和资源文件放到--add-data里这样脚本就能在定制环境中自由运行。一个能交付的脚本比一百个只活在 IDE 里的脚本更有价值。最后给你自己留个后门在__main__里加个--version输出方便追踪你给同事的哪一版出了问题。写到这里你会发现这些技巧没有一个是改变世界的大招它们全是磨刀石的功夫。脚本的可靠不是靠一次写对而是靠耐心地处理边界情况。当你今天用pathlib替换了os.path.join明天给网络请求加上超时后天为任务加上重试——你的脚本就不再是一堆会腐烂的代码而是一台能够自己运转的机器。真正的自动化不是省掉手动操作那一刻的爽快而是日后每一天不看它它也能老实干活的从容。把这几个技巧刻进你的肌肉记忆你的收藏夹可以清空一半了。