Python实现文件夹增量同步到移动硬盘的定时备份脚本
对于长期在电脑本地存放工作资料的人来说,一旦硬盘损坏、电脑丢失或遭遇勒索病毒,数据可能瞬间归零。手动复制粘贴容易遗漏,也难坚持。利用 Python 写一个增量同步脚本,再配合 Windows 任务计划程序,即可实现指定文件夹的定时自动备份。本文围绕这一需求,给出完整的脚本实现与关键代码解析。增量同步的核心思路是:每次运行时,只复制源目录中新增的、修改过的或大小发生变化的文件,而不是全量拷贝。这样既能节省时间,也能减少磁盘写入。实现上通过对比源文件和目标文件的修改时间、文件大小来决定是否需要复制,使用 shutil.copy2 完成复制并保留文件元数据。
环境方面,脚本只用 Python 标准库,无需安装第三方模块。需要特别提醒的是,shutil、os、pathlib、datetime 都是内置模块,直接 import 即可,不需要执行 pip install shutil,网上有的教程会误导读者去安装一个不存在的 PyPI 包。
下面是完整实现。脚本提供三个方案:增量同步、打包压缩备份、版本化备份。日常推荐使用增量同步,重要节点可配合压缩包和版本历史。
import os
import shutil
import time
from pathlib import Path
from datetime import datetime
# ==================== 方案 1:增量文件同步 ====================
def sync_folders(source_dir, backup_dir, log_file=None):
"""
将源文件夹增量同步到备份文件夹
参数:
source_dir: 源文件夹路径(原始文件)
backup_dir: 备份文件夹路径(目标位置)
log_file: 日志文件路径(可选)
"""
source = Path(source_dir)
backup = Path(backup_dir)
if not source.exists():
print(f"错误:源目录 {source_dir} 不存在!")
return False
# 创建备份目录
backup.mkdir(parents=True, exist_ok=True)
files_copied = 0
files_updated = 0
files_skipped = 0
total_size = 0
log_lines = []
def log(msg):
print(msg)
log_lines.append(msg)
log(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 开始同步")
log(f"源目录: {source}")
log(f"备份目录: {backup}")
log("-" * 50)
# 遍历源目录所有文件
for file_path in source.rglob('*'):
if file_path.is_file():
# 计算相对路径
rel_path = file_path.relative_to(source)
dest_path = backup / rel_path
# 判断是否需要复制
need_copy = False
if not dest_path.exists():
need_copy = True
action = "新增"
elif file_path.stat().st_mtime > dest_path.stat().st_mtime:
need_copy = True
action = "更新"
elif file_path.stat().st_size != dest_path.stat().st_size:
need_copy = True
action = "大小变更"
if need_copy:
# 确保目标目录存在
dest_path.parent.mkdir(parents=True, exist_ok=True)
try:
shutil.copy2(str(file_path), str(dest_path))
file_size = file_path.stat().st_size
total_size += file_size
if action == "新增":
files_copied += 1
else:
files_updated += 1
log(f"[{action}] {rel_path} ({file_size / 1024:.1f} KB)")
except Exception as e:
log(f"[失败] {rel_path}: {e}")
else:
files_skipped += 1
# 汇总报告
log("-" * 50)
log(f"同步完成!")
log(f"新增文件: {files_copied}")
log(f"更新文件: {files_updated}")
log(f"跳过文件: {files_skipped}")
log(f"总复制量: {total_size / 1024 / 1024:.2f} MB")
# 写入日志
if log_file:
Path(log_file).parent.mkdir(parents=True, exist_ok=True)
with open(log_file, 'a', encoding='utf-8') as f:
f.write('\n'.join(log_lines) + '\n\n')
return True
# ==================== 方案 2:打包压缩备份 ====================
def backup_as_zip(source_dir, backup_dir):
"""
将整个文件夹打包为带日期的 ZIP 压缩包
"""
source = Path(source_dir)
backup = Path(backup_dir)
backup.mkdir(parents=True, exist_ok=True)
date_str = datetime.now().strftime('%Y%m%d_%H%M%S')
folder_name = source.name
zip_name = f"{folder_name}_备份_{date_str}"
zip_path = shutil.make_archive(
str(backup / zip_name),
'zip',
str(source)
)
file_size = os.path.getsize(zip_path) / 1024 / 1024
print(f"备份完成: {zip_path} ({file_size:.2f} MB)")
return zip_path
# ==================== 方案 3:保留多个历史版本 ====================
def versioned_backup(source_dir, backup_base_dir, keep_versions=5):
"""
保留多个历史版本的备份(类似时间机器)
参数:
source_dir: 源目录
backup_base_dir: 备份根目录
keep_versions: 保留最近的几个版本
"""
source = Path(source_dir)
backup_base = Path(backup_base_dir)
backup_base.mkdir(parents=True, exist_ok=True)
# 按日期创建版本文件夹
date_str = datetime.now().strftime('%Y%m%d_%H%M%S')
version_dir = backup_base / f"v{date_str}"
# 全量复制
if version_dir.exists():
shutil.rmtree(version_dir)
shutil.copytree(str(source), str(version_dir))
print(f"已创建备份版本: {version_dir}")
# 清理旧版本(保留最新的 keep_versions 个)
versions = sorted(backup_base.iterdir())
while len(versions) > keep_versions:
old_version = versions.pop(0)
if old_version.is_dir():
shutil.rmtree(old_version)
print(f"已清理旧版本: {old_version}")
# ==================== 使用示例 ====================
if __name__ == "__main__":
# 配置项
SOURCE = r"D:\工作资料"
BACKUP = r"E:\备份\工作资料"
# 方案 1:增量同步
sync_folders(SOURCE, BACKUP, log_file="backup_log.txt")
# 方案 2:打包压缩
# backup_as_zip(SOURCE, r"E:\备份")
# 方案 3:版本化备份(保留 5 个版本)
# versioned_backup(SOURCE, r"E:\备份\版本历史", keep_versions=5)
这段代码中,增量同步是最常用的场景。其判断逻辑采用了三层条件:目标文件不存在时视为新增;源文件修改时间晚于目标文件时视为更新;文件大小不同时也视为变更。这样能覆盖绝大多数文件变动情况。复制时使用 shutil.copy2,它与 shutil.copy 的区别在于,copy2 会额外尝试保留文件的修改时间、访问时间等元数据,让备份文件与源文件属性尽可能一致。
目录遍历使用 Path.rglob('*'),它会递归取得所有子目录和文件,配合 file_path.is_file() 过滤出文件,用 file_path.relative_to(source) 得到相对路径,再拼接到备份目录下。这种写法比 os.walk 更简洁,也更符合面向对象风格。
如果希望脚本自动定时运行,可以配合 Windows 任务计划程序。先建立一个 .bat 批处理文件,内容为:
@echo off
python "D:\scripts\auto_backup.py"
然后在系统的“任务计划程序”中创建基本任务,设置触发器(例如每天 18:00),操作选择启动这个 .bat 文件即可。这样脚本就会在指定时间自动执行,全程无需人工干预。
备份完成后,如果需要弹窗提示,可以在脚本末尾加入 Windows 原生消息框调用:
import ctypes
ctypes.windll.user32.MessageBoxW(0, "备份完成!", "自动备份", 0x40)
如果只需要备份特定类型的文件,可以在遍历时加入后缀名过滤。例如只备份文档和表格:
ALLOWED_EXTENSIONS = {'.docx', '.xlsx', '.pdf'}
for file_path in source.rglob('*'):
if file_path.is_file() and file_path.suffix.lower() in ALLOWED_EXTENSIONS:
# 同步逻辑
pass
关于网络驱动器或 NAS 的备份,路径可以直接使用 UNC 格式,比如:
BACKUP = r"\\192.168.1.100\shared\backup\工作资料"
同步过程中如果遇到断电或中断,不必担心。因为脚本是逐个文件复制的,已经复制完的文件会保留目标文件及其元数据。下次重新运行同步时,脚本会根据修改时间和大小判断出这些文件无需再次复制,从而自动跳过,继续处理未完成的文件。
备份占用空间过大时,有两个优化方向。一是使用版本化备份函数 versioned_backup,通过 keep_versions 参数限制保留的版本数量,自动清理旧版本。二是使用 zipfile 模块进行压缩归档,例如:
import zipfile
with zipfile.ZipFile(backup_path, 'w', zipfile.ZIP_DEFLATED) as zf:
for file_path in source.rglob('*'):
if file_path.is_file():
arcname = file_path.relative_to(source)
zf.write(file_path, arcname)
总结一下,三种备份策略各有适用场景。增量同步 sync_folders 适合日常高频备份,速度快,占用空间相对小;压缩打包 backup_as_zip 适合需要传输或归档的场景;版本化备份 versioned_backup 类似时间机器,适合需要回溯历史版本的场景。推荐组合使用:日常增量同步 + 每周打包压缩 + 保留最近 3 到 5 个版本,这样既能保证数据安全,又不会让备份文件夹失控增长。
Re: Python实现文件夹增量同步到移动硬盘的定时备份脚本
楼主的思路很实用,标准库就能搞定,特别适合不想装第三方依赖的场景。那个“不要 pip install shutil”的提醒很到位,网上确实有些教程会误导新手。 增量判断我用过类似方案,不过有几个点想和楼主探讨一下:只比对修改时间和大小,如果文件内容被改但时间被刻意还原,可能漏同步。当然日常备份够用了。另外目前只处理了源目录新增或更新的文件,如果源目录里删了文件,备份端不会自动清理,时间长了可能积累冗余。可以加一个反向遍历,把备份端有但源端没有的文件移到一个“已删除”文件夹,或者按需开启。 还有就是如果文件数量特别大,`rglob` 全量遍历一次也可能耗时,不过对于个人工作资料来说通常没问题。 方案里提到还有压缩打包和版本化备份,期待这部分。日常用增量同步,重要节点再打包,这个习惯确实比较稳妥。感谢分享,收藏了。Re: Python实现文件夹增量同步到移动硬盘的定时备份脚本
感谢分享,这个脚本很实用。增量同步的思路清晰,用修改时间和大小判断是否复制也够用了,日常备份完全够使。特别是提醒了不要 pip install shutil 这点很贴心,确实容易踩坑。 方案里提到打包压缩和版本化备份,不知道后面会不会展开讲?如果能把删除文件的处理也考虑进去就更好了,现在这样只增不改删,长期看备份目录会越积越大。另外如果源文件数量特别多,每次遍历全部文件可能有点慢,或许可以加个简单的文件清单缓存来提速。Re: Python实现文件夹增量同步到移动硬盘的定时备份脚本
这脚本很实用,正好解决了我经常忘了备份的毛病。增量同步的逻辑清晰,日志输出也方便排查。有个小建议,如果能把删除操作也同步过去就更好了,比如源目录删了文件,备份那边也自动清掉,这样备份会更干净。当然有历史版本需求的话另说。总之感谢分享,先收藏了!
页:
[1]