尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

10个Python自动化脚本:文件整理、报表处理、PDF合并一次搞定

发布时间:2026/9/28 18:08:42

资讯中心
01
ARTICLE

10个Python自动化脚本:文件整理、报表处理、PDF合并一次搞定

10个Python自动化脚本:文件整理、报表处理、PDF合并一次搞定
很多人的电脑桌面常年躺着一堆“新建文件夹 (3)”。问起来就是“先放着哪天整理”。这个“哪天”我一般等不到所以我选择用 Python 写自动化脚本让程序替我做归档文件、合并表格、压缩图片、发日报这些重复劳动。今天这篇就把我最近在用的 10 个脚本整理出来不追求炫技突出一个“能跑、能改、能救急”。适合谁一是每天和文件夹、Excel、PDF 打交道的办公党二是刚学完 Python 基础、想找点真实练手项目的人三是单纯想偷懒但又怕把电脑搞乱的朋友。我会按场景分组讲每个脚本都会说清楚它解决了什么问题、依赖什么库、运行前要注意什么。默认你已经装好 Python 3.8还没装的直接去 python.org 下载安装时勾选 Add Python to PATH第三方库用 pip install 补齐。另外我建议给这些脚本单独建一个虚拟环境免得和系统里其他项目互相影响。1. 先收拾桌面文件归档与批量重命名的两个脚本文件整理这事的难点从来不是“知道该分类”而是“坚持分类”。所以我建议把这件事交给脚本。这里两个脚本拆开用一个管归档一个管重命名。拆开的原因很简单出问题的时候你只需要调试一个函数而不是在一坨代码里翻半天。1.1 按扩展名自动归档下载文件夹import shutil import time from pathlib import Path SOURCE Path.home() / Downloads TARGET SOURCE / sorted EXT_MAP { .jpg: 图片, .jpeg: 图片, .png: 图片, .gif: 图片, .mp4: 视频, .mkv: 视频, .avi: 视频, .mp3: 音频, .wav: 音频, .flac: 音频, .pdf: 文档, .docx: 文档, .xlsx: 表格, .pptx: 演示, .py: 代码, .js: 代码, .html: 代码, .css: 代码, .zip: 压缩包, .rar: 压缩包, .7z: 压缩包, } files [item for item in SOURCE.iterdir() if item.is_file()] for item in files: folder EXT_MAP.get(item.suffix.lower(), 其他) dest_dir TARGET / folder dest_dir.mkdir(parentsTrue, exist_okTrue) dest dest_dir / item.name if dest.exists(): dest dest_dir / f{item.stem}_{int(time.time())}{item.suffix} shutil.move(str(item), str(dest)) print(f归档完成共处理 {len(files)} 个文件)这个脚本的思路很直接把下载目录里每一个文件的后缀名取出来对照字典找到它应该去哪个子目录然后移动过去。我用pathlib而不是os.path拼接路径代码看起来更干净shutil.move负责移动同一个磁盘下是 rename 操作速度极快跨盘才会复制后删除。有一点要注意如果你把目标目录放在源目录里面移动文件会改变目录结构所以先通过files [...]把文件快照收集好再循环。不要在遍历目录的同时移动文件否则可能跳过或重复处理。另一个容易忽略的坑是文件重名如果目标文件夹里已经有同名文件shutil.move会直接覆盖掉你可能毫无察觉。所以代码里先判断目标是否存在存在就在文件名后加时间戳。第一次跑的时候我建议先别急着真移动把前面的shutil.move替换成print预览一下每个文件会落到哪里确认规则没问题再放开跑。提示shutil.move在目标文件已存在时会覆盖。上面的脚本用时间戳重命名来规避实际操作时先跑一遍 print 预览更安心。1.2 批量重命名给交付文件加统一前缀和序号from pathlib import Path import re folder Path(rD:\交付文件) prefix 项目A # 去掉 Windows 文件名非法字符 base re.sub(r[\\/:*?|], , prefix) for idx, file in enumerate(sorted(folder.iterdir()), 1): if not file.is_file(): continue new_name f{base}_第{idx:02d}{file.suffix} dest folder / new_name if not dest.exists(): file.rename(dest) else: print(f已存在跳过{dest})这个脚本适合给一批文件加统一前缀和序号。比如给设计交付的 PNG 加上“项目A_第01期”这样的名字给比赛照片加上日期前缀批量加序号能保证顺序不乱而且是可预期的。sorted(folder.iterdir())是为了让文件按文件名排序后再编序号否则os.listdir的顺序在不同系统上可能不一样最后编号顺序会很随机。重命名是不可逆操作改完再想找回原文件名特别麻烦。我习惯在脚本里加一个 dry-run 开关先把file.rename(dest)换成print(new_name)跑一遍看清单再真正执行。Windows 下文件名不能包含\ / : * ? |这些字符如果前缀是从外部传来的记得用re.sub清洗另外也不要让新文件名太长Windows 的路径长度限制在 260 个字符左右。2. 报表人的救赎表格合并与日报生成的自动化见客户、交周报、拉数据很多时候时间都耗在打开 Excel、复制粘贴、删重复行这些重复操作上。用 pandas 处理数据会快得多。这一节两个脚本一个处理 Excel 合并一个处理日志统计和消息推送核心都是“读进来 - 处理 - 输出”三步。2.1 多表合并与去重顺手清理脏数据import pandas as pd files [sales_1.xlsx, sales_2.xlsx, sales_3.xlsx] frames [] for f in files: df pd.read_excel(f, dtypestr) # 清洗列名去掉首尾空格和换行 df.columns [str(c).strip().replace(\n, ) for c in df.columns] # 关键字段去掉空格避免同一订单因为空格被当成两条 if 订单号 in df.columns: df[订单号] df[订单号].astype(str).str.strip() frames.append(df) merged pd.concat(frames, ignore_indexTrue) if 订单号 in merged.columns: merged merged.drop_duplicates(subset[订单号], keepfirst) merged.to_excel(sales_all.xlsx, indexFalse) print(f合并完成共 {len(merged)} 条)dtypestr是我强烈建议保留的参数。订单号、身份证号、银行卡号只要超过 15 位Excel 就会偷偷转成科学计数法pandas 默认也可能推断成数字类型最后丢尾数。强制按字符串读入问题直接消失。列名的坑也很常见不同人交上来的表列名可能带空格、带换行合并前先统一处理否则concat之后会出现“订单号”和“订单号 ”两列。drop_duplicates用subset参数指定按订单号去重keepfirst表示保留第一个出现的记录相当于默认保留最靠前的文件里的数据。如果你希望后面的表覆盖前面的把keeplast就行。还有一个实用小操作在concat之前给每张表加一列来源比如df[来源] f合并后就能知道每条数据是从哪个文件来的排查问题方便很多。2.2 日志统计自动生成日报推送到企业微信群import requests from datetime import date log_file app.log error_keywords [ERROR, Exception, Traceback] total 0 errors [] with open(log_file, encodingutf-8) as f: for line in f: total 1 if any(k in line for k in error_keywords): errors.append(line.strip()) text f{date.today()} 日报日志总量 {total} 条异常 {len(errors)} 条 if errors: text \n最近异常\n \n.join(errors[:5]) webhook https://qyapi.weixin.qq.com/cgi-bin/webhook/send?keyYOUR_KEY resp requests.post( webhook, json{msgtype: text, text: {content: text}}, timeout10, ) print(resp.status_code)这个脚本很多人都需要每天下班前看一眼日志有没有异常。与其打开终端翻半天不如让脚本把统计结果直接推到群里。企业微信机器人只需要一个 webhook URL不用配账号密码是最轻量的方案如果你用钉钉或飞书换个接口地址和消息格式就行。记得在群里给机器人添加关键词校验比如内容必须包含“日报”否则消息会被拒绝这是官方安全机制。读取日志我用的是逐行for line in f不是一次性f.read()。日志文件动辄几百 MB一次性全读到内存里会卡逐行处理更稳。如果日志文件是 logs 目录下多个文件可以用glob.glob(logs/*.log)套一层循环。还有一点webhook key 等同于往你群里发消息的权限千万别把它写进代码后推到公开仓库用环境变量或本地配置文件存。3. 图片与 PDF 处理把素材格式化的体力活交给脚本图片压缩、PDF 合并、提文字这些事在线工具都能做但只要文件一多上传下载比手动处理还累。而且有些文件不适合传到第三方网站本地脚本更让人放心。我最早学自动化就是因为要处理一百多张现场照片一个个改尺寸实在受不了。3.1 批量压缩图片并转换格式顺手解决透明底变黑from PIL import Image from pathlib import Path src Path(photos) dst Path(photos_compressed) dst.mkdir(exist_okTrue) max_width 1920 for p in src.glob(*.png): img Image.open(p) if img.width max_width: ratio max_width / img.width img img.resize((max_width, int(img.height * ratio)), Image.LANCZOS) if img.mode ! RGB: rgba img.convert(RGBA) background Image.new(RGB, img.size, (255, 255, 255)) background.paste(rgba, maskrgba.split()[-1]) img background img.save(dst / f{p.stem}.jpg, quality85) print(压缩完成)先缩放到最大宽度再把 PNG 转成 JPG 并压缩是图片处理任务的常见组合。Pillow 的Image.LANCZOS在 Pillow 10 会提示改用Image.Resampling.LANCZOS如果你不想看到 DeprecationWarning替换一下即可。quality85是体积和画质比较平衡的点网上很多教程默认quality80实际对照片来说 85 更稳肉眼几乎看不出差别体积能小不少。透明底变黑是转 JPG 最常见的翻车点。PNG 的透明通道在 JPG 里没有对应存储如果不处理透明区域会直接变成黑色。我这里的做法是先转成 RGBA然后贴到一张白色背景上再把 alpha 通道作为 mask 合过去。实测下来处理带透明的 logo、截图都非常稳。批量处理前先抽两三张试一下确定参数没问题再全量跑不然一百张图压完才发现颜色不对返工成本很高。提示Pillow 10 中Image.LANCZOS会显示弃用警告可以换成Image.Resampling.LANCZOS。3.2 PDF 合并、拆分与文本提取一条脚本全搞定import fitz # PyMuPDF from pathlib import Path def merge_pdfs(pdf_list, outputmerged.pdf): result fitz.open() for pdf in pdf_list: with fitz.open(pdf) as doc: result.insert_pdf(doc) result.save(output) result.close() def split_pages(pdf_path, out_dirsplit): doc fitz.open(pdf_path) Path(out_dir).mkdir(exist_okTrue) for i in range(len(doc)): new fitz.open() new.insert_pdf(doc, from_pagei, to_pagei) new.save(f{out_dir}/page_{i1}.pdf) new.close() def extract_text(pdf_path): doc fitz.open(pdf_path) return \n.join(page.get_text() for page in doc) if __name__ __main__: merge_pdfs([a.pdf, b.pdf])PyMuPDF 的包名是PyMuPDF但 import 的名字却是fitz这个错位让很多人第一次装完就懵了。合并 PDF 的核心是insert_pdf它可以一次插入多个页面拆分的时候from_page和to_page设成同一个页码就能只取出那一页。如果只需要合并和拆分pypdf也可以但页数多、文件多的时候 PyMuPDF 明显更快。get_text()提取的是 PDF 的文字层对扫描版、拍照版 PDF 没用那种 PDF 本质上是一张张图片必须先 OCR。如果你经常处理扫描件可以配合 Tesseract 做 OCR 流程但那是另一个话题了。有一点建议生成的拆分文件放到单独目录避免和原 PDF 混在一起等下想整体删除都不好删。4. 盯梢与提醒网页监控和定时邮件的基本姿势有些自动化任务是“立刻执行”有些是“盯着变化”。网页上什么时候出现某个内容、系统报表什么时候该发出去这些更适合让脚本盯着有变化再通知人。这一节就用两个最常见场景页面监控和定时发信。4.1 页面内容变化监控检测到目标内容就发提醒import time import requests import smtplib from email.mime.text import MIMEText url https://example.com/some/page monitor_text 立即购买 check_interval 300 smtp_config { server: smtp.qq.com, port: 465, user: youqq.com, password: 授权码, to: [meexample.com], } def send_alert(content): msg MIMEText(content, plain, utf-8) msg[Subject] 监控提醒 msg[From] smtp_config[user] msg[To] , .join(smtp_config[to]) with smtplib.SMTP_SSL(smtp_config[server], smtp_config[port]) as server: server.login(smtp_config[user], smtp_config[password]) server.sendmail(smtp_config[user], smtp_config[to], msg.as_string()) while True: try: resp requests.get(url, timeout10, headers{User-Agent: Mozilla/5.0}) if monitor_text in resp.text: send_alert(f页面出现目标内容{url}) break except Exception as e: print(请求异常, e) time.sleep(check_interval)这个脚本应用场景很多盯商品上架、盯官网公告、盯某个页面有没有出现你想看到的关键词。实现上就是一个请求循环每 5 分钟访问一次公开页面检查关键词有没有出现在返回文本里出现就发邮件并退出。注意两个底线一是轮询间隔不要太短5 分钟起步二是只碰公开的、允许访问的页面别拿它去高频请求小网站更不要为了破解访问限制去写绕过脚本。requests默认的 User-Agent 是python-requests很多服务器会拦所以 headers 里带一个常规浏览器的 User-Agent 能降低请求被拒的概率。try/except不能省网络抖动是常态一次失败就退出的话半夜监控就断了。如果你需要监控登录后的页面就带上 Cookie但 Cookie 属于敏感信息放进环境变量比写死在代码里安全。4.2 SMTP 定时发信日报、附件、临时通知一网打尽import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.mime.application import MIMEApplication def send_email(subject, content, attach_pathNone): smtp_cfg { server: smtp.qq.com, port: 465, user: youqq.com, password: 授权码, to: bossexample.com, } msg MIMEMultipart() msg[From] smtp_cfg[user] msg[To] smtp_cfg[to] msg[Subject] subject msg.attach(MIMEText(content, plain, utf-8)) if attach_path: with open(attach_path, rb) as f: part MIMEApplication(f.read()) part.add_header( Content-Disposition, attachment, filenameattach_path.rsplit(/, 1)[-1], ) msg.attach(part) with smtplib.SMTP_SSL(smtp_cfg[server], smtp_cfg[port]) as server: server.login(smtp_cfg[user], smtp_cfg[password]) server.sendmail(smtp_cfg[user], [smtp_cfg[to]], msg.as_string()) send_email(每日备份报告, 今天的备份已完成, backup.zip)发送邮件最常用的方式是SMTP_SSL465 端口。企业邮箱、QQ 邮箱、163 都能用但密码必须填授权码而不是登录密码授权码在邮箱设置里开启 SMTP 服务后生成。如果直接把登录密码写在脚本里一旦脚本泄露等于把整个邮箱交出去用授权码还有机会单独撤回权限。如果你想定时跑可以把函数和schedule库结合比如每天早上 9 点调用一次。附件部分我用的是MIMEApplication支持任意文件类型但要确认发送的附件路径存在否则open会抛异常。中文附件名在部分客户端上会出现显示乱码稳妥做法是用email.header.Header对文件名编码我日常图省事直接用英文文件名等真有需求再去处理。最后在公司网络环境下可能还需要配置邮件服务器白名单总之先在本地测通一次再上定时任务。5. 懒人维护术按天打包备份和剪贴板历史这一组的共同点是“平时没存在感关键时刻救命”。备份脚本是提前布防剪贴板历史是随时查证。两个都不复杂但能极大减少“当时没留底现在找不回”的焦虑。5.1 按天全量打包关键目录保留 7 天自动清理import os import zipfile import datetime src rD:\work backup_root rD:\backup keep_days 7 os.makedirs(backup_root, exist_okTrue) today datetime.date.today() zip_path os.path.join(backup_root, fbackup_{today.strftime(%Y%m%d)}.zip) with zipfile.ZipFile(zip_path, w, zipfile.ZIP_DEFLATED) as zf: for root, dirs, files in os.walk(src): # 跳过可再生成的临时目录 dirs[:] [d for d in dirs if d not in (.git, __pycache__, node_modules)] for f in files: file_path os.path.join(root, f) arcname os.path.relpath(file_path, src) zf.write(file_path, arcname) now datetime.datetime.now() for name in os.listdir(backup_root): if name.startswith(backup_) and name.endswith(.zip): fpath os.path.join(backup_root, name) if os.path.getmtime(fpath) (now - datetime.timedelta(dayskeep_days)).timestamp(): os.remove(fpath) print(f备份完成{zip_path})我的备份策略很简单每天全量打一个 zip文件名带日期保留 7 天超过就删。和真正的增量备份相比全量 zip 占空间更多但恢复非常简单解压就是全部。对于大多数人重要的文档、项目代码、素材目录这种粗放策略反而最不容易出问题。zipfile是标准库几乎不引入新依赖。注意跳过.git、__pycache__、node_modules这类临时或可再生成的目录压缩体积能小很多速度也快。两个容易被忽略的点备份千万别放在和源目录同一个物理磁盘上硬盘坏了那叫陪葬备份目录也不要放在被备份的源目录里否则下次备份会把上次的压缩包一起打进去体积会越来越离谱。清理旧备份时用 mtime 而不是文件名日期更稳。如果源目录特别大建议改用增量方案先记录每个文件的修改时间或哈希只复制变化的文件但那是进阶玩法。日常使用先把这个按天全量跑起来比任何完美的备份方案都强。5.2 剪贴板历史记录让 CtrlC 不再被覆盖import pyperclip import time from datetime import datetime log_file clip_history.txt last_text last_time 0 while True: now datetime.now() text pyperclip.paste() if text and text ! last_text and (now.timestamp() - last_time) 1: with open(log_file, a, encodingutf-8) as f: f.write(f\n[{now:%Y-%m-%d %H:%M:%S}]\n{text}\n{- * 40}\n) last_text text last_time now.timestamp() time.sleep(0.5)这个脚本本质上是每 0.5 秒读一次剪贴板发现内容变了就追加到一个文本文件。pyperclip在 Windows 和 macOS 上很省心在 Linux 上需要先装xclip或xsel否则取不到剪贴板内容。很多人复制完一段话转头复制别的上一段就找不回来了有了这个历史文件至少能翻回来。改起来也很灵活想只记录网址就加一个判断想按天滚动日志就按日期生成文件名。必须提醒一个隐私问题剪贴板里可能出现密码、验证码、身份证号如果这个脚本常驻这些内容都会被明文记录下来。所以我的习惯是只在自己信任的个人电脑上开而且日志文件不要放桌面不给别人看的机会。它也有局限性只能抓文本复制图片是抓不到的想做完整的剪贴板管理器需要配合 GUI 和图片处理但核心逻辑就是这段轮询。提示剪贴板历史会明文记录密码等敏感内容别在共用机器上常驻运行。我自己实用的体会是自动化脚本起步阶段最忌讳贪大求全。很多人想做一个“一键全自动”的终极脚本结果写了三天还没跑通。不如像我这样每个脚本只解决一个痛点先让一个脚本稳定跑起来再慢慢加下一个。10 个脚本看着多拆开其实每个都在 30 行以内你完全可以照着改出属于自己的一套。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。