python对文件夹内文件去重

从爬虫到去重:一个Python脚本解决重复文件困扰

📌 问题起源

昨天写了一个百度图片爬虫,想搜点"斗图"素材。结果爬下来3000多张图片,但一半以上都是重复的!手动删除太费劲,于是写了个Python脚本来去重。本文将分享这个文件去重工具的实现原理、优化思路和使用方法。

🐍 一、文件去重脚本(完整版)

以下脚本基于MD5哈希值比对实现文件去重,支持所有文件类型(图片、文档、视频等)。已在Python 3.6环境下开发测试。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# 作者:GuoYabin
# 功能:基于MD5的文件夹重复文件删除工具
# 版本:v1.0
# 日期:2017-05-18

import os
import hashlib
import sys

def filecount():
    """统计当前目录下的文件数量(仅统计文件,不包含子目录)"""
    # dir /B 列出所有文件名,find /V /C "" 统计行数
    try:
        count = int(os.popen('dir /B | find /V /C ""').read().strip())
        return count
    except:
        # 如果不是Windows环境,使用Python原生方式统计
        files = [f for f in os.listdir('.') if os.path.isfile(f)]
        return len(files)

def md5sum(filename):
    """计算文件的MD5哈希值(分块读取,适合大文件)"""
    # 创建MD5对象
    md5 = hashlib.md5()
    
    try:
        with open(filename, 'rb') as f:
            # 分块读取,避免一次性加载大文件到内存
            while True:
                fb = f.read(8192)  # 8KB缓冲区
                if not fb:
                    break
                md5.update(fb)
        return md5.hexdigest()
    except Exception as e:
        print(f"警告: 无法读取文件 {filename} - {str(e)}")
        return None

def delfile():
    """删除当前目录下的重复文件(保留第一个出现的版本)"""
    all_md5 = {}  # 存储已保留文件的MD5值
    deleted_count = 0
    
    # 遍历当前目录
    for root, dirs, files in os.walk(os.getcwd()):
        for file in files:
            filepath = os.path.join(root, file)
            
            # 计算文件MD5
            file_md5 = md5sum(filepath)
            if file_md5 is None:
                continue
            
            # 如果MD5已存在,则删除文件
            if file_md5 in all_md5.values():
                try:
                    os.remove(filepath)
                    deleted_count += 1
                    print(f"删除重复文件: {file}")
                except Exception as e:
                    print(f"删除失败 {file}: {str(e)}")
            else:
                # 记录新文件的MD5
                all_md5[file] = file_md5
    
    return deleted_count

if __name__ == '__main__':
    print('''
==========================================
   文件去重工具 v1.0
   基于MD5哈希值比对
   支持所有文件类型
==========================================
    ''')
    
    # 确认操作
    keyword = input('请确认已把本程序放到要去重的文件夹内,按回车继续,按Ctrl+C退出\n')
    
    # 统计去重前文件数
    old_count = filecount()
    print(f'\n去重前共有 {old_count} 个文件\n')
    print('正在扫描并删除重复文件,请稍候...\n')
    
    # 执行去重
    deleted = delfile()
    
    # 统计去重后文件数
    new_count = filecount()
    
    # 输出结果
    print('\n' + '='*40)
    print(f'去重完成!')
    print(f'去重前文件数: {old_count}')
    print(f'去重后文件数: {new_count}')
    print(f'共删除重复文件: {deleted} 个')
    print('='*40)
    
    input('\n按回车键退出...')

📦 已编译版本下载(Windows免Python环境)

百度网盘: http://pan.baidu.com/s/1bpalugf 密码:kfk4

⚠️ Python 3.6编译版本需要安装VC2015运行库。如果不想安装运行库,可直接用Python 2.7运行源码。

🔍 二、核心原理与代码解析

2.1 文件去重的基本思路

文件去重的核心是判断两个文件内容是否相同。常用的方法有两种:

方法原理优点缺点
文件大小比较 比较文件字节数 速度快 不同文件可能大小相同(误判)
MD5哈希值 计算文件内容的哈希指纹 准确性高,冲突概率极低 大文件计算耗时
字节逐位比较 逐字节对比两个文件 100%准确 极慢,不适合大量文件

本脚本采用MD5哈希值方案,在准确性和性能之间取得平衡。

2.2 关键函数详解

📊 filecount() - 文件计数

# Windows命令方式(速度快)
count = int(os.popen('dir /B | find /V /C ""').read())

# 命令解析:
# dir /B          : 以简洁格式列出当前目录所有文件名
# find /V /C ""   : 统计非空行数(/V /C 组合)
# 最终得到文件总数

🔑 md5sum() - MD5计算

# 分块读取设计(关键优化)
def md5sum(filename):
    md5 = hashlib.md5()
    with open(filename, 'rb') as f:
        while True:
            fb = f.read(8192)  # 每次读取8KB
            if not fb:
                break
            md5.update(fb)
    return md5.hexdigest()

# 为什么要分块?
# 如果一次性读取整个文件(f.read()),当处理几百MB的大文件时,
# 会占用大量内存,甚至导致内存溢出。分块读取用时间换空间,适合各种大小的文件。

🗑️ delfile() - 去重核心

# 去重算法:
1. 创建一个空字典 all_md5,用于存储已保留文件的MD5
2. 遍历当前目录所有文件
3. 对每个文件计算MD5
4. 如果MD5已存在于字典中,说明是重复文件 → 删除
5. 如果MD5不存在,说明是新文件 → 记录到字典

# 这种算法保证:
# - 保留第一个出现的文件
# - 后续所有重复文件都会被删除

⚡ 三、性能优化与改进建议

原脚本在处理大量文件时有几个可以优化的地方:

优化点原脚本问题改进方案
大文件处理 已采用分块读取,合理 可增加缓冲区大小可配置
先按大小过滤 直接计算所有文件的MD5 先比较文件大小,只有大小相同的才计算MD5,可减少90%以上的MD5计算
进度显示 静默执行,用户不知道进度 添加进度条或定期输出已处理文件数
子目录支持 os.walk已支持子目录 可增加选项控制是否递归子目录

3.1 优化版代码片段(先按大小过滤)

def delfile_optimized():
    # 先按文件大小分组
    size_dict = {}
    for root, dirs, files in os.walk(os.getcwd()):
        for file in files:
            filepath = os.path.join(root, file)
            size = os.path.getsize(filepath)
            if size not in size_dict:
                size_dict[size] = []
            size_dict[size].append(filepath)
    
    # 只对大小相同的文件组计算MD5
    deleted = 0
    for size, file_list in size_dict.items():
        if len(file_list) <= 1:
            continue  # 没有重复可能
        
        md5_dict = {}
        for filepath in file_list:
            file_md5 = md5sum(filepath)
            if file_md5 in md5_dict:
                os.remove(filepath)
                deleted += 1
            else:
                md5_dict[file_md5] = filepath
    return deleted

⚠️ 四、使用注意事项

🔴 1. 删除不可恢复

脚本执行永久删除文件,不会放入回收站。建议先在一个测试文件夹中试用,确认效果后再用于重要数据。

🔴 2. 保留策略

脚本默认保留第一个遇到的文件,删除后续重复项。如果需要保留最新文件或特定命名规则的文件,需要对脚本进行修改。

🔴 3. 运行环境

  • 源码运行:需要Python 3.6+ 或 Python 2.7
  • 编译版本:Windows 7/8/10/2008+,可能需要VC2015运行库
  • Linux/macOS:需修改filecount()函数(已提供备选实现)

🔴 4. 文件名编码问题

如果文件名包含特殊字符或非英文字符,在Windows控制台可能出现显示乱码,但不影响文件删除操作。

📊 五、实战测试:3000张图片去重

在包含3128个文件的"斗图"文件夹中测试脚本效果:

测试项数值说明
去重前文件数 3128 爬虫下载的图片
去重后文件数 1456 去重率53.4%
执行时间 约45秒 取决于硬盘速度和文件大小

📝 六、总结与扩展

这个脚本虽然简单,但解决了实际问题。从"what a fuck program"到"真香",只差一个去重脚本的距离。未来可以扩展的功能:

  • 图形界面:用tkinter或PyQt做个简单的GUI
  • 相似图片识别:不只是完全重复,还能识别相似图片(需要用到图像处理库)
  • 软链接/硬链接替代删除:节省空间的同时保留文件
  • 跨平台支持:完善Linux/macOS的兼容性

—— 如果你也被重复文件困扰,希望这个脚本能帮到你。欢迎收藏转发~

无耻的求一下赞助

posted @ 2017-05-19 17:20  一起走过的路  阅读(2304)  评论(0)    收藏  举报