10个Python自动化脚本实战:高效处理办公与系统维护

发布时间:2026/9/9 3:56:24
10个Python自动化脚本实战:高效处理办公与系统维护 每天下班前我都要在电脑前重复做这几件事下载文件夹里的截图和安装包堆成山、二十多张格式相同的 Excel 等着手工合并、该签到的网页又忘了点、磁盘空间悄悄飘红……这些事本身不复杂但日复一日地做就是纯粹的时间黑洞。后来我把其中规则最清楚的部分全部换成了 Python 自动化脚本每天至少能省出三四十分钟而且出错率比手工低得多。这篇文章把我实际在用的 10 个 Python 自动化脚本沉底整理出来覆盖办公文件处理、网络信息获取、系统维护三类高频场景。每个脚本都给了核心代码和踩坑记录适合手里有大量重复操作、想用脚本把自己从繁琐事务里解放出来的人哪怕你 Python 刚入门不到一个月照着配置环境和改路径也能跑起来。1. 写这10个脚本之前我在想什么1.1 什么样的重复劳动适合交给自动化很多人问我到底哪些事情适合做自动化我自己的判断标准很简单规则明确、步骤重复、结果可以预期。下载文件夹里的文件扩展名对应“图片”“文档”“压缩包”这是明确的五十张表结构相同的 Excel 合并成一张总表步骤是重复的天气接口返回的 JSON 字段是固定的结果是可预期的。这些任务交给人做容易疲劳出错交给 Python 脚本反而特别合适。反过来需要大量主观判断的事情不要碰。我见过一个朋友试图写脚本帮自己过滤“重要邮件”结果不到一周就放弃了因为他对“重要”的定义每天都在变。自动化脚本不适合承载这种模糊需求硬要做只会让维护成本高到超过手工操作。如果你刚开始接触 Python 自动化脚本我建议从“不做也没什么严重后果但做了能省事”的小任务开始跑了几天稳定了再扩大范围。1.2 选这10个脚本的四个标准在这次整理的 10 个脚本里我遵循了四个硬标准。第一低风险优先。脚本出错的最坏结果不能是把重要文件删了、把正式数据覆盖了。所以涉及文件移动、删除的地方我都会优先做成“移动”而不是“删除”或者先输出一份候选清单让人确认。第二可以安全重跑。任务失败之后直接重新运行脚本不会产生副作用。比如 Excel 合并脚本每次生成的都是独立的新文件不会破坏原始表格。第三必须有执行反馈。脚本跑完以后要有日志、文件输出或一条通知不能像石头丢进水里一样无声无息。后面专门有一节讲日志原因就在这里。第四单脚本尽量短。大多数脚本控制在 100 行以内因为自动化脚本写出来是要长期维护的半年后你回来看代码太复杂会连自己都看不懂。短代码更容易排查问题。1.3 10个脚本总览脚本号功能类别主要依赖库1下载文件夹智能整理办公文件pathlib, shutil2Excel 多表合并汇总办公表格pandas3PDF 批量合并与文字提取文档处理pypdf4定时报表邮件自动发送办公通信smtplib5带登录态的网页自动签到网络信息requests6天气预警自动推送网络信息requests7公开页面信息定时采集网络信息requests, BeautifulSoup8久坐定时休息提醒个人效率plyer9磁盘空间预警与临时文件分析系统维护shutil10批量图片压缩与格式转换媒体处理Pillow这张表建议你保存下来后面每一节会逐个拆开讲。2. 动手前的环境配置别在这里翻车2.1 Python 版本怎么选安装时注意什么这 10 个脚本我统一用的是 Python 3.10 以上版本。如果你是 Windows 用户直接去 python.org 下载 3.12 或 3.13 的安装包安装第一步务必勾选“Add python.exe to PATH”否则后面在命令行里输入 python 会提示找不到命令。这一步是新手最容易犯的错等装完才发现python敲不出来又得重新装一遍。如果你电脑里已经装过多个 Python 版本我建议在命令行里敲py -0看看本机有哪些版本然后用py -3.12 script.py这种形式指定版本运行避免调用了不是你预期的那个解释器。macOS 用户注意系统自带的 Python3 是给系统工具用的最好用 Homebrew 另外装一个日常开发的 Python别去动系统自带那个。Linux 用户直接用apt install python3 python3-pip或dnf install python3即可。2.2 编辑器与虚拟环境稳定运行的关键一步编辑器我用的是 VS Code装好 Python 扩展后按CtrlShiftP选择“Python: Select Interpreter”把解释器指到你刚安装的 Python 上。单看一个脚本用记事本也能跑但VS Code的调试和自动补全会省很多排查问题的时间。比编辑器更重要的是虚拟环境。强烈建议每个自动化项目单独建一个.venv命令是python -m venv .venvWindows 下激活命令是.venv\Scripts\activatemacOS 和 Linux 下是source .venv/bin/activate。为什么要多这一步因为不同的脚本会用不同版本的第三方库比如某个项目需要 pandas 2.x另一个项目可能因为历史代码只兼容 1.5堆在同一个环境里迟早会冲突。虚拟环境能把你当前的依赖锁定在项目内部换电脑、换目录都不会乱。我见过太多人直接在全局环境里pip install一堆库几个月后某个库升级把另一个脚本搞挂了排查半天才明白是依赖冲突。2.3 本文涉及的第三方库统一安装下面这条命令把后面会用到的第三方库一次性装好pip install pandas openpyxl pypdf requests beautifulsoup4 lxml pillow plyer注意发送邮件用的是标准库 smtplib不需要额外安装。如果你在安装过程中发现速度很慢可以临时换用国内镜像源比如pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 库名但镜像源可能比官方源滞后这属于网络环境问题不影响代码逻辑。装好之后可以用pip list验证所有包是否都出现在列表里。3. 脚本1—4办公自动化四件套3.1 下载文件夹智能整理你的下载文件夹是不是也常年乱到不想看截图、安装包、PDF、Word 文档、压缩包全部堆在一起每次找一个文件要翻好几屏。这个脚本做的事情很朴素扫描目标文件夹里的所有文件按扩展名归类到“图片”“视频”“PDF”“文档”“压缩包”“安装包”等子目录。我用的核心代码是from pathlib import Path import shutil import time src Path.home() / Downloads mapping { .jpg: 图片, .jpeg: 图片, .png: 图片, .gif: 图片, .mp4: 视频, .avi: 视频, .mkv: 视频, .pdf: PDF, .docx: 文档, .xlsx: Excel, .pptx: PPT, .zip: 压缩包, .rar: 压缩包, .7z: 压缩包, .exe: 安装包, } for item in src.iterdir(): if not item.is_file() or item.name.startswith(.): continue folder_name mapping.get(item.suffix.lower(), 其他) dst_dir src / folder_name dst_dir.mkdir(exist_okTrue) target dst_dir / item.name if target.exists(): new_name f{item.stem}_{time.strftime(%Y%m%d%H%M%S)}{item.suffix} item item.rename(src / new_name) shutil.move(str(item), str(dst_dir / item.name))这里面有两个我用了几次才发现的关键点。第一用Path.home()来拼路径不要硬编码C:\Users\你的名字\Downloads因为换电脑或换用户名后脚本不至于立刻废掉。第二文件名冲突时要自动加时间戳否则第二次运行脚本时移动文件会覆盖掉同名文件。我因为这个吃过亏同名文件在目标目录里直接覆盖丢了半天的资料。另外建议你在第一次实际运行时先不处理旧文件只处理最近 7 天下载的文件。可以在遍历时加一个时间判断time.time() - item.stat().st_mtime 7 * 86400。一次性把几个月的老文件全部搬走会让正在处理某个项目的你找不到刚下载的素材体验很糟。脚本自动整理是好习惯但也要给人留出适应缓冲期。3.2 Excel 多表数据自动汇总每到月底要汇总多个 Excel 表格的人一定懂这种痛苦表格格式完全一样只是数据分布在十几个不同文件里手工打开、复制、粘贴循环二十次眼睛都要花掉。用 pandas 处理这种场景可以说是降维打击from pathlib import Path import pandas as pd files list(Path(data).glob(*.xlsx)) frames [] for f in files: df pd.read_excel(f, engineopenpyxl) frames.append(df) merged pd.concat(frames, ignore_indexTrue) merged.to_excel(合并结果.xlsx, indexFalse) print(f共合并 {len(files)} 个文件总行数 {len(merged)})pd.concat会按照列名对齐数据即使每个文件的列顺序不完全一样只要列名一致结果也不会错。这是一个很实用的特性比手工逐列复制安全得多。我刚写这个脚本时踩过一个坑直接按文件名的字典顺序读取导致结果顺序和业务上的时间顺序不一致。如果文件名里有日期建议按日期排序后再 concat。还有一个隐藏问题当单个文件很大会占用大量内存如果遇到几十 MB 甚至几百 MB 的 Excelpandas 一次性读完所有文件会让内存飙高这种情况下建议只读取需要的列或者分批处理。如果合并之后需要保留原表的单元格格式和公式pandas 就做不到了那属于 openpyxl 的复制 sheet 范畴复杂度会明显上升。我的建议是先明确你到底是要“数据汇总”还是“格式保留”大多数日报、周报场景只需要数据那就用 pandas 更快。3.3 PDF 批量合并与关键文字提取PDF 处理看起来简单其实非常容易出问题。先是库的选择老一点的教程会让你用 PyPDF2但这个库维护状态不太理想新项目我直接推荐用 pypdfPyPDF2 的后续版本实际上也迁移到了 pypdf 的命名空间。合并一批 PDF 的核心代码是from pypdf import PdfReader, PdfWriter from pathlib import Path pdf_dir Path(pdfs) out_path Path(merged.pdf) writer PdfWriter() for file in sorted(pdf_dir.glob(*.pdf)): reader PdfReader(file) for page in reader.pages: writer.add_page(page) print(f已加入 {file.name}页数 {len(reader.pages)}) with open(out_path, wb) as f: writer.write(out_path) print(f合并完成共 {len(writer.pages)} 页)这里最容易被忽略的是“排序”两个字。如果你直接用glob返回的文件名顺序合并结果往往不是你想要的。因为按字符串排序时10.pdf会排在2.pdf前面。如果文件名都是纯数字用keylambda x: int(x.stem)这种自然排序方式数字在前按数字大小排。文字提取也有个常见误区。reader.pages[i].extract_text()只能提取 PDF 里真实存在的文字层如果文件本身是扫描版或者图片型 PDF提取出来是空字符串这不是代码问题而是需要 OCR。遇到这种情况别急着调代码先确认文档性质。另外不要试图用 pypdf 处理加密 PDF带密码的文档需要先用其他工具解除密码或用reader.decrypt但解密也依赖文件的权限设置并非所有加密文档都能处理。3.4 定时报表邮件自动发送做完报表还要人工发邮件这一点也不自动化。我最常用的方式是 smtplib 发送带附件的邮件核心代码可以封装成下面这样import smtplib import os from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.mime.application import MIMEApplication from email.utils import formataddr from pathlib import Path def send_mail(subject, body, to_addr, attachment_paths): smtp_host smtp.qq.com smtp_port 465 sender os.environ[MAIL_USER] auth_code os.environ[MAIL_AUTH_CODE] msg MIMEMultipart() msg[From] formataddr([自动任务, sender]) msg[To] to_addr msg[Subject] subject msg.attach(MIMEText(body, plain, utf-8)) for path in attachment_paths: p Path(path) part MIMEApplication(p.read_bytes()) part.add_header(Content-Disposition, attachment, filenamep.name) msg.attach(part) with smtplib.SMTP_SSL(smtp_host, smtp_port) as server: server.login(sender, auth_code) server.send_message(msg)注意这里我用的是os.environ从环境变量里读邮箱和授权码而不是直接写死在代码里。很多人第一次配置邮件时习惯把自己的邮箱密码写进脚本一旦脚本上传到 GitHub 或者发给别人邮箱信息就泄露了。建议在系统环境变量里配好或者在脚本里用getpass临时输入。各大邮箱的 SMTP 服务器地址和端口有差别QQ 邮箱是smtp.qq.com加 465 端口163 邮箱是smtp.163.com但都需要先登录邮箱后台开启 SMTP 服务并生成“授权码”这个授权码才是脚本里 login 要用的密码。邮件服务商对附件大小通常有限制普通企业邮箱一般限制单封 20MB 到 50MB如果你生成的 Excel 超过这个限制必须压缩附件或拆分发送。4. 脚本5—7网络自动化让信息自己找上门4.1 带登录态的网页自动签到很多网站或内部系统每天都要手动点一次“签到”偶尔忘了就断签。用 Python 模拟签到请求的本质是让脚本复用你浏览器里的登录状态。我这里用一个通用模板说明思路具体请结合你自己的目标场景调整import requests session requests.Session() session.headers.update({ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64), Referer: https://example.com/, }) # 从浏览器开发者工具里复制出来的 Cookie粘贴到下面字符串里 cookies {sessionid: 你的会话值} session.cookies.update(cookies) resp session.post(https://example.com/api/sign, timeout15) if resp.status_code 200 and 签到成功 in resp.text: print(签到完成) else: print(签到失败, resp.status_code)我不建议把用户名密码硬编码进脚本里用程序模拟登录。原因很简单很多网站有验证码、二次验证、风控策略账号密码登录的自动化逻辑会非常脆弱密码一改脚本就失效而且明文保存密码本身就很危险。更稳的方案是在浏览器里正常登录一次然后打开开发者工具的 Network 面板找到签到请求把请求头里的 Cookie 复制进脚本。Cookie 过期后重新登录浏览器再复制一次就行。从网络面板里观察实际请求比凭空猜接口要准得多。签到类接口通常是一个 POST 请求返回 JSON 或者页面片段。但有一点要注意脚本要能识别“今天已签到”的状态不能傻乎乎地重复请求。如果页面已经提示今日已签再发送签到请求就是无意义的网络请求还可能被服务端判定为异常行为。建议在脚本里加上判断分支已签到就正常退出未签到时才执行签到。4.2 天气预警自动推送天气变化看起来不紧急但极端天气出现时早一点知道能避免很多麻烦。我记得有一次出差全靠脚本在早上六点推送了一条暴雨预警我才提前换了出行计划。用天气 API 实现这个功能非常快关键思路是“每天定时拉取天气数据遇到预警条件再推送”而不是每天无脑推送一堆普通人看不懂的气象代码。我的做法是这样的import requests API_KEY 你的天气API Key city 你的城市 resp requests.get( https://provider.example.com/weather, params{key: API_KEY, city: city}, timeout10, ) data resp.json() # 以实际 API 返回结构为准通常是 alerts / forecast 之类的字段 alerts data.get(alerts, []) if alerts: top alerts[0] send_message(f{city}天气预警{top.get(title)}{top.get(description)}) else: print(无预警一切正常)去找免费天气 API 时先看免费额度够不够你用。免费档通常每分钟有个请求上限个人每天推送一次完全够。注册后服务商给你一个 Key它就是从服务端换取数据的钥匙。需要留心的一个细节是城市名如果包含中文URL 里必须做编码用 requests 的params参数会自动处理千万不要自己拼 URL 字符串然后不编码否则中文城市名会请求失败。推送通道我这里用的是通用的send_message它可以是企业微信机器人、钉钉机器人或者 Server酱这类服务。核心思路是把预警信息转发到手机端真正实现对异常天气的实时感知。用这个脚本后没多久你会意识到一个原则不重要的天气信息不要天天推。如果每天都推送“今天晴转多云”人很快就对推送免疫了真到了预警那天反而不会认真看。我自己的习惯是只对影响出行和安全的异常天气做推送其他天气只写入日志不打扰自己。4.3 公开页面信息的定时采集最后一个网络类脚本是页面数据采集通常被大家叫爬虫。不管采集什么站点有两条底线我必须先讲清楚只采集公开可访问的信息遵守目标网站的 robots 规则和用户协议控制请求频率不造成负担采集到的数据只用于个人学习研究不用于商业和传播。这是一个长期做信息采集的人最基本的职业素养。下面是一个适合新手的采集模板针对的是一个新闻列表类的公开页面import requests import csv from bs4 import BeautifulSoup headers {User-Agent: Mozilla/5.0} resp requests.get(https://example.com/news, headersheaders, timeout10) resp.encoding resp.apparent_encoding soup BeautifulSoup(resp.text, html.parser) rows [] for item in soup.select(.news-list h3 a)[:20]: rows.append({ 标题: item.get_text(stripTrue), 链接: item.get(href), }) with open(news.csv, w, newline, encodingutf-8-sig) as f: writer csv.DictWriter(f, fieldnames[标题, 链接]) writer.writeheader() writer.writerows(rows) print(f已采集 {len(rows)} 条数据)resp.encoding resp.apparent_encoding这一行是我加进去的救命令。很多网站页面没有正确声明 charset导致 requests 默认用 UTF-8 解码后出现乱码apparent_encoding是根据内容自动检测编码虽然不一定 100% 准确但大多数中文站点都能救回来。写 CSV 时用utf-8-sig而不是utf-8是因为 Excel 打开无 BOM 的 UTF-8 文件时会把中文识别成乱码utf-8-sig可以避免这个问题这也是很多新手容易踩的坑。BeautifulSoup 的选择器.news-list h3 a是根据页面结构定的不同站点完全不同。这里没有捷径只能在浏览器开发者工具里查看网页源码找到列表项对应的 class 和标签层级。另外一定要在代码里做异常处理因为网站改版、选择器失效是常态没有异常处理的任务半夜里静默失败你都不知道它挂了。5. 脚本8—10系统维护与个人效率5.1 久坐定时休息提醒长时间坐在电脑前真的容易忘记时间。工作一忙两个小时不起身是常态等反应过来脖子已经僵了。这个脚本的作用就是在合适的时间弹一条系统通知提醒你站起来活动。用 plyer 库发系统通知的代码非常短from plyer import notification from pathlib import Path import time flag Path.home() / .last_break # 距离上次提醒不足 25 分钟就退出避免重复打扰 if flag.exists() and time.time() - float(flag.read_text()) 1500: exit() notification.notify( title该起来动一动了, message离开屏幕站起来活动5分钟喝口水, timeout10, ) flag.write_text(str(time.time()))把这段脚本交给 Windows 任务计划器让它每 20 分钟运行一次脚本自身通过时间戳判断是否需要提醒。这种“调度器只负责定时拉起脚本自己决定要不要干活”的模式比在 Python 里写一个无限循环更好用。因为 Python 的while True循环一旦被系统挂起或崩溃任务就彻底断了而定时任务每次都是全新启动状态天然能通过文件系统保存更不容易积累问题。你可能会想如果正好在开会或者演示怎么办这种弹窗确实可能打扰到别人。后来我在脚本里加了一个简单的忽略逻辑检测到当前运行的窗口是腾讯会议、Zoom 之类的全屏应用时跳过本次提醒。这个逻辑不复杂但很实用用 pywin32 获取前台窗口标题就能实现不同系统实现方式不同这里不展开。核心思想是提醒也要有优先级不要为了健康反而制造尴尬。5.2 磁盘空间预警与临时文件清理磁盘空间告急的时候往往已经晚了。Windows 的 C 盘剩几个 GB 才发现很多大型软件就开始各种卡顿报错。其实完全可以在空间低于某个阈值时提前预警还能把最容易清理的临时文件列出来。用shutil.disk_usage可以拿到磁盘的总体和剩余情况import shutil import os from pathlib import Path total, used, free shutil.disk_usage(C:/) free_gb free / (1024 ** 3) threshold 10 if free_gb threshold: print(f警告C盘剩余空间仅 {free_gb:.1f}GB低于阈值 {threshold}GB) # 列出用户临时目录下占用空间较大的文件供后续确认 temp_dir Path(os.environ.get(TEMP, /tmp)) for p in temp_dir.rglob(*): try: if p.is_file() and p.stat().st_size 200 * 1024 * 1024: print(f大临时文件{p}占用 {p.stat().st_size / 1024 / 1024:.1f} MB) except OSError: continue else: print(f磁盘空间正常剩余 {free_gb:.1f}GB)这个脚本有意识地没有自动删除任何文件这是我的一个原则删除类操作不适合自动化直接执行因为你预料不到某个文件是否正被正在运行的程序占用、是否属于重要数据。把风险降到最低的做法是让脚本明确提示“哪些文件可以清理”把最终删除动作留给人。真正等你确定可以删了再手动执行清理或者用专门的清理工具比让脚本自作主张安全一个量级。实际使用中我发现大临时文件往往集中在几个固定的目录里比如用户的 Temp 目录、浏览器的下载缓存、各种安装包的残留。如果能快速找出单文件超过 200MB 的临时文件通常已经解决了一大半磁盘问题。这个脚本运行一次的时间很短建议挂到每天中午的定时任务里上午干活的缓存已经积累出来了又不影响晚上做备份。5.3 批量图片压缩与格式转换经常处理图片的人会明白手机拍出来的照片一张动不动就五到十 MB发邮件、传系统、贴文档的时候根本传不上去。我写批量压缩脚本的初衷就是为了处理旅行照片几百张原图直接用任何一个云盘都传得费劲。核心代码长这样from PIL import Image, ImageOps from pathlib import Path src_dir Path(photos) # 原始图片目录 out_dir Path(photos_compressed) # 压缩后输出目录 out_dir.mkdir(exist_okTrue) for p in src_dir.glob(*.jpg): img Image.open(p) img ImageOps.exif_transpose(img) # 处理手机拍照方向 img.thumbnail((1920, 1920)) # 最长边限制在1920以内 img.save(out_dir / p.name, quality85, optimizeTrue) print(f已处理 {p.name}新尺寸 {img.size})ImageOps.exif_transpose是容易被忽略的重要细节。手机拍的照片其实带了一个 EXIF 方向信息很多看图软件会自动旋转但 Pillow 默认不会读取这个信息如果你直接打开再保存横着拍的照片传上去可能莫名其妙转了 90 度。加上这行代码方向问题就解决了。输出目录一定要和源目录分开这个原则我已经重复好几遍了。批量处理图片时如果直接覆盖原图一旦压缩质量不理想原图已经没了想恢复都没有办法。先输出到photos_compressed确认效果后再决定要不要替换原图是最稳妥的流程。如果你需要处理的是 PNG 透明图保存时要注意 Pillow 默认不支持直接把 RGBA 模式存成 JPEG需要先转成 RGB或者直接保存为 PNG。批量处理几百张图片时Pillow 处理一张大约 0.1 到 0.5 秒几百张图几分钟内就能跑完。如果图片数量到了几万张建议引入并发处理但并发会带来内存压力正常情况下单线程已经够用。6. 把脚本挂到系统里实现真正的无人值守6.1 Windows 任务计划程序配置要点脚本本身写得再好如果每次都要手动运行那就还是半自动化。Windows 下的挂载方式主要靠“任务计划程序”。打开任务计划程序创建基本任务名称填“下载整理”触发器可以填“每天”操作选择“启动程序”在“程序或脚本”这一栏填上你 Python 环境里 python.exe 的绝对路径然后在“添加参数”填脚本完整路径在“起始于”填脚本所在目录。这三项配置缺一不可尤其“起始于”经常被忽略。如果你在脚本里用了相对路径比如Path(data)而“起始于”没填任务