Python自动化办公的10个脚本合集(含源码)
作者:小庄-Python办公
前言
这些脚本覆盖邮件、Excel处理、图片、PDF和归档等常见办公场景,并在本文中完整嵌入源码,便于复制与学习。
1. 批量文件重命名 (bulk_rename.py)
批量文件重命名
用途:为目录内的文件批量添加前缀/后缀、序号,或进行字符串替换。
命令行用法:
python bulk_rename.py --dir “./docs” --prefix “NEW_” --suffix “_v2” --start 1 --pad 3 --replace “old:new” --ext “.pdf” --dry-run
示例:python bulk_rename.py --dir “./images” --prefix “IMG_” --start 1 --pad 4
源码
"""
批量文件重命名
用途:为目录内的文件批量添加前缀/后缀、序号,或进行字符串替换。
命令行用法:
python bulk_rename.py --dir "./docs" --prefix "NEW_" --suffix "_v2" \
--start 1 --pad 3 --replace "old:new" --ext ".pdf" --dry-run
示例:
python bulk_rename.py --dir "./images" --prefix "IMG_" --start 1 --pad 4
"""
import argparse
from pathlib import Path
def plan_new_name(path: Path, prefix: str, suffix: str, seq: int | None, pad: int, replace: str | None) -> Path:
base = path.stem
if replace:
if ":" in replace:
old, new = replace.split(":", 1)
else:
old, new = replace, ""
base = base.replace(old, new)
new_base = f"{prefix}{base}{suffix}"
if seq is not None:
new_base = f"{new_base}_{str(seq).zfill(pad)}"
return path.with_name(new_base + path.suffix)
def main():
parser = argparse.ArgumentParser(description="批量文件重命名")
parser.add_argument("--dir", required=True, help="目标目录")
parser.add_argument("--prefix", default="", help="添加到文件名开头的前缀")
parser.add_argument("--suffix", default="", help="添加到文件名末尾的后缀")
parser.add_argument("--start", type=int, help="序号起始值,例如1")
parser.add_argument("--pad", type=int, default=3, help="序号补零位数,如3生成001")
parser.add_argument("--replace", help="字符串替换,格式 old:new 或仅提供old删除之")
parser.add_argument("--ext", help="仅处理指定扩展名,如 .pdf 或 .jpg")
parser.add_argument("--dry-run", action="store_true", help="只预览重命名方案,不实际执行")
args = parser.parse_args()
root = Path(args.dir)
if not root.exists() or not root.is_dir():
raise SystemExit("目录不存在或不是目录")
files = [p for p in sorted(root.iterdir()) if p.is_file()]
if args.ext:
files = [p for p in files if p.suffix.lower() == args.ext.lower()]
seq = args.start
for i, f in enumerate(files):
current_seq = (seq + i) if seq is not None else None
new_path = plan_new_name(f, args.prefix, args.suffix, current_seq, args.pad, args.replace)
if args.dry_run:
print(f"预览: {f.name} -> {new_path.name}")
else:
f.rename(new_path)
print(f"重命名: {f.name} -> {new_path.name}")
if __name__ == "__main__":
main()
2. CSV转Excel (csv_to_excel.py)
CSV转Excel
用途:将CSV文件转换为Excel(支持指定工作表名)。
命令行用法:
python csv_to_excel.py --input “data.csv” --output “data.xlsx” --sheet-name “Sheet1”
示例:python csv_to_excel.py --input “report.csv” --output “report.xlsx”
源码
"""
CSV转Excel
用途:将CSV文件转换为Excel(支持指定工作表名)。
命令行用法:
python csv_to_excel.py --input "data.csv" --output "data.xlsx" --sheet-name "Sheet1"
示例:
python csv_to_excel.py --input "report.csv" --output "report.xlsx"
"""
import argparse
from pathlib import Path
import pandas as pd
def main():
parser = argparse.ArgumentParser(description="CSV转Excel")
parser.add_argument("--input", required=True, help="输入CSV文件")
parser.add_argument("--output", required=True, help="输出Excel文件")
parser.add_argument("--sheet-name", default="Sheet1", help="工作表名")
args = parser.parse_args()
df = pd.read_csv(args.input, encoding="utf-8-sig")
df.to_excel(args.output, index=False)
print(f"已生成: {args.output} (sheet: {args.sheet_name})")
if __name__ == "__main__":
main()
3. Excel按列去重 (excel_deduplicate.py)
Excel按列去重
用途:按指定关键列对Excel数据去重,并输出新的Excel文件。
命令行用法:
python excel_deduplicate.py --input data.xlsx --output dedup.xlsx --key-columns “邮箱,姓名”
示例:python excel_deduplicate.py --input data.xlsx --key-columns “ID”
源码
"""
Excel按列去重
用途:按指定关键列对Excel数据去重,并输出新的Excel文件。
命令行用法:
python excel_deduplicate.py --input data.xlsx --output dedup.xlsx --key-columns "邮箱,姓名"
示例:
python excel_deduplicate.py --input data.xlsx --key-columns "ID"
"""
import argparse
import pandas as pd
def parse_keys(text: str | None) -> list[str]:
if not text:
return []
return [k.strip() for k in text.split(",") if k.strip()]
def main():
parser = argparse.ArgumentParser(description="Excel按列去重")
parser.add_argument("--input", required=True, help="输入Excel文件")
parser.add_argument("--output", default="dedup.xlsx", help="输出Excel文件")
parser.add_argument("--key-columns", required=True, help="关键列,逗号分隔")
args = parser.parse_args()
keys = parse_keys(args.key_columns)
if not keys:
raise SystemExit("请提供至少一个关键列 --key-columns")
df = pd.read_excel(args.input)
dedup = df.drop_duplicates(subset=keys)
dedup.to_excel(args.output, index=False)
print(f"去重完成: {args.output} (由{len(df)}行 -> {len(dedup)}行)")
if __name__ == "__main__":
main()
4. 合并多个Excel文件到一个工作表 (excel_merge_files.py)
合并多个Excel文件到一个工作表
用途:批量读取目录下的Excel文件并合并为一个Excel;可选增加来源文件名列。
命令行用法:
python excel_merge_files.py --input-dir ./excels --pattern “*.xlsx” --output merged.xlsx --sheet-name merged --add-source-col
示例:python excel_merge_files.py --input-dir ./data --output all.xlsx --add-source-col
源码
"""
合并多个Excel文件到一个工作表
用途:批量读取目录下的Excel文件并合并为一个Excel;可选增加来源文件名列。
命令行用法:
python excel_merge_files.py --input-dir ./excels --pattern "*.xlsx" \
--output merged.xlsx --sheet-name merged --add-source-col
示例:
python excel_merge_files.py --input-dir ./data --output all.xlsx --add-source-col
"""
import argparse
from pathlib import Path
import pandas as pd
def main():
parser = argparse.ArgumentParser(description="合并目录中的多个Excel文件到一个")
parser.add_argument("--input-dir", required=True, help="包含Excel的目录")
parser.add_argument("--pattern", default="*.xlsx", help="文件匹配模式(默认*.xlsx)")
parser.add_argument("--output", default="merged.xlsx", help="输出Excel文件名")
parser.add_argument("--sheet-name", default="merged", help="输出工作表名")
parser.add_argument("--add-source-col", action="store_true", help="增加来源文件名列")
args = parser.parse_args()
root = Path(args.input_dir)
files = sorted(root.glob(args.pattern))
if not files:
raise SystemExit("未找到Excel文件")
frames = []
for f in files:
df = pd.read_excel(f)
if args.add_source_col:
df["源文件"] = f.name
frames.append(df)
merged = pd.concat(frames, ignore_index=True)
merged.to_excel(args.output, index=False)
print(f"合并完成: {args.output} (共{len(files)}个文件, {len(merged)}行)")
if __name__ == "__main__":
main()
5. Excel按列拆分 (excel_split_columns.py)
Excel按列拆分
用途:将每一列单独导出为独立文件(xlsx或csv)。
命令行用法:
python excel_split_columns.py --input “data.xlsx” --sheet “Sheet1” --output-dir “./out_cols” --to “xlsx”
示例:python excel_split_columns.py --input “data.xlsx” --to “csv”
源码
"""
Excel按列拆分
用途:将每一列单独导出为独立文件(xlsx或csv)。
命令行用法:
python excel_split_columns.py --input "data.xlsx" --sheet "Sheet1" \
--output-dir "./out_cols" --to "xlsx"
示例:
python excel_split_columns.py --input "data.xlsx" --to "csv"
"""
import argparse
from pathlib import Path
import pandas as pd
def main():
parser = argparse.ArgumentParser(description="按列拆分Excel为多个文件")
parser.add_argument("--input", required=True, help="输入Excel文件")
parser.add_argument("--sheet", help="工作表名称")
parser.add_argument("--output-dir", default="out_cols", help="输出目录")
parser.add_argument("--to", choices=["xlsx", "csv"], default="xlsx", help="导出格式")
args = parser.parse_args()
out_dir = Path(args.output_dir)
out_dir.mkdir(parents=True, exist_ok=True)
df = pd.read_excel(args.input, sheet_name=args.sheet)
for col in df.columns:
col_df = df[[col]]
safe = "".join(c if c not in "/\\:*?\"<>|" else "_" for c in str(col))
if args.to == "csv":
out_path = out_dir / f"col_{safe}.csv"
col_df.to_csv(out_path, index=False, encoding="utf-8-sig")
else:
out_path = out_dir / f"col_{safe}.xlsx"
col_df.to_excel(out_path, index=False)
print(f"导出: {out_path}")
if __name__ == "__main__":
main()
6. Excel按行拆分(按列分组导出) (excel_split_rows.py)
Excel按行拆分(按列分组导出)
用途:按某一列的唯一值,将Excel工作表拆分为多个文件;未提供分组列时按每行导出一个文件。
命令行用法:
python excel_split_rows.py --input “data.xlsx” --sheet “Sheet1” --key-column “部门” --output-dir “./out”
示例:python excel_split_rows.py --input “data.xlsx” --key-column “部门”
源码
"""
Excel按行拆分(按列分组导出)
用途:按某一列的唯一值,将Excel工作表拆分为多个文件;未提供分组列时按每行导出一个文件。
命令行用法:
python excel_split_rows.py --input "data.xlsx" --sheet "Sheet1" \
--key-column "部门" --output-dir "./out"
示例:
python excel_split_rows.py --input "data.xlsx" --key-column "部门"
"""
import argparse
from pathlib import Path
import pandas as pd
def sanitize_filename(name: str) -> str:
return "".join(c if c not in "/\\:*?\"<>|" else "_" for c in str(name)).strip()
def main():
parser = argparse.ArgumentParser(description="按列分组或按行将Excel拆分为多个文件")
parser.add_argument("--input", required=True, help="输入Excel文件路径")
parser.add_argument("--sheet", help="工作表名称,默认首个工作表")
parser.add_argument("--key-column", help="分组列名,按此列的唯一值拆分")
parser.add_argument("--output-dir", default="out_rows", help="输出目录")
args = parser.parse_args()
out_dir = Path(args.output_dir)
out_dir.mkdir(parents=True, exist_ok=True)
df = pd.read_excel(args.input, sheet_name=args.sheet)
if args.key_column and args.key_column in df.columns:
for key, group in df.groupby(args.key_column):
fname = sanitize_filename(key)
out_path = out_dir / f"{fname}.xlsx"
group.to_excel(out_path, index=False)
print(f"导出: {out_path}")
else:
for i in range(len(df)):
row_df = df.iloc[[i]]
out_path = out_dir / f"row_{i+1}.xlsx"
row_df.to_excel(out_path, index=False)
print(f"导出: {out_path}")
if __name__ == "__main__":
main()
7. 文件夹打包为ZIP (folder_backup_zip.py)
文件夹打包为ZIP
用途:将指定目录打包为ZIP,支持排除某些通配模式的文件。
命令行用法:
python folder_backup_zip.py --source-dir ./project --output backup.zip --exclude “.tmp,.log”
示例:python folder_backup_zip.py --source-dir ./docs --output docs.zip
源码
"""
文件夹打包为ZIP
用途:将指定目录打包为ZIP,支持排除某些通配模式的文件。
命令行用法:
python folder_backup_zip.py --source-dir ./project --output backup.zip --exclude "*.tmp,*.log"
示例:
python folder_backup_zip.py --source-dir ./docs --output docs.zip
"""
import argparse
import fnmatch
import os
from pathlib import Path
import zipfile
def parse_patterns(text: str | None) -> list[str]:
if not text:
return []
return [p.strip() for p in text.split(",") if p.strip()]
def should_exclude(name: str, patterns: list[str]) -> bool:
return any(fnmatch.fnmatch(name, pat) for pat in patterns)
def main():
parser = argparse.ArgumentParser(description="文件夹打包为ZIP")
parser.add_argument("--source-dir", required=True, help="源目录")
parser.add_argument("--output", default="backup.zip", help="输出ZIP文件名")
parser.add_argument("--exclude", help="排除模式,逗号分隔,如 *.tmp,*.log")
parser.add_argument("--store-absolute", action="store_true", help="在ZIP内保存绝对路径(默认保存相对路径)")
args = parser.parse_args()
src = Path(args.source_dir)
if not src.exists() or not src.is_dir():
raise SystemExit("源目录不存在")
patterns = parse_patterns(args.exclude)
with zipfile.ZipFile(args.output, "w", compression=zipfile.ZIP_DEFLATED) as zf:
for root, _, files in os.walk(src):
for name in files:
full = Path(root) / name
rel = full if args.store_absolute else full.relative_to(src)
if should_exclude(name, patterns):
continue
zf.write(full, arcname=str(rel))
print(f"添加: {rel}")
print(f"打包完成: {args.output}")
if __name__ == "__main__":
main()
8. 批量图片改尺寸 (image_resize_batch.py)
批量图片改尺寸
用途:批量将图片按指定尺寸缩放,支持保持宽高比与输出质量。
命令行用法:
python image_resize_batch.py --input-dir “./images” --output-dir “./resized” --width 1024 --height 768 --keep-aspect --quality 85
示例:python image_resize_batch.py --input-dir ./imgs --output-dir ./out --width 1080 --keep-aspect
源码
"""
批量图片改尺寸
用途:批量将图片按指定尺寸缩放,支持保持宽高比与输出质量。
命令行用法:
python image_resize_batch.py --input-dir "./images" --output-dir "./resized" \
--width 1024 --height 768 --keep-aspect --quality 85
示例:
python image_resize_batch.py --input-dir ./imgs --output-dir ./out --width 1080 --keep-aspect
"""
import argparse
from pathlib import Path
from PIL import Image
SUPPORTED_EXTS = {".jpg", ".jpeg", ".png", ".bmp", ".tif", ".tiff", ".webp"}
def calc_size(orig_w: int, orig_h: int, width: int | None, height: int | None, keep_aspect: bool) -> tuple[int, int]:
if not keep_aspect:
return width or orig_w, height or orig_h
# 保持比例:按给定边界等比缩放
if width and height:
r = min(width / orig_w, height / orig_h)
return max(1, int(orig_w * r)), max(1, int(orig_h * r))
if width and not height:
r = width / orig_w
return max(1, int(orig_w * r)), max(1, int(orig_h * r))
if height and not width:
r = height / orig_h
return max(1, int(orig_w * r)), max(1, int(orig_h * r))
return orig_w, orig_h
def main():
parser = argparse.ArgumentParser(description="批量图片改尺寸")
parser.add_argument("--input-dir", required=True, help="输入图片目录")
parser.add_argument("--output-dir", default="resized", help="输出目录")
parser.add_argument("--width", type=int, help="目标宽度")
parser.add_argument("--height", type=int, help="目标高度")
parser.add_argument("--keep-aspect", action="store_true", help="保持宽高比")
parser.add_argument("--quality", type=int, default=85, help="JPEG质量(1-95)")
args = parser.parse_args()
in_dir = Path(args.input_dir)
out_dir = Path(args.output_dir)
out_dir.mkdir(parents=True, exist_ok=True)
for p in sorted(in_dir.iterdir()):
if not p.is_file() or p.suffix.lower() not in SUPPORTED_EXTS:
continue
img = Image.open(p)
new_w, new_h = calc_size(img.width, img.height, args.width, args.height, args.keep_aspect)
resized = img.resize((new_w, new_h), Image.LANCZOS)
out_path = out_dir / p.name
save_kwargs = {}
if out_path.suffix.lower() in {".jpg", ".jpeg"}:
save_kwargs["quality"] = args.quality
save_kwargs["optimize"] = True
resized.save(out_path, **save_kwargs)
print(f"导出: {out_path} ({img.width}x{img.height} -> {new_w}x{new_h})")
if __name__ == "__main__":
main()
9. 批量PDF合并 (pdf_merge.py)
批量PDF合并
用途:将目录中的多个PDF文件按名称排序合并为一个PDF。
命令行用法:
python pdf_merge.py --input-dir ./pdfs --pattern “*.pdf” --output merged.pdf
示例:python pdf_merge.py --input-dir ./pdfs --output all.pdf
源码
"""
批量PDF合并
用途:将目录中的多个PDF文件按名称排序合并为一个PDF。
命令行用法:
python pdf_merge.py --input-dir ./pdfs --pattern "*.pdf" --output merged.pdf
示例:
python pdf_merge.py --input-dir ./pdfs --output all.pdf
"""
import argparse
from pathlib import Path
from pypdf import PdfWriter, PdfReader
def main():
parser = argparse.ArgumentParser(description="批量PDF合并")
parser.add_argument("--input-dir", required=True, help="PDF所在目录")
parser.add_argument("--pattern", default="*.pdf", help="文件匹配模式(默认*.pdf)")
parser.add_argument("--output", default="merged.pdf", help="输出PDF文件名")
args = parser.parse_args()
root = Path(args.input_dir)
files = sorted(root.glob(args.pattern))
if not files:
raise SystemExit("未找到PDF文件")
writer = PdfWriter()
for f in files:
reader = PdfReader(f)
for page in reader.pages:
writer.add_page(page)
print(f"合并: {f.name}")
with open(args.output, "wb") as out:
writer.write(out)
print(f"完成: {args.output}")
if __name__ == "__main__":
main()
10. 自动发邮件 (send_email.py)
自动发邮件
用途:通过 SMTP 发送邮件,支持多个收件人、抄送、附件目录;可选从环境变量/.env读取配置。
依赖:标准库(smtplib、email、ssl、mimetypes),可选 python-dotenv。
命令行用法:
python send_email.py --smtp-server smtp.example.com --port 465 --username user@example.com --password your_password --to “a@x.com,b@y.com” --subject “主题” --body-file “body.md” --attachments-dir “./attachments”
示例:python send_email.py --use-env --to “boss@company.com” --subject “周报” --body “本周进展见附件” --attachments-dir “./out”
源码
"""
自动发邮件
用途:通过 SMTP 发送邮件,支持多个收件人、抄送、附件目录;可选从环境变量/.env读取配置。
依赖:标准库(smtplib、email、ssl、mimetypes),可选 python-dotenv。
命令行用法:
python send_email.py --smtp-server smtp.example.com --port 465 \
--username user@example.com --password your_password \
--to "a@x.com,b@y.com" --subject "主题" --body-file "body.md" \
--attachments-dir "./attachments"
示例:
python send_email.py --use-env --to "boss@company.com" --subject "周报" \
--body "本周进展见附件" --attachments-dir "./out"
"""
import argparse
import mimetypes
import os
import ssl
from email.message import EmailMessage
from pathlib import Path
import smtplib
try:
from dotenv import load_dotenv # type: ignore
except Exception:
load_dotenv = None
def parse_list(text: str | None) -> list[str]:
if not text:
return []
return [x.strip() for x in text.split(",") if x.strip()]
def read_body(body: str | None, body_file: str | None) -> str:
if body_file:
return Path(body_file).read_text(encoding="utf-8")
return body or ""
def collect_attachments(dir_path: str | None) -> list[Path]:
if not dir_path:
return []
p = Path(dir_path)
if not p.exists() or not p.is_dir():
return []
return [f for f in sorted(p.iterdir()) if f.is_file()]
def build_message(sender: str, to: list[str], cc: list[str], subject: str, body: str, html: bool,
attachments: list[Path]) -> EmailMessage:
msg = EmailMessage()
msg["From"] = sender
msg["To"] = ", ".join(to)
if cc:
msg["Cc"] = ", ".join(cc)
msg["Subject"] = subject
if html:
msg.add_alternative(body, subtype="html")
else:
msg.set_content(body)
for path in attachments:
ctype, encoding = mimetypes.guess_type(path.name)
if ctype is None or encoding is not None:
ctype = "application/octet-stream"
maintype, subtype = ctype.split("/", 1)
with open(path, "rb") as f:
msg.add_attachment(f.read(), maintype=maintype, subtype=subtype, filename=path.name)
return msg
def send_email(
smtp_server: str,
port: int,
username: str,
password: str,
sender: str,
to: list[str],
cc: list[str],
bcc: list[str],
subject: str,
body: str,
html: bool,
attachments: list[Path],
) -> None:
# 构建并发送邮件
message = build_message(sender, to, cc, subject, body, html, attachments)
all_rcpts = list({*(to or []), *(cc or []), *(bcc or [])})
context = ssl.create_default_context()
# 465默认走SSL,其他端口尝试StartTLS
if port == 465:
with smtplib.SMTP_SSL(smtp_server, port, context=context) as server:
server.login(username, password)
server.send_message(message, from_addr=sender, to_addrs=all_rcpts)
else:
with smtplib.SMTP(smtp_server, port) as server:
server.ehlo()
try:
server.starttls(context=context)
server.ehlo()
except smtplib.SMTPException:
pass
server.login(username, password)
server.send_message(message, from_addr=sender, to_addrs=all_rcpts)
def main():
parser = argparse.ArgumentParser(description="通过SMTP发送邮件,支持附件、抄送、HTML正文")
parser.add_argument("--smtp-server", help="SMTP服务器地址")
parser.add_argument("--port", type=int, default=465, help="SMTP端口,默认465(SSL)")
parser.add_argument("--username", help="SMTP用户名")
parser.add_argument("--password", help="SMTP密码/授权码")
parser.add_argument("--from", dest="sender", help="发件人地址(默认同用户名)")
parser.add_argument("--to", required=True, help="收件人列表,逗号分隔")
parser.add_argument("--cc", help="抄送列表,逗号分隔")
parser.add_argument("--bcc", help="密送列表,逗号分隔")
parser.add_argument("--subject", default="(无主题)", help="邮件主题")
parser.add_argument("--body", help="纯文本正文")
parser.add_argument("--body-file", help="从文件读取正文")
parser.add_argument("--html", action="store_true", help="将正文按HTML发送")
parser.add_argument("--attachments-dir", help="附件目录,将目录下所有文件作为附件")
parser.add_argument("--use-env", action="store_true", help="从环境变量/.env读取SMTP配置")
args = parser.parse_args()
if args.use_env and load_dotenv:
# 允许使用本地 .env 文件
if Path(".env").exists():
load_dotenv(".env")
else:
load_dotenv()
smtp_server = args.smtp_server or os.getenv("SMTP_SERVER") or ""
port = args.port or int(os.getenv("SMTP_PORT", "465"))
username = args.username or os.getenv("SMTP_USER") or ""
password = args.password or os.getenv("SMTP_PASS") or ""
if not smtp_server or not username or not password:
raise SystemExit("缺少SMTP配置:请提供 --smtp-server/--username/--password 或使用 --use-env 并设置环境变量")
sender = args.sender or username
to = parse_list(args.to)
cc = parse_list(args.cc)
bcc = parse_list(args.bcc)
body = read_body(args.body, args.body_file)
attachments = collect_attachments(args.attachments_dir)
if not to:
raise SystemExit("至少需要一个收件人 --to")
send_email(
smtp_server=smtp_server,
port=port,
username=username,
password=password,
sender=sender,
to=to,
cc=cc,
bcc=bcc,
subject=args.subject,
body=body,
html=args.html,
attachments=attachments,
)
if __name__ == "__main__":
main()
以上就是Python自动化办公的10个脚本合集(含源码)的详细内容,更多关于Python自动化办公脚本的资料请关注脚本之家其它相关文章!
