每天下午五点半我一边盯着屏幕把二十几个Excel表挨个打开复制粘贴一边在心里算今天又在这种破事上烧了多久。后来我把这套动作换成了run一个脚本整个过程从二十五分钟左右压到十秒以内。今天整理这10个Python自动化脚本就是想把你手上那些重复得毫无意义的工作全部交给代码去跑。这篇文章里的脚本全部是可以直接复制粘贴、改两行路径就用的版本。覆盖文件整理、Excel汇总拆分、PDF合并、图片压缩、日志清理、网页监控提醒、定时消息推送这些日常最耗时的场景。每个脚本我都标注了适用人群和容易踩的坑适合已经被重复劳动折磨了一段时间、刚学完Python基础语法却不知道用来干什么的读者。哪怕你只用到里面两三个按每天各省半小时算一个月下来也不少了。1. 为什么是这10个脚本我按什么标准挑的网上聊Python自动化脚本的文章一抓一大把但大多数要么是玩具代码跑完就忘要么是刻意炫技日常根本用不上。我这10个脚本的筛选标准很简单必须是过去一年里我自己电脑上真实跑过、且频率不低于一周两次的。另一个标准是零思考成本。真正能省时间的脚本不是那种每次用之前还要看半天文档回忆参数的东西而是双击就能跑错了能看懂报错的级别。所以下面所有的脚本我都会尽量用Python标准库搭配少量最主流的第三方库pandas、Pillow、requests避免引入那些维护不积极、装起来还容易出问题的小众包。还有一点很重要脚本要克制。市面上很多自动化教程喜欢把脚本写得很全能什么参数都有配置项铺一大堆。但我的实际体验是一个脚本只解决一个痛点写得越短越好维护。你的时间应该花在决定要自动化什么上而不是花在调试一个想解决一切问题的脚本上。2. 动手之前先把Python环境一次配好2.1 别直接用系统Python装一个Miniconda省心很多很多初学者第一步就卡在环境上。系统自带的Python尤其是macOS和Linux自带的通常版本老而且直接往系统环境里pip装包很容易把依赖搞乱。我建议所有做自动化脚本的人统一用Miniconda管理Python环境。下载安装Miniconda之后创建一个专门跑脚本的环境conda create -n auto python3.10 -y conda activate auto装依赖只需要一条命令下面几个包基本覆盖今天所有脚本的需求pip install pandas openpyxl pypdf Pillow requests pyperclip我特别想强调pandas和openpyxl的搭配。很多人处理Excel直接用openpyxl死磕遇到稍微大一点的文件就慢得难受。pandas底层处理逻辑快得多日常办公级别的数据量几万行以内完全是秒开。2.2 明确脚本目录和路径规划环境配好之后先在用户目录下建一个专门的文件夹比如~/auto_scripts/然后按类型建子目录。我自己是这么分的auto_scripts/ ├── file_tools/ # 文件整理类脚本 ├── data_tools/ # Excel、PDF处理 ├── remind_tools/ # 监控、提醒类 └── logs/ # 脚本运行日志为什么要单独强调目录规范因为自动化脚本最大的敌人不是代码写错而是文件不知道被谁移动了。路径一旦写死脚本就废了一半。我后面给的每个脚本都会把你需要改的路径统一放在文件开头用大写的配置常量标明这是从无数坑里总结出来的习惯。3. 文件整理下载目录、重命名、日志清理3.1 脚本一下载文件夹自动分类归档这应该是投入产出比最高的一个脚本。想象你每次从浏览器下载文件不管源文件是什么类型都一股脑堆在下载目录里过一个月那个文件夹基本没法看。这个脚本做的事情就是把下载目录里所有文件按扩展名分类移动到对应子目录。import os import shutil from pathlib import Path # 配置区 DOWNLOAD_DIR Path(~/Downloads).expanduser() MOVE_RULES { 图片: [.jpg, .jpeg, .png, .gif, .webp, .svg], 文档: [.pdf, .docx, .xlsx, .pptx, .txt, .md], 压缩包: [.zip, .rar, .7z, .tar, .gz], 安装包: [.dmg, .exe, .pkg, .deb, .rpm], 代码: [.py, .js, .ts, .json, .html, .css], } OTHER_DIR 其他 classify_dir {category: DOWNLOAD_DIR / category for category in MOVE_RULES} classify_dir[OTHER_DIR] DOWNLOAD_DIR / OTHER_DIR for d in classify_dir.values(): d.mkdir(exist_okTrue) for item in DOWNLOAD_DIR.iterdir(): if not item.is_file() or item.name.startswith(.): continue ext item.suffix.lower() target_category OTHER_DIR for category, exts in MOVE_RULES.items(): if ext in exts: target_category category break target_path classify_dir[target_category] / item.name shutil.move(str(item), str(target_path)) print(f移动: {item.name} - {target_category}/)这段代码没有使用os.walk这种大而全的东西因为下载目录一般不需要深递归。Path.iterdir()配合is_file()判断就够了结构清晰。为什么不用item.suffix直接map到目录因为实际使用中下载到一半的临时文件比如Chrome的.crdownload后缀往往会被误判到其他这种反而安全。等文件名恢复正常了下次跑脚本它自然会被归位。3.2 脚本二批量重命名支持正则表达式匹配设计师、运营、做素材整理的朋友一定懂这个痛甲方发来的文件叫什么都有最终版V3改2(1).png这种字段看着就想摔键盘。这个脚本用正则表达式提取文件名关键信息按你指定的模板批量重命名。import re from pathlib import Path # 配置区 TARGET_DIR Path(~/work/project_assets).expanduser() PATTERN r(final|最新|完成|终版) # 要匹配的关键字 REPLACEMENT done # 替换成什么 FILE_FILTER *.png # 只处理png文件 for item in TARGET_DIR.glob(FILE_FILTER): if not item.is_file(): continue new_name re.sub(PATTERN, REPLACEMENT, item.stem) item.suffix if new_name item.name: continue new_path item.with_name(new_name) if new_path.exists(): print(f跳过(重名): {item.name}) continue item.rename(new_path) print(f重命名: {item.name} - {new_name})这里有一个细节值得留意if new_path.exists()的判断。批量重命名最怕的就是覆盖了同目录下已存在的文件别问我怎么知道的。宁可跳过也不要覆盖这是文件操作的第一原则。3.3 脚本三超期日志文件自动清理服务器日志、爬虫运行日志、程序调试输出这些文件日积月累非常占空间。这个脚本做的事情是删除指定目录下超过N天没修改过的指定后缀文件。import os import time from pathlib import Path # 配置区 LOG_DIR Path(~/logs).expanduser() EXPIRED_DAYS 7 FILE_SUFFIXES {.log, .tmp, .bak} now time.time() expired_secs EXPIRED_DAYS * 24 * 60 * 60 removed_count 0 for item in LOG_DIR.rglob(*): if not item.is_file() or item.suffix.lower() not in FILE_SUFFIXES: continue mtime item.stat().st_mtime if now - mtime expired_secs: item.unlink() removed_count 1 print(f清理: {item}) print(f共清理 {removed_count} 个文件)核心逻辑就一个比较文件最后修改时间和当前时间的差超过阈值就删。用rglob(*)而不是glob(*)是为了连子目录下的日志一起处理。删除前打印日志这个动作建议保留万一误删了还能凭输出记录找回一部分。4. 数据处理Excel汇总、拆分、PDF合并4.1 脚本四多个Excel表一键汇总成总表这项工作是很多行政、财务、销售助理的老朋友。每月固定时间把部门所有人的报表汇总成一张总表每个人打开Excel复制-粘贴到汇总表操作不难但极度耗时。用pandas做这事几十行代码就能解决。import pandas as pd from pathlib import Path # 配置区 SOURCE_DIR Path(~/work/monthly_reports).expanduser() PATTERN *.xlsx OUTPUT_PATH Path(~/work/汇总表.xlsx).expanduser() all_data [] for file in SOURCE_DIR.glob(PATTERN): if 汇总 in file.name: # 跳过之前生成的汇总文件 continue df pd.read_excel(file) df[来源文件] file.name # 标记数据来自哪个文件方便溯源 all_data.append(df) print(f读取: {file.name}) if not all_data: print(没有找到需要汇总的文件) else: merged_df pd.concat(all_data, ignore_indexTrue) merged_df.to_excel(OUTPUT_PATH, indexFalse) print(f汇总完成共 {len(merged_df)} 行输出至 {OUTPUT_PATH})这段代码的关键点有两个。第一是df[来源文件] file.name这一步很多人会漏掉但一旦合并后发现某个数字对不上这个来源标记就是定位问题的救命稻草。第二是跳过汇总关键词文件不然第二次运行脚本时会把上次生成的汇总表也读进来越跑数据越多。4.2 脚本五按某列的值自动拆分Excel与汇总相反有时候你手里是一张大表需要按部门、城市、月份之类的维度拆成多张小表发给对应负责人。用Excel筛选另存可能要折腾半小时代码只需要几秒import pandas as pd from pathlib import Path # 配置区 SOURCE_FILE Path(~/work/销售明细.xlsx).expanduser() SPLIT_COLUMN 城市 # 按哪列拆分 OUTPUT_DIR Path(~/work/split).expanduser() OUTPUT_DIR.mkdir(exist_okTrue) df pd.read_excel(SOURCE_FILE) for value, sub_df in df.groupby(SPLIT_COLUMN): safe_name str(value).replace(/, _) # 处理Windows下的非法字符 output_path OUTPUT_DIR / f{SPLIT_COLUMN}_{safe_name}.xlsx sub_df.to_excel(output_path, indexFalse) print(f生成: {output_path.name}{len(sub_df)}行) print(f拆分完成共 {df[SPLIT_COLUMN].nunique()} 个分组)这里用了groupby之后直接遍历分组的写法pandas会自动帮你把同一值的行聚到一起既不用手动去重也不用写循环遍历所有行。拆文件名时用了replace(/, _)这是Windows路径兼容性最常见的坑城市名如果写成上海/南京这种格式不改直接当文件名会直接报错。4.3 脚本六PDF文件批量合并没有对比就没有伤害网上PDF合并工具的会员价格有多离谱大家应该都有印象。其实用Python的pypdf库十行代码就能做完全一样的活from pathlib import Path from pypdf import PdfReader, PdfWriter # 配置区 PDF_DIR Path(~/work/reports).expanduser() OUTPUT_FILE Path(~/work/merged.pdf).expanduser() pdf_files sorted(PDF_DIR.glob(*.pdf)) if not pdf_files: print(目录下没有PDF文件) exit() writer PdfWriter() for pdf_file in pdf_files: reader PdfReader(str(pdf_file)) for page in reader.pages: writer.add_page(page) print(f已添加: {pdf_file.name}) with open(OUTPUT_FILE, wb) as f: writer.write(f) print(f合并完成总页数: {len(writer.pages)})合并PDF的顺序取决于文件名排序所以如果文件名叫1.pdf、2.pdf这样的数字序号sorted()能给出正确的顺序。但如果是第1章.pdf这种带了中文字符的名字排序就未必是你想要的了。稳妥做法是在脚本开头列一个需要合并的文件列表手动控制顺序。5. 网络监控与消息推送5.1 脚本七网页内容变化监控变化时发邮件通知抢课、查成绩、盯官网公告、看产品页面降价这些场景本质都是同一个需求定时去看看某个网页内容变了就通知我。这个脚本的思路是先把网页内容抓下来算一个哈希值下次再抓哈希值变了就说明页面变了。import hashlib import smtplib import time from email.mime.text import MIMEText from urllib.request import urlopen # 配置区 TARGET_URL https://example.com/notice CHECK_INTERVAL 60 * 10 # 每10分钟检查一次 STATE_FILE page_state.txt # 邮件发送配置 SMTP_SERVER smtp.qq.com SMTP_PORT 465 EMAIL_USER your_emailqq.com EMAIL_PASS 你的授权码 ALERT_TO receiverexample.com def get_page_hash(url: str) - str: with urlopen(url, timeout10) as resp: content resp.read() return hashlib.md5(content).hexdigest() def send_alert(url: str): msg MIMEText(f检测到页面变化请查看: {url}, plain, utf-8) msg[Subject] 网页变化提醒 msg[From] EMAIL_USER msg[To] ALERT_TO with smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT) as server: server.login(EMAIL_USER, EMAIL_PASS) server.send_message(msg) def main(): current_hash get_page_hash(TARGET_URL) try: with open(STATE_FILE, r, encodingutf-8) as f: old_hash f.read().strip() except FileNotFoundError: old_hash if old_hash and old_hash ! current_hash: send_alert(TARGET_URL) print(页面变化已通知) with open(STATE_FILE, w, encodingutf-8) as f: f.write(current_hash) while True: try: main() except Exception as e: print(f检查失败: {e}) time.sleep(CHECK_INTERVAL)哈希值对比是简化处理好处是不用关心网页结构坏处是页面里只要有动态时间戳或随机推荐位就会造成误报。如果你监控的页面变化不大可以用一个更精细的策略只抓页面里某个特定元素的文本内容来做对比比如用正则提取公告区域的文字。这个脚本我建议配合后面第7部分讲的cron来跑而不是挂着while True常驻因为Windows下挂着容易休眠断掉Linux下也容易忘记管理进程。5.2 脚本八通过Webhook向群里推送每日定时消息现在很多团队都用企业微信、钉钉或飞书办公这些平台都支持Webhook机器人。你只需要往一个URL地址POST一段JSON就能把消息发进群里。这个脚本做的就是在固定时间把当天需要大家知悉的内容推送到群里比如早会提醒、日报汇总、待办事项。import json import urllib.request # 配置区 WEBHOOK_URL https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key你的key def send_text(content: str): data { msgtype: text, text: { content: content, mentioned_list: [all] } } req urllib.request.Request( WEBHOOK_URL, datajson.dumps(data).encode(utf-8), headers{Content-Type: application/json} ) with urllib.request.urlopen(req, timeout10) as resp: result json.loads(resp.read().decode(utf-8)) if result.get(errcode) 0: print(消息推送成功) else: print(f推送失败: {result}) if __name__ __main__: today_report 今天的发布计划\n1. 10:00 前端联调\n2. 14:30 数据核对 send_text(today_report)用标准库urllib.request而不是requests对很多人来说可能觉得多此一举。但Webhook推送这种场景功能太单一引入requests写起来也没简单多少反而在离线环境下装不了库就麻烦了。如果你已经装了requests当然也可以用两种都跑得起来。Webhook机器人的关键限制是频控一般是每分钟最多20条。日常推送完全够用但如果你的脚本里循环发送大量消息就会触发限流报错代码通常是请求太频繁。6. 图片与媒体文件批处理6.1 脚本九图片批量压缩给硬盘和网页瘦身运营和电商的朋友应该深有体会一个几MB的素材图直接放进网页打开速度直线下降。这个脚本把指定目录下的所有图片按比例压缩并输出到新目录。from pathlib import Path from PIL import Image # 配置区 SOURCE_DIR Path(~/work/images).expanduser() OUTPUT_DIR Path(~/work/images_compressed).expanduser() TARGET_WIDTH 1920 # 宽度超过这个值才压缩 QUALITY 80 # 压缩质量 1-100 OUTPUT_DIR.mkdir(exist_okTrue) for file in SOURCE_DIR.glob(*.jpg): img Image.open(file) original_size file.stat().st_size / 1024 / 1024 if img.width TARGET_WIDTH: ratio TARGET_WIDTH / img.width new_height int(img.height * ratio) img img.resize((TARGET_WIDTH, new_height), Image.LANCZOS) output_path OUTPUT_DIR / file.name img.save(output_path, JPEG, qualityQUALITY, optimizeTrue) compressed_size output_path.stat().st_size / 1024 / 1024 print(f{file.name}: {original_size:.2f}MB - {compressed_size:.2f}MB)Pillow库负责解码和编码Image.LANCZOS重采样算法在缩图时画质损失最小。optimizeTrue会对图片做优化编码文件还能再小一点。注意图片格式复杂时最好在保存路径上加后缀判断比如PNG带透明通道的图强行存成JPEG会丢背景所以上面的代码只处理了.jpg文件PNG的情况需要单独处理。6.2 脚本十视频批量抽帧做素材预览或剪辑辅助对于做短视频、做剪辑的人来说从视频里按固定间隔抽帧出来查看内容是非常高频的操作。这个脚本用OpenCV把视频每隔N秒取一帧存成图片import cv2 from pathlib import Path # 配置区 VIDEO_PATH Path(~/work/video/source.mp4).expanduser() OUTPUT_DIR Path(~/work/video/frames).expanduser() INTERVAL_SECS 5 OUTPUT_DIR.mkdir(exist_okTrue) cap cv2.VideoCapture(str(VIDEO_PATH)) fps cap.get(cv2.CAP_PROP_FPS) frame_interval int(fps * INTERVAL_SECS) total_frames int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) count 0 saved 0 while True: ret, frame cap.read() if not ret: break if count % frame_interval 0: timestamp count / fps output_name OUTPUT_DIR / fframe_{timestamp:.1f}s.jpg cv2.imwrite(str(output_name), frame) saved 1 print(f保存: {output_name.name}) count 1 cap.release() print(f抽取完成共 {saved} 帧)运行前记得pip install opencv-python。cv2.CAP_PROP_FPS这个参数有些视频文件读取可能会返回0这种情况处理方式是在配置区直接手动指定fps值比如视频是30帧/秒就写30。抽帧脚本最忌讳的是把参数都写死在逻辑里一旦视频分辨率变化又要改代码用配置常量就能避免这种麻烦。7. 让脚本真正每天自己跑定时任务配置脚本写好了但每天手动打开终端运行它那等于没自动化。真正省时间的关键一步是把脚本挂到系统定时任务上。Windows和macOS/Linux的配置方式不同我都说一下。7.1 Windows使用任务计划程序按WinR输入taskschd.msc打开任务计划程序创建基本任务触发器选择每天设置执行时间操作选择启动程序程序或脚本填写你的Python解释器绝对路径conda环境下是在~/miniconda3/envs/auto/python.exe这种路径添加参数填写脚本绝对路径比如C:\Users\你的用户名\auto_scripts\file_tools\organize.py起始于填写脚本所在目录很多人配置完定时任务发现不执行九成是程序或脚本那里填成了python而不是python解释器的完整路径。因为定时任务环境不加载你命令行里的环境变量。7.2 macOS / Linux使用crontab在终端输入crontab -e添加一行0 9 * * * /Users/你的用户名/miniconda3/envs/auto/bin/python /Users/你的用户名/auto_scripts/file_tools/organize.py /Users/你的用户名/auto_scripts/logs/organize.log 21这行的意思是每天早上9点运行一次脚本并把标准输出和错误输出都追加到日志文件里。加上21是必须的否则脚本报错了你看不到任何信息还以为一切正常。7.3 定时任务排错的基本功我不止一次在定时任务踩坑脚本手动跑没问题一放进cron就没反应。排查顺序是这样的看日志文件有没有内容没有说明cron没执行或路径错了确认python路径是否绝对路径cron环境不加载shell配置确认脚本文件有执行权限Linux下用chmod x script.py确认脚本中没有依赖相对路径的文件操作最好把所有路径都改成绝对路径8. 实际用下来我最想提醒你的几件事脚本能跑起来只是第一步用得久不踩坑才是真功夫。我在过去写自动化脚本的过程中积累了几条经验这里直接分享给大家。路径统一用Path对象而不是字符串拼接字符串拼接路径在Windows上容易遇到反斜杠和转义符的问题而且代码很丑。pathlib.Path用/运算符组合路径跨平台不会错代码也更易读。给脚本加日志输出一个print看似简单但配上 log.txt重定向出问题时能省十几倍排查时间。尤其是定时任务里跑的脚本没有日志等于盲跑。脚本之间尽量保持独立不要在一个脚本里既做文件整理又做数据汇总再发邮件。每件事拆成一个独立脚本然后按需组合。这样任何一个环节出问题影响范围都可控。定期review一下脚本逻辑自动化脚本跑久了文件路径、目录结构、依赖库都可能变化。我习惯每季度花一点时间检查一遍还在使用的脚本顺手把过时的正则表达式、废弃的API改掉。这个投入非常值。使用变量保存关键路径不要硬编码你可能只是把脚本复制给别人用一次但三个月后你自己也要用。把下载目录、汇总表路径、检查间隔这些选项放到脚本开头统一管理改动成本会低很多。这10个脚本覆盖的场景各不相同但它们背后的思路是一致的把规则明确、重复度高、没有创造性的工作交给代码去做。我个人的建议是从你最有感知的一个痛点开始先选定一个脚本跑通它然后让它进入你的日常节奏再逐步增加新的自动化项。自动化不是一次配完就结束它是一个持续迭代的过程。希望我的这些脚本和经验能帮你真正从重复劳动里抽出一点时间去做点更有价值的事。