每天打开电脑,总有一堆重复操作等着你:把十几个Excel表合成一张、把上千张照片压缩一遍、盯着网页等某个数据更新、手动从数据库拉报表再发群里。这些事看着不大,但日积月累,时间的损耗相当惊人。我用Python写了不少自动化脚本,今天挑出10个最高频、最实用的,按“处理文件、抓取数据、解放运维”三类场景整理出来。这篇文章不仅给你们代码,更会把每个脚本的设计思路、关键参数怎么选、踩过什么坑都讲清楚。适合刚学Python的入门用户,也适合每天被机械操作折磨的办公室人群,你不需要成为编程高手,装好Python、复制代码、稍微改两行路径就能用。
1. 自动化脚本的设计思路:先搞清楚值不值得“自动化”
很多人一上来就问“我要学哪些脚本”,但我更喜欢先聊判断标准。不是所有操作都值得写脚本,高效自动化的前提是三个条件同时满足:操作足够高频、流程完全规则化、失败时能快速定位。
以“每周合并销售报表”为例:如果只是偶尔做一次,手动复制粘贴可能只要10分钟,写脚本反而花一小时,那就不划算。但如果是每天都要做、涉及几十个文件、表头还有变化,脚本的价值就完全体现出来了。判断准则是“重复三次以上的手动操作,就值得花时间写脚本”。
选Python而不是其他语言,图的是它的生态。文件处理有os和pathlib,表格有pandas和openpyxl,抓网页有requests和BeautifulSoup,监控有logging和smtplib,几乎每个场景都有成熟的第三方库。这意味着你不用从零造轮子,大部分脚本60%以上的代码都是调用现成工具。
这套脚本整理下来,我习惯按“输入、处理、输出”三段式拆解。输入环节搞清楚数据从哪里来,是本地文件、网络接口还是数据库;处理环节是核心,要明确规则怎么写;输出环节决定最终产物是另存为一个文件、推送到某个平台,还是只打印日志。把这三段理清,脚本结构自然就通了。
提示:脚本不是越短越好,也不是功能越多越好,优先级永远是“能跑、稳定、出错时看得明白”。代码写得再炫,隔两个月你自己都看不懂,就等于报废了。
1.1 这10个脚本的场景分类
先给你们一张总览表,后面逐个拆解时心里有个底。
| 场景类别 | 脚本名称 | 核心库 | 解决什么问题 |
|---|---|---|---|
| 文件处理 | 多表数据合并 | pandas, glob | 合并几十个Excel文件 |
| 文件处理 | 文件名批量规范化 | os, re | 统一格式、去重、补日期 |
| 文件处理 | 临时文件自动清理 | pathlib, time | 按时间规则删除旧文件 |
| 文件处理 | 图片批量压缩 | Pillow | 批量缩小图片体积 |
| 办公文档 | PDF关键页提取 | pdfplumber | 从一堆PDF里提取指定页 |
| 办公文档 | Word模板批量生成 | python-docx | 批量生成合同/通知 |
| 数据抓取 | 网页信息定时推送 | requests, time | 盯数据变化并推送通知 |
| 数据抓取 | 图片OCR文字识别 | pytesseract | 批量提取图片文字 |
| 运维监控 | 日志异常自动告警 | logging, smtplib | 扫描日志并邮件告警 |
| 运维监控 | 数据库自动拉表 | pyodbc, pandas | 定时拉取报表并归档 |
2. 文件与办公自动化:把重复劳动压缩到几秒
先讲最贴近日常办公的6个脚本。这些脚本的共同特点是:把你要花半小时以上手工完成的活儿,压缩到几秒钟执行完。
2.1 多表数据合并:不再逐个“复制、粘贴”
这是使用率最高的脚本,没有之一。月末财务对账、汇总各业务线数据、合并全班成绩单……凡是遇到“几十个Excel的结构相同只需要拼接”的活,用它就对了。
import pandas as pd import glob files = glob.glob(r"D:\data\*.xlsx") all_data = [] for f in files: df = pd.read_excel(f, sheet_name=0) all_data.append(df) merged = pd.concat(all_data, ignore_index=True) merged.to_excel(r"D:\data\合并结果.xlsx", index=False) print(f"已合并 {len(files)} 个文件,共 {len(merged)} 行数据")核心原理就四条:glob按通配符扫描目录拿到全部文件路径,pd.read_excel逐个读取,pd.concat按行拼接,最后to_excel输出。看起来简单,但有几个细节要特别注意。
Sheet名不要写死,用sheet_name=0读第一个Sheet,因为业务部门交来的文件往往改名了。表头必须完全一致,如果有的文件多了一列少了一列,concat后会出现大量空值和错位,建议合并前先做个校验:set(df.columns) == set(template.columns),不一致时打印文件路径并跳过。
数据类型也是常见坑。同一个“工号”列,在A表里是文本“00123”,在B表里被Excel转成了数字123,拼完后全乱了。解决办法是在read_excel时加上dtype=str参数,强制所有列读成文本,再按需转换。
实操心得:如果分表列名有小差异,建议把
pd.concat换成merged = pd.concat(all_data, join="outer"),宁可保留多余列也不要丢数据。
2.2 文件名批量规范化:正则表达式拯救强迫症
从网上下载的资料、相机导出的照片,文件名经常是“IMG_2389 (1).jpg”“文档(最终版)(1).docx”这种。手动改到心累,用脚本十几秒就全搞定了。
import os, re folder = r"D:\downloads" for name in os.listdir(folder): path = os.path.join(folder, name) if not os.path.isfile(path): continue new_name = re.sub(r"\s*\(\d+\)", "", name) new_name = re.sub(r"下载|copy|副本", "", new_name) new_name = re.sub(r"[【】\[\]]", "", new_name) if new_name != name: os.rename(path, os.path.join(folder, new_name)) print(f"{name} -> {new_name}")这套脚本的核心就是正则替换。\s*\(\d+\)匹配空格加括号加数字,也就是“(1)”“(2)”这类浏览器下载自动加的后缀;下载|copy|副本是常见冗余词;[【】\[\]]清理全角半角括号。每条规则都对应一类真实命名残留,你们可以按自己场景不断增加规则,比如加re.sub(r"[年月日]", "-", new_name)把中文日期改成横线连接。
重名问题要提前想好。如果改完的新名字已经存在,os.rename会直接覆盖原文件。我一般在改名之前建一个字典记录已有文件名,重名时自动在尾部加_new。
注意:路径里有中文时,Windows下偶尔会触发编码问题。在Python的字符串前面加
r""声明原始字符串,可以避免反斜杠转义坑。
2.3 临时文件自动清理:定时任务里的家庭大扫除
不管是服务器还是个人电脑,时间久了downloads文件夹总会堆积几百个没用的临时安装包、旧日志文件。这个脚本配合系统定时任务,可以帮你每周自动清理一次。
from pathlib import Path import time folder = Path(r"D:\downloads") cutoff = time.time() - 7 * 24 * 3600 # 7天前的时间戳 for f in folder.iterdir(): if f.is_file() and f.suffix in [".tmp", ".log", ".zip", ".exe"]: if f.stat().st_mtime < cutoff: f.unlink() print(f"已删除 {f.name} ({time.strftime('%Y-%m-%d %H:%M', time.localtime(f.stat().st_mtime))})")判断逻辑很简单:先限定后缀白名单,再比较文件修改时间,超过7天就删。这里有一个变量值得说清楚,7 * 24 * 3600计算的是7天总秒数,st_mtime拿到的也是时间戳秒数,两者单位一致才能直接比较大小。
如果觉得直接删除风险太大,可以启动“观察模式”——脚本只打印哪些文件会被删,不实际执行删除操作。把f.unlink()注释掉,运行一周看看效果,确认没问题再恢复,这是我处理任何“删除类”脚本的固定操作。
2.4 PDF关键页提取:从几十页报告中抽你要的那几页
做方案的同学经常要“把某某报告的3-5页和12页发给客户”,用工具手动拆页太麻烦,用脚本来得更干净。
import pdfplumber pdf_path = r"D:\报告\产品说明.pdf" out_path = r"D:\报告\关键页.pdf" pages = [2, 3, 4, 11] # 第3、4、5、12页(索引从0开始) with pdfplumber.open(pdf_path) as pdf: writer = [] for i in pages: if i < len(pdf.pages): writer.append(pdf.pages[i]) with pdfplumber.open(out_path) if False else __import__("io").BytesIO() as buf: # 合并页需要借助PyPDF2或pdfrw,这里用个更轻的思路: pass先说明上面这段代码有个“未完成”的感觉,我自己实际用的是更简洁的方案:直接用PyPDF2的PdfWriter,逐页读取再写入新文件,代码反而更清晰。
from PyPDF2 import PdfReader, PdfWriter reader = PdfReader(r"D:\报告\产品说明.pdf") writer = PdfWriter() for i in [2, 3, 4, 11]: writer.add_page(reader.pages[i]) with open(r"D:\报告\关键页.pdf", "wb") as f: writer.write(f)这里唯一的坑是索引错位。PDF第1页对应索引0,凡是有人告诉你要“第X页”,都先自己减1验证一遍。我甚至会额外跑一段打印总页数的代码,len(reader.pages),确认页数没超出范围,不然直接报错。
2.5 Word模板批量生成:合同、通知全靠它
“给200个员工发录用通知书,内容大同小异,就姓名和岗位不同”,这是Word批量生成脚本最典型的使用场景。先做一个Word文档作为模板,把需要变化的字段用{{姓名}}、{{岗位}}这种占位符标出来,再用脚本批量替换生成新文件。
from docx import Document source_file = r"D:\模板\通知书模板.docx" employees = [{"name": "张三", "position": "产品经理"}, {"name": "李四", "position": "后端工程师"}] for emp in employees: doc = Document(source_file) for para in doc.paragraphs: para.text = para.text.replace("{{姓名}}", emp["name"]) para.text = para.text.replace("{{岗位}}", emp["position"]) doc.save(rf"D:\输出\通知书_{emp['name']}.docx")替换的原理是遍历段落后检查文本内容,用str.replace做全局替换。但这个方案有个局限:如果你的字段在Word表格里,不在普通段落里,只遍历doc.paragraphs是找不到了。这时候要同时遍历doc.tables里的每个单元格,cell.text同样执行一遍replace再写回。更复杂的动态表格和图片填充,就要用doc.add_table和add_picture来生成,网上例子很多,我们按需扩展即可。
注意:模板里不要用“文本框”承载占位符,python-docx默认读不到文本框里的内容。如果模板是别人做的,先检查一下,否则生成的文件里会出现“{{姓名}}”原样输出。
2.6 图片批量压缩:一张图几千字的“瘦身”术
写公众号、传素材库时最烦图片太大。用Pillow库可以批量把图片从5MB压到500KB,宽高按比例缩小,画质肉眼几乎无损失。
from PIL import Image from pathlib import Path folder = Path(r"D:\photos") out_folder = folder / "compressed" out_folder.mkdir(exist_ok=True) for f in folder.iterdir(): if f.suffix.lower() in [".jpg", ".jpeg", ".png"]: img = Image.open(f) img.thumbnail((2000, 2000)) # 等比缩到长边不超过2000 img.save(out_folder / f.name, quality=85, optimize=True) print(f"{f.name}: {f.stat().st_size // 1024}KB -> {(out_folder / f.name).stat().st_size // 1024}KB")核心就两行:thumbnail等比缩放,save里的quality=85控制压缩率。质量参数建议设置在80-90之间,低于75能明显看到文字边缘发虚,高于90又压不出体积差。
还要注意,thumbnail和resize的区别是:thumbnail保持原图比例,只会缩小不会拉伸;resize则可能变形,需要自己算宽高比。处理批量图片时,用thumbnail更安全。
3. 数据抓取与运维监控:让“盯数据”这件事自动化
这类脚本的价值不在“写一次用一次”,而在于配好定时任务后,它能在你睡觉时替你盯数据、替你发告警,做到真正的“无人值守”。
3.1 网页关键信息定时推送:盯着价格和榜单变动
想监控某个商品价格变化、某篇文章阅读量更新,写一个“轮询+判断”脚本,发现变化就推送通知到手机,这是爬虫类里面最实用的一种。
import requests, time from bs4 import BeautifulSoup target_url = "https://example.com/data" push_url = "https://push-api.example.com/send" # 换成你的通知webhook last_value = None while True: resp = requests.get(target_url, timeout=10, headers={"User-Agent": "Mozilla/5.0"}) soup = BeautifulSoup(resp.text, "html.parser") now_value = soup.select_one(".price").text.strip() if last_value is not None and now_value != last_value: requests.post(push_url, json={"msg": f"价格变化: {now_value}"}) print(f"[{time.strftime('%H:%M:%S')}] 变化: {last_value} -> {now_value}") last_value = now_value time.sleep(300) # 5分钟查一次核心思路是个“状态机”:last_value记录上一次的值,循环里每次重新抓取,只有发生变化才推送,避免频繁打扰。这是“事件驱动”的经典思路,比固定每分钟推送实用得多。
请求头里的User-Agent是必带的,很多网站反爬第一道关就是识别UA。timeout要显式设置,否则个别请求卡住时整个脚本会僵死在那里。
实操心得:监控类脚本最忌讳的是“崩了都不知道”。把脚本改成“出现异常立即推送告警”,比如
except Exception as e: requests.post(push_url, json={"msg": f"监控脚本报错: {e}"}),这样脚本死了你反而第一时间能发现。
关于推送渠道,最省事的是用“Server酱”或者Telegram Bot的webhook,把告警内容POST到对应接口。不想依赖第三方的话,也可以用smtplib发邮件到手机邮箱,邮件客户端会主动推送通知。这个后面第3.4节再展开。
3.2 图片OCR文字识别:两百张打卡截图一次搞定
业务上有“把截图里的文字提取出来填表”的需求,手动一张张打字非常浪费时间。OCR脚本把图片交给识别引擎,自动输出文本再写入Excel。
from PIL import Image import pytesseract import pandas as pd file_list = [r"D:\打卡记录\01.png", r"D:\打卡记录\02.png"] results = [] for f in file_list: text = pytesseract.image_to_string(Image.open(f), lang="chi_sim+eng") results.append({"file": f, "text": text}) df = pd.DataFrame(results) df.to_excel(r"D:\打卡记录\识别结果.xlsx", index=False)我实际用过tesseract和rapidocr两个引擎,后者对中文识别率明显更好,但CPU占用感人,处理大量图片时风扇狂转。如果图片量大,建议批量处理前先压缩尺寸,识别的速度能差好几倍。
识别质量的坑在于图片清晰度。手机截图没问题,但拍照的文档一定要先做“灰度化+二值化”预处理,Image.open(f).convert("L")可以大幅提高对比度,减少噪点。我踩过这个坑,最初直接用彩色图识别,错误率高得离谱,转成灰度后准确率立刻上来了。
注意:识别出来的文字通常包含大量换行和杂项符号,不要直接进Excel。用
re.sub(r"\s+", " ", text)把空白压缩成单个空格,再按业务规则用split拆列。
3.3 日志监控与异常告警:半夜“盯梢”不用自己醒
服务器日志每天生成几百MB,但真正需要关心的只有ERROR和Timeout。这个脚本定时扫描日志文件,把匹配到的异常行提取出来,汇总后发邮件。
import smtplib, re, time from email.mime.text import MIMEText from pathlib import Path log_path = Path(r"/var/log/app.log") errors = [] for line in log_path.open(errors="ignore"): if re.search(r"ERROR|Timeout|Connection refused", line): errors.append(line.strip()) if len(errors) > 10: # 超过阈值才告警,避免鸡毛蒜皮 msg = MIMEText("\n".join(errors[-20:]), "plain", "utf-8") msg["Subject"] = f"[应用告警] {len(errors)} 条异常日志" msg["From"] = "alert@example.com" msg["To"] = "ops@example.com" smtp = smtplib.SMTP_SSL("smtp.example.com", 465) smtp.login("alert@example.com", "password") smtp.send_message(msg) smtp.quit()核心逻辑是“正则匹配 + 阈值熔断”:errors[-20:]取最近20条,防止日志太多后邮件爆量。为什么要设阈值?因为有些业务日志的ERROR是正常状态的一部分(比如偶尔的网络抖动),全部推送给只会让告警变成噪音,最后真正出问题时反而没人看邮件。
smtplib.SMTP_SSL走的是465端口SSL加密,发送前需要在邮箱后台开启“SMTP服务”并获取授权码。这里提醒一句,不要往代码里写明文密码。把密码放到环境变量os.environ["MAIL_PASS"]里,或者放到单独配置文件中并设置只有本人可读的权限,避免代码被误传后泄露。
3.4 数据库自动拉表:再也不用在“数据后台”手动导出
公司系统如果开了数据库只读账号,你就可以用脚本定时把业务表拉下来,归档或做二次分析。这也是“AI自动化”话题里最实在的一类应用。
import pyodbc, pandas as pd conn_str = r"DRIVER={ODBC Driver 17 for SQL Server};SERVER=10.0.0.5;DATABASE=report;UID=reader;PWD=你的密码" conn = pyodbc.connect(conn_str) query = """ SELECT date, zone_id, sales_amount FROM daily_sales WHERE date >= DATEADD(day, -1, GETDATE()) """ df = pd.read_sql(query, conn) df.to_excel(rf"D:\data\daily_sales_{pd.Timestamp.now():%Y%m%d}.xlsx", index=False) conn.close()数据库连接串的格式看着唬人,但其实就是“驱动 + 地址 + 实例 + 账号密码”五件套。需要先安装对应的ODBC驱动,很多系统报错“找不到驱动”都是因为缺了ODBC Driver 17 for SQL Server或MySQL ODBC 8.0,去微软或MySQL官网下载安装就行。
脚本里DATEADD(day, -1, GETDATE())是SQL Server语法,意思是取昨天数据。如果公司库是MySQL,要改成WHERE date >= DATE_SUB(CURDATE(), INTERVAL 1 DAY)。很多人的连接查询没有问题,却卡在SQL方言上,这部分一定要先确认库类型。
注意:数据库账号权限务必要申请“只读”权限。自动化脚本只在需要查数据、写文件,不需要对生产库做任何写操作。如果你用的账号能写,万一代码里多执行了一条DELETE,后果不堪设想。
3.5 定时任务调度:Windows和Linux怎么“到点执行”
脚本写好了,不可能每次都手动双击运行。定时任务调度是自动化脚本从“玩具”变成“生产力工具”的关键一步。
Windows上可以用“任务计划程序”创建基本任务,设置触发器为“每天/每周”,操作选择“启动程序”,程序填python.exe的路径,参数填你脚本的完整路径。注意python.exe最好用绝对路径,比如C:\Python312\python.exe,因为任务计划程序的环境变量和你的命令窗口不一样,直接填python可能找不到。
Linux或者macOS上更简单,用的是crontab:
# 每天凌晨2点执行数据拉表脚本 0 2 * * * /usr/bin/python3 /home/ops/scripts/auto_report.py >> /home/ops/logs/auto_report.log 2>&1cron的五段格式分别是“分钟、小时、日、月、星期”,0 2 * * *表示每天凌晨2点0分。最后的>> log 2>&1把脚本的标准输出和错误都追加写入日志文件,这是调试定时任务最关键的技巧——脚本悄悄失败的时候,日志是唯一能告诉你“为什么失败”的线索。
实操心得:定时任务最坑的是“环境变量不一致”。手动能跑、定时跑不了,90%都是因为PATH里没找到Python路径或第三方库路径。解决办法是脚本开头加两行
import sys和sys.path调整,或者干脆用venv里Python的绝对路径来跑。
4. 环境准备与脚本维护:把这些自动化脚本装进“保险箱”
脚本不是写完就结束了。保存好依赖、设计好异常处理,才能让代码在半年后依然“能跑”。
4.1 虚拟环境与依赖导出
我用venv为每个脚本组单独建环境,不要一股脑把pandas、Pillow全堆在全局环境。虚拟环境的好处是各项目之间依赖隔离,比如脚本A需要pandas==1.5.3,脚本B需要pandas==2.2.1,互不干扰。
python -m venv venv venv\Scripts\activate # Windows # source venv/bin/activate # Linux/macOS pip install pandas pillow requests pdfplumber python-docx pytesseract pyodbc pip freeze > requirements.txtpip freeze > requirements.txt会把当前环境所有库名和版本号导出。以后换新机器,执行pip install -r requirements.txt就能完整还原环境。别贪心一次性装一堆库,跑哪个脚本缺哪个再补哪个,依赖越少越好维护。
4.2 异常处理和日志记录:别让脚本“静默死亡”
写脚本时最怕的就是“跑着跑着没反应了,也不报错”。所以每个脚本最好都套一个顶层异常捕获,把错误信息记录到专门的日志文件里。
import logging logging.basicConfig( filename=r"D:\scripts\auto_report.log", level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s" ) try: # 主逻辑 logging.info("脚本开始执行") except Exception as e: logging.error(f"脚本崩溃: {e}", exc_info=True) raiseexc_info=True会把完整堆栈信息写进日志,定位问题时根本不用再翻代码猜“错在第几行”。很多同学报错处理只写print(e),打印完控制台一关就什么都没留住,不好定位。
日志级别建议日常用INFO,出错用ERROR。调试阶段可以临时切到DEBUG看更多细节,正式跑定时任务时再调回INFO,避免日志文件膨胀。
我个人的习惯是:每个脚本至少打印首行和尾行日志,比如“开始执行”和“执行完成,共处理12个文件”。定时任务跑没跑、跑完没有,一眼就能从日志看出来。
5. 常见问题与排查技巧实录
这里整理了我实际用这些脚本时最常遇到的几个问题,按“症状—原因—解决”的方式列出来,方便你们对照排查。
| 症状 | 常见原因 | 解决办法 |
|---|---|---|
| 读取Excel报错 | 后缀是.xls但实际是.csv | 用pd.read_csv读,或先另存为xlsx |
| 中文路径报错 | Windows控制台编码问题 | 路径前加r"";文件开头加# -*- coding: utf-8 -*- |
| pandas没找到数据 | 读到了Sheet名但结构变了 | 改成sheet_name=None读全部Sheet再按需筛选 |
| OCR识别中文乱码 | 缺少中文语言包 | pytesseract.get_languages()确认chi_sim在不在 |
| 定时任务不触发 | 触发器配置错误或程序路径不对 | 核对任务计划程序的“起始于”和python绝对路径 |
| 爬虫被反爬 | 请求头太“素” | 添加User-Agent、Referer、Cookie,控制访问频率 |
| 脚本运行卡住 | 网络请求无超时 | 所有requests.get必须加timeout,推荐5-10秒 |
| 数据库连接失败 | ODBC驱动未安装 | 到官网装对应驱动,连接串的驱动名要和已装版本一致 |
| Excel生成后打开报错 | 文件正被程序占用 | 先关闭Excel文件再运行脚本,或改输出文件名 |
| 图片风火轮狂转 | 大尺寸图片未压缩 | 先在脚本里做thumbnail((2000, 2000))缩小再处理 |
这里单独说说排查的通用思路,所有自动化脚本出问题都可以按这个顺序来:先看日志文件有没有报错堆栈;再手动用命令行跑一次脚本,看是否复现;定位到具体一行后,用最小测试数据验证。不要上来就改代码,很多时候是自己的输入数据变了,不是代码逻辑错了。
另一个高频坑是“脚本在电脑A能跑,在电脑B报错”。八成是环境不一致,先对比pip list输出,把缺失的库补上。python版本差异也会作妖,比如pathlib在Python 3.4以上才有,如果公司服务器上还是Python 2,很多新语法直接不能运行。写脚本前第一件事python --version确认版本,第二个是不同机器之间不要依赖绝对路径,尽量用Path(__file__).parent定位脚本目录,这样脚本拷到哪都能跑。
如果你用Anaconda管理多个Python环境,还要注意“环境串味”问题。命令行里通过conda activate切换环境,否则装库可能装到base环境,最后脚本运行时根本读取不到这些第三方包。这属于最容易把头挠秃的一类问题,却也是最容易排查的一类——看一行pip show就能确认。
再分享一个小技巧:给脚本加个“干跑模式”。通过命令行参数--dry-run控制,默认只打印“将要做什么”而不实际执行。这样不管新部署的脚本还是大改后的脚本,都能先安全试运行几天,确认逻辑无误后再放开真实操作权限。这个习惯帮我躲过好几次“批量误删文件名”的麻烦。
我在实际使用中的体会是:自动化脚本的核心价值不在于省下的那几分钟,而在于它把人的注意力从重复劳动里解放出来,让你能去做真正需要判断和创造的事情。当每个脚本都用日志、异常处理和干跑模式武装好之后,它们的维护成本会变得非常低,你会越来越愿意把更多琐事交给它们。