每天上班打开电脑,先面对一堆文件名乱七八糟的表格、文件夹里散落的文档、重复下载的安装包,还没开始正式干活就已经烦了。如果你也有这种感觉,那Python自动化脚本就是那个能真正解放双手的东西。这篇文章要聊的,不是那种只能跑一次就丢的一次性代码,而是我在日常工作里反复用了很久的10个脚本,覆盖文件整理、数据汇总、日志分析、系统监控、定时备份这些最常见的重复场景。无论你是运维、测试、数据分析,还是日常被Excel折磨的行政岗位,都能从里面挑出几个直接能用的。
我不太喜欢那种列了一堆代码但根本不解释为什么这么写的教程,所以下面每个脚本都会说清楚适用场景、核心思路、需要注意的坑,以及我会踩完之后才总结出来的细节。你可以直接照着抄,也可以改造成适合自己的版本。
1. 开工前先看清全局:几条设计思路
1.1 自动化脚本解决什么问题
很多人一开始接触Python自动化,容易陷入一个误区——看到什么都要写脚本,结果写脚本的时间比手工操作还长。我自己的判断标准很简单:一件事如果每周要做超过两次、每次超过五分钟、而且步骤完全重复,那它就值得自动化。比如每天从不同系统导出的Excel表格要合并、每周要清理一次临时目录、每天要看一遍日志有没有报错,这些事情的特点是机械、规则明确、不需要太多判断,交给脚本处理远比人肉操作稳定。
另一个判断标准是容错成本。手工操作一百次可能会出两三次错,脚本跑一百次结果都一样。尤其那些涉及重复命名的任务,你手动改个一百个文件,总有一个会手滑,脚本不会。所以自动化不只是省时间,更重要的是消除人为失误带来的不确定感。我会在编写脚本时默认“第二次运行也不能出事”,而不是只保证第一次能跑通,这是自动化脚本和普通脚本最本质的差别。
还有一个思路上的问题值得提前说:别追求一步到位。我见过很多人一上来就想写一个几百行的超级脚本,把所有需求都塞进去,结果跑起来之后一个问题接一个问题。更务实的做法是把一个任务拆成几个小脚本,或者用函数把功能拆开,每个函数只做一件事。这样就算某个环节出问题,也容易定位,不会牵一发动全身。
1.2 工具链与运行环境的准备
工欲善其事必先利其器,但不需要把环境搞得特别复杂。我建议的标准配置是Python 3.10以上版本,加一个虚拟环境目录,所有依赖都装在虚拟环境里,避免和系统环境互相污染。平时用到最多的第三方库就那几样:操作文件和目录用标准库的os、shutil、pathlib,处理表格用pandas或者openpyxl,抓网页用requests加BeautifulSoup,定时任务用schedule,截图用pyautogui。这些库全部用pip安装,遇到装不上的情况多半是网络源的问题,换国内镜像源就能解决。
运行环境这块,Windows、macOS、Linux都行,但要注意路径分隔符和编码的差异。我在Windows上习惯用pathlib替代os.path,因为它对跨平台支持更好,代码里不需要写一堆判断。另外建议大家把脚本统一放在一个目录里,比如用户主目录下的automation文件夹,再按照脚本用途命名,跑的时候方便,后续维护也方便。
pip install pandas openpyxl requests beautifulsoup4 schedule pyautogui关于定时执行,我的做法是优先用系统自带的任务计划程序,而不是让脚本自己一直占着进程。Windows下面的任务计划程序、Linux下面的crontab,都是稳定的选择。脚本本身只负责干活,什么时候干活交给系统调度,这样就算电脑重启了,计划任务也会照常触发。
2. 文件与办公自动化:最先解放双手的5个脚本
2.1 文件自动归档:按扩展名分门别类
桌面和下载文件夹乱成一锅粥是很多人的常态。我之前有个习惯,下载的文件全部堆在一个目录里,时间一长,要找某个PDF就只能靠搜索。后来写了一个自动归档脚本,核心逻辑很简单:扫描目标目录下的所有文件,按照扩展名分类,然后移动到对应的子文件夹,比如.jpg放到图片文件夹,.docx放到文档文件夹,.zip放到压缩包文件夹。
代码逻辑并不复杂,无非是遍历文件、取出后缀、判断分类、移动文件。但真正要注意的是两个细节。第一,移动之前必须判断目标目录里是否已经存在同名文件,如果存在就在文件名后面加时间戳,避免覆盖。第二,程序文件本身、正在被占用的文件、以及隐藏文件要跳过。另外我建议先跑一轮调试模式,只打印出每个文件会被移动到哪个位置,确认无误之后再真正执行move操作。这个习惯帮我避免过好几次误移动系统重要文件的尴尬。
from pathlib import Path import shutil target_dir = Path.home() / "Downloads" mapping = { ".jpg": "Images", ".jpeg": "Images", ".png": "Images", ".gif": "Images", ".doc": "Documents", ".docx": "Documents", ".pdf": "Documents", ".txt": "Documents", ".xls": "Sheets", ".xlsx": "Sheets", ".csv": "Sheets", ".zip": "Archives", ".rar": "Archives", ".7z": "Archives", } for file in target_dir.iterdir(): if file.is_file() and file.suffix.lower() in mapping: dest = target_dir / mapping[file.suffix.lower()] / file.name if dest.exists(): dest = dest.with_stem(f"{dest.stem}_{int(time.time())}") shutil.move(str(file), str(dest))这个脚本我通常配合Windows任务计划程序每天下午五点跑一次,正好把当天下载的一堆乱七八糟文件收拾干净。实际运行中需要留意的一个点是对中文文件名和空格的处理,Python标准库在Windows上对Unicode路径支持还可以,但某些老程序生成的乱码文件名会直接导致脚本中断,所以建议在异常处理里捕获所有异常并记录到日志文件,千万不要让脚本因为一个特殊文件就整个挂掉。
2.2 批量重命名:一句话让数百文件名变规范
批量重命名是另一个高频需求。摄影师朋友会有一堆IMG_20240101_001.jpg这样的原始照片名,产品经理会有"最终版v2(1)(最终).docx"这样的产物。用Python来做这件事,核心是用正则表达式提取原有文件名里的关键信息,再按照自己的规则重新拼接。比如说,把一批日志文件按照访问日期命名,或者把所有jpeg后缀统一改成jpg,都可以用脚本一次性处理。
我写过一个相对通用的重命名脚本,支持两种模式:替换文件名中的指定字符串,或者在文件名前加固定前缀。实现思路是用pathlib的rename方法,先构造新名字,再判断目标是否存在,避免覆盖。这里需要特别提醒:重命名是有风险的,批量重命名一定要先在测试目录跑一遍,或者用dry run模式打印出新旧名字对照关系,确认规则没有问题再真正执行。
from pathlib import Path import re folder = Path(r"D:\work\files") for file in folder.iterdir(): if file.is_file(): new_name = re.sub(r"\s+", "_", file.stem) + file.suffix new_name = re.sub(r"最终版|副本|\([0-9]\)", "", new_name) target = file.with_name(new_name) if target.exists(): print(f"skip {file.name} -> {new_name}") else: file.rename(target)正则表达式里圆括号要尤其小心,因为它是元字符,直接写会当成分组处理。上面代码里我用转义的方式处理了"(1)"这种情况。另外一个容易忽略的问题是不同操作系统对文件名的长度限制,Windows下路径总长度有限制,超过255个字符就会报错,所以如果文件名很长,建议先把路径缩短或者改名规则简化。
我个人的经验是,重命名脚本的价值不在于一次处理几百个文件,而在于它是可复用的。下次遇到类似需求,只要改动一下正则表达式,就能解决新问题。这也是为什么我建议把类似的通用函数集中放到一个util.py里,方便复用。
2.3 Excel多表自动汇总:告别复制粘贴
Excel合并大概是行政、财务、人事这些岗位最深的痛。每周要把各团队发来的几十张表合成一张总表,每张表格式还不完全一样,手工操作又慢又容易漏行。用pandas来处理这类问题效率高很多,思路是把所有同构表格读入列表,然后用pd.concat纵向拼接,最后统一导出。
实际处理时要特别留意表头不统一的问题。有些表第一列叫“姓名”,有些叫“员工姓名”,有些表前面多两行说明文字,直接读取会把说明文字当成数据。我通常会在读取时指定header参数跳过前N行,再来做列名标准化。还有一个是编码问题,很多人的Excel导出其实是xlsx,用pandas的read_excel处理没问题,但如果遇到的是csv,就要小心编码,常见的有utf-8、gbk、gb2312,读出来乱码就换编码再试。
import pandas as pd from pathlib import Path folder = Path(r"D:\reports") all_sheets = [] for f in folder.glob("*.xlsx"): df = pd.read_excel(f, header=0) all_sheets.append(df) result = pd.concat(all_sheets, ignore_index=True) result.to_excel(folder / "merged.xlsx", index=False)这种脚本一旦跑通,节省的时间是肉眼可见的。之前我帮某位朋友处理过一次50多张表的合并,原来她至少要一上午,脚本跑起来十几秒就完事。不过要提醒一句,合并之前务必检查每张表的列顺序,如果列名一致但顺序不同,concat出来的结果会非常混乱。更稳妥的做法是先统一列的顺序,再执行合并。
2.4 重复文件清理:找出并删除冗余内容
重复文件清理是我强烈建议每个人都试一下的方向。电脑用了几年之后,同一张照片、同一个PDF、同一个压缩包会在不同文件夹里出现多次,白白占用大量磁盘空间。识别重复文件最简单的方案是计算文件哈希值,比如MD5,哈希值相同就说明文件内容一致。不过对大文件来说,先比较文件大小可以大幅减少计算量,大小不同的文件不可能内容相同,只有大小一样的才需要进一步计算哈希。
from pathlib import Path import hashlib folder = Path.home() / "Desktop" size_map = {} for f in folder.rglob("*"): if f.is_file(): size = f.stat().st_size size_map.setdefault(size, []).append(f) duplicates = [] for size, files in size_map.items(): if len(files) > 1: hashes = {} for f in files: md5 = hashlib.md5(f.read_bytes()).hexdigest() hashes.setdefault(md5, []).append(f) for same in hashes.values(): if len(same) > 1: duplicates.extend(same[1:]) for f in duplicates: print("duplicate:", f)实际写的时候,不要直接删除,先打印出重复文件列表,手动看一眼,确认没问题再删除。因为有些文件虽然哈希值一样,但可能分别位于不同项目目录里,两边都有引用,删了会影响项目完整性。我身边就有同事因为跑了个清理脚本把关联文件删掉,结果项目编译失败的案例。所以这个脚本我坚持只负责“找出”,删除动作由人来做,至少也要默认移动到回收站而不是物理删除。
2.5 屏幕自动截图记录:开会/演示的补救神器
这个脚本的使用场景比较特殊,但一旦用上就很依赖。有时候你在远程开会或者做演示,需要记录整个过程的屏幕画面,或者给培训写文档需要每隔一段时间截一下屏幕状态。用pyautogui可以很简单地定时截图,配合系统任务计划,就能实现每隔几十秒自动保存一张截图。
import pyautogui import time from pathlib import Path out_dir = Path.home() / "screenshots" out_dir.mkdir(exist_ok=True) while True: ts = time.strftime("%Y%m%d_%H%M%S") img = pyautogui.screenshot() img.save(out_dir / f"shot_{ts}.png") time.sleep(60)需要注意的是,pyautogui在Windows上截图基本没什么问题,但在某些远程桌面服务下面会截到黑屏,因为系统禁止了屏幕捕获。还有一个隐私问题,如果你屏幕上有聊天窗口、邮件正文之类的内容,一定要确认这些内容是允许被记录的,否则截出来的图会给自己惹麻烦。我的习惯是只在开完会之后跑一小段,或者只截自己本地的文档页面,不会全天候开。
3. 数据搜集与处理脚本:让信息自己找上门
3.1 日志文件分析与异常告警
如果你管过几台服务器,一定体会过每天登录上去翻日志的感觉。日志文件可能几百兆,手动打开根本翻不到重点。用Python做日志分析,核心是按行读取文件,用正则表达式提取时间、级别、报错信息,然后把ERROR级别的条目筛出来,统计频率,最后把结果写入一个汇总文件,或者通过邮件、企业微信机器人发出来。这个脚本的意义不在于找到所有错误,而在于帮你把需要人工关注的异常收敛到一个很小的范围。
import re from pathlib import Path log_path = Path(r"/var/log/app.log") errors = [] pattern = re.compile(r"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) (ERROR|WARN) (.+)") with log_path.open(encoding="utf-8", errors="ignore") as f: for line in f: match = pattern.search(line) if match: errors.append(match.groups()) for item in errors[-50:]: print(item)我踩过的一个坑是日志编码。Linux服务日志多半是UTF-8,但有些老服务用系统默认编码,你直接用open()读取会报UnicodeDecodeError。最简单的处理方式是在open时加上errors="ignore",或者用errors="replace",保证脚本在任何编码下都能跑完。另一个问题是日志的滚动机制,如果日志文件已经被logrotate重命名,脚本读取的可能是旧的或者空的文件,所以建议脚本里先检测当前活动日志文件的修改时间,如果太旧就提示一下。
3.2 网页信息自动抓取:比价与新闻聚合
网页抓取是Python自动化的经典场景。你可以用它定时抓商品价格、聚合几个站点的新增内容、监控某篇文章是否更新。实现基础是requests库发送HTTP请求,然后用BeautifulSoup解析HTML,定位到目标数据所在节点,提取文本或者属性值。整个过程不难,但确实有一些规范和坑需要知道。
首先是robots协议的问题。你要抓取的网站如果明确禁止爬虫访问某些路径,从合规角度讲就不应该去碰。其次要控制请求频率,我一般会加上time.sleep(x),避免短时间内发出大量请求。还有就是反爬措施,很多站点会校验User-Agent、Cookie、请求头,所以我会在请求里设置一个常见的浏览器User-Agent,必要时带上登录后的Cookie。
import requests from bs4 import BeautifulSoup import time headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"} url = "https://example.com/list" resp = requests.get(url, headers=headers, timeout=10) soup = BeautifulSoup(resp.text, "html.parser") for item in soup.select(".item-title"): print(item.get_text().strip())我做过一个简单的商品价格监控脚本,每天固定时间抓一次目标商品的页面,把价格写入CSV,连续记录一个月就能看出价格波动趋势。这里要特别提示:不要去抓需要登录才能访问的会员数据,也不要去抓个人隐私相关的页面,没有必要也没有正当性。抓公开信息、监控自己运营的站点、采集研究用的数据这些都还好,别越界。
解析HTML最容易出的问题是页面结构一变选择器就失效,所以脚本里我会把选择器和目标URL放在配置字典里,结构变动时只改配置,不改逻辑。还有一个容易忽略的点是页面是否需要JavaScript渲染,如果数据是异步加载的,直接用requests拿到的HTML里没有内容,这时候要么改用selenium模拟浏览器,要么找页面内置的JSON数据接口,后者效率更高。
3.3 批量图片压缩与格式转换
图片压缩这件事,看起来跟自动化关系不大,但真遇上封面图、产品图、培训素材要批量处理时,没有脚本就只能一张一张用软件导。用Pillow处理非常简单,打开图片、设置尺寸或质量、保存为新文件。批量处理的脚本核心是遍历目录里的图片文件,把超过指定宽度的图片等比缩小,或者把PNG转为JPG来减小体积。
from PIL import Image from pathlib import Path folder = Path(r"D:\images") for f in folder.glob("*.png"): img = Image.open(f) if img.width > 1920: ratio = 1920 / img.width new_size = (1920, int(img.height * ratio)) img = img.resize(new_size, Image.LANCZOS) img.save(f.with_suffix(".jpg"), quality=85, optimize=True) print(f"converted {f.name}")转JPG时要特别注意透明背景的问题,PNG转JPG会把透明区域变成黑色,如果原图有透明背景,必须先把透明通道合成到白色底上,否则输出图会很难看。另外Pillow的resize在不同版本里插值算法名称有变化,新版本用Image.LANCZOS,老版本写法是Image.ANTIALIAS。压缩质量参数也不是越小越好,我一般用85,肉眼几乎看不出差别,体积却能小一大截。
这个脚本的现实价值很直接:一个满载图片素材的文件夹可能从200多M压缩到40多M,往聊天工具里发就不会再出现“文件过大”的提示。我平时会把这个脚本和文件归档脚本串起来,先压缩,再按类型归档,一条命令完成两条线。
4. 系统维护与远程任务:后台运行的小管家
4.1 磁盘空间监控与自动清理
磁盘满常常是线上事故的第一导火索。数据库日志、临时文件、打包产物,不知不觉就能把磁盘占满。写一个磁盘监控脚本,核心是用shutil.disk_usage拿到磁盘的总空间、已用空间和剩余空间,当剩余比例低于某个阈值时,进一步扫描指定目录下的大文件,把超过N天没有访问的文件列出来,确认后删除或移动到备份盘。
import shutil from pathlib import Path import time usage = shutil.disk_usage("/") percent = usage.free / usage.total * 100 print(f"free: {usage.free / 2**30:.2f} GB, {percent:.1f}%") if percent < 10: folder = Path("/var/log") cutoff = time.time() - 30 * 86400 for f in folder.rglob("*"): if f.is_file() and f.stat().st_mtime < cutoff: print("candidate:", f, f.stat().st_size)关于“删除”这件事,我一直坚持一个原则:脚本只完成任务清单里明确允许删除的文件,而且首先打印计划,然后删除时先移动到系统回收站或者临时隐藏目录,观察一段时间没有任何问题再彻底清除。自动清理和人工确认之间要有一个缓冲期,这是我的基本操作习惯。
还要注意磁盘空间的统计粒度。Linux上某些目录挂载在不同的盘,你不能只检查根目录,要分别检查各个挂载点。Windows上要检查所有盘符。我在脚本里把待检查路径放在一个列表里,遍历检查,这样后续新增盘只要改配置。
4.2 定时备份与分发:重要文件双保险
备份是很反人性的事,大家都知道重要,但很难坚持每天手动操作。我用schedule库写了一个定时备份脚本,每天固定时间把指定的项目目录打包成zip,存到另一个磁盘分区,同时保留最近7天的备份,超过7天的自动清理,避免备份文件越积越多。
import schedule import time import zipfile from pathlib import Path src = Path(r"D:\work\project") backup_dir = Path(r"E:\backups") backup_dir.mkdir(exist_ok=True) def do_backup(): ts = time.strftime("%Y%m%d_%H%M%S") zip_path = backup_dir / f"backup_{ts}.zip" with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf: for f in src.rglob("*"): if f.is_file(): zf.write(f, f.relative_to(src)) keep = 7 for old in sorted(backup_dir.glob("backup_*.zip"))[:-keep]: old.unlink() print("backup done:", zip_path) schedule.every().day.at("18:00").do(do_backup) while True: schedule.run_pending() time.sleep(30)这个脚本里有几个容易忽略的点。第一,schedule库的while True循环如果脚本进程退出,定时任务就全没了,所以需要配合系统任务计划或者nohup挂在后台。第二,打包时要注意排除那些不需要备份的目录,比如node_modules、.git、缓存目录,否则备份文件会又大又杂。第三,备份文件要放到不同磁盘,如果源盘和目标盘在同一块物理硬盘上,备份的价值大打折扣。
还有一点,zipfile的路径处理要用relative_to,不然会把绝对路径整个写进压缩包,解压出来是一长串目录。这种细节虽然不影响备份完整性,但恢复时会额外增加一层目录,看着很别扭。
5. 常见问题与排查技巧实录
脚本写多了,你会发现自动化脚本真正跑挂的时候,往往不是逻辑问题,而是各种环境问题。这里整理一份我遇到的典型问题速查表,希望对你有用。
| 问题现象 | 常见原因 | 解决办法 |
|---|---|---|
| 脚本一运行就报ModuleNotFoundError | 依赖库没装或者装错环境 | 检查pip list,确认当前使用的是不是虚拟环境 |
| 中文路径/文件名乱码或报错 | 控制台或文件编码不匹配 | Python源码头部声明utf-8,输出时用errors="ignore" |
| 文件被占用导致脚本中断 | Windows下文件正在被程序打开 | 先跳过该文件,记录日志,最后统一处理失败项 |
| 定时任务不执行 | 计划任务配置的Python路径不对 | 在计划任务里写明虚拟环境python.exe的绝对路径 |
| 抓取页面返回403 | 缺少User-Agent或触发反爬 | 添加浏览器UA,降低请求频率,设置Cookie |
| 脚本跑得好好的突然漏文件 | 文件名超出系统长度限制 | 用pathlib处理路径,或者缩短目标目录层级 |
| 用pandas读Excel报错 | 文件里有多行表头或者空行 | 指定header=0或skiprows,先print前几行确认结构 |
| 打包zip解压后多一级目录 | write时使用了绝对路径 | 用f.relative_to(src)作为arcname参数 |
| 磁盘清理误删了重要文件 | 清理条件太宽松 | 先打印候选文件列表,人工确认后再清理 |
排查思路也有一个基本套路:先看报错信息最尾部,区分是语法错误、依赖错误还是运行逻辑错误;然后最小化测试,把脚本拆成一段段,逐段验证中间输出结果。比如文件归档脚本没动,那就先打印文件列表。数据抓取脚本返回空,那就先打印response.status_code和前200字节内容。不要一上来就怀疑逻辑,大部分问题都在环境和输入数据上。
我还想分享一个习惯:每个脚本都写一个日志输出函数,把运行时间、处理文件数、异常信息都写进log文件。这样即使脚本半夜定时跑出问题,第二天查日志也能很快定位到是哪一步失败的。别看这个动作小,它能让整个脚本库的可维护性提高一个档次。用最简单的logging模块就行,设置一下日志级别和输出格式,不要在那个循环里print一堆东西,否则日志文件会很快膨胀。
最后再说说我自己的扩展思路。第一个是脚本参数化,把那些可能变化的目录、文件类型、阈值都改成命令行参数或者配置文件,这样脚本本身不用频繁改动。第二个是集成通知,脚本跑完或者跑挂都推送一条消息到自己的手机,我现在用的是企业微信机器人,POST一个webhook就能完成,非常简单。第三个是把常用脚本集中放到一个目录,并且统一包一层入口函数,比如所有备份类脚本都在backup.py里,所有文件整理都在organize.py里,用的时候只记住这几个文件名就够了。
自动化这件事,真正的成就感不在于你把多少工作交给了脚本,而在于你多出来多少可以自由支配的时间。我最初也只是写了第一个文件整理脚本,后来才慢慢积累出整个工具库。所以不必想着一次性搞定所有任务,从今天最烦的那个重复劳动开始,写一个小脚本解决它,然后把它维护好。用不了太久,你也会有自己的自动化脚本集合。
根据我个人的经验,坚持用“配置化管理+日志记录+人工确认删除”这套组合,是自动化脚本能长期稳定跑下去的关键。别的都不算太难,难的是遇到一次误删或者一次定时任务静默失效之后就再也不敢自动化。所以请一定善待你的脚本,给它足够的日志和失败兜底,它才会真正成为你信得过的帮手。