使用场景

网上爬取的图片重复量太大,需要删除重复图片。

环境

python 3
hashilib

分析一下这个问题:

首先,文件个数非常多,手工查找是不现实的,再说,单凭我们肉眼,在几千张图片或文件里面找到完全相同的难度也是很大的。所以要用程序实现。那么用程序怎么实现呢?根据什么判断两个文件完全相同呢?

  • 首先,根据文件名判断是靠不住的,因为文件名可以被随意更改,但文件内容不变。再说在同一个文件夹下面,也不可能出现两个完全相同的文件名,操作系统不允许的。
  • 还有一种方法就是根据文件大小来判断,这不失为一种好办法,但是,文件大小相同的图片可能不一样。再说图片一般都比较小,超过3M的基本没有,大部分不够1M,如果文件夹下面文件特别多,出现大小相同的的文件可能性是相当大的。所以单凭文件大小来比较不靠谱。
  • 还有一种方法是读取每张图片的内容,然后比较这个图片的内容和其他图片是否完全相同,如果内容相同那么这两张图片肯定是完全相同的。这种方法看起来是比较完美的,让我们来分析一下他的时空效率:首先每张图片的内容都要和其他图片进行比较,这就是一个二重循环,读取的效率低,比较的效率更低,所有的都比较下来是非常费时的!内存方面,如果预先把所有图片读取到内存可以加快文件的比较效率,但是普通计算机的内存资源有限,如果图片非常多,好几个G的话,都读到内存是不现实的。如果不把所有的文件读取到内存,那么每比较一次之前就要先读取文件内容,比较几次就要读取几次,从硬盘读取数据是比较慢的,这样做显然不合适。
  • 那么有没有更好的方法呢?我冥思苦想,绞尽脑汁,最后想到了md5。也许你会问,md5不是加密的吗?和我们的问题有关系吗?问得好!md5可以把任意长度的字符串进行加密后形成一个32的字符序列,包括数字和字母(大写或小写),因为字符串任何微小的变动都会导致md5序列改变,因此md5可以看作一个字符串的‘指纹’或者‘信息摘要’,因为md5字符串总共有36的32次方个,所以两个不同的字符串得到一个相同的md5概率是很小的,几乎为0,同样的道理,我们可以得到每个文件的md5,若干文件的md5相同的话就基本上可以肯定两个文件是相同的,因为md5相同而文件不同的概率太小了,基本可以忽略,这样我们就可以这样做:得到每个文件的md5,通过比较md5是否相同我们就可以确定两张图片是否相同。

下面是代码实现:

# -*- coding: cp936 -*-
import hashlib
import os
import timedef getmd5(filename):"""获取文件 md5 码:param filename: 文件路径:return: 文件 md5 码"""file_txt = open(filename, 'rb').read()# 调用一个md5对象m = hashlib.md5(file_txt)# hexdigest()方法来获取摘要(加密结果)return m.hexdigest()def main():# 文件夹路径path = input("path: ")# 存放文件的 md5 码all_md5 = []total_file = 0total_delete = 0# 开始时间start = time.time()# 遍历文件夹下的所有文件for file in os.listdir(path):# 文件数量加 1total_file += 1# 文件的路径real_path = os.path.join(path, file)# 判断文件是否是文件if os.path.isfile(real_path) == True:# 获取文件的md5码filemd5 = getmd5(real_path)# 如果文件 md5 已存在,则删除此文件if filemd5 in all_md5:total_delete += 1print('删除', file)else:# 如果文件 md5 不存在,则将此文件的 md5 码添加到 all_md5 列表中all_md5.append(filemd5)# 结束时间end = time.time()time_last = end - startprint('文件总数:', total_file)print('删除个数:', total_delete)print('耗时:', time_last, '秒')if __name__ == '__main__':main()

上面的程序原理很简单,就是依次读取每个文件,计算md5,如果md5在md5列表不存在,就把这个md5加到md5列表里面去,如果存在的话,我们就认为这个md5对应的文件已经出现过,这个图片就是多余的,然后我们就可以把这个图片删除了。下面是程序的运行截图:

我们可以看到,在这个文件夹下面有5235个文件,有144个是重复的,找到所有重复文件共耗时1.88秒。效率不算高,能不能进行优化呢?我分析了一下,我的程序里面有两个功能比较耗时间,一个是计算每个文件的md5,这个占了大部分时间,还有就是在列表中查找md5是否存在,也比较费时间的。从这两方面入手,我们可以进一步优化。

首先我想的是解决查找问题,或许我们可以对列表中的元素先排一下序,然后再去查找,但是列表是变化的,每次都排序的话效率就比较低了。我想的是利用字典进行优化。字典最显著的特点是一个key对应一个值我们可以把md5作为key,key对应的值就不需要了,在变化的情况下字典的查找效率比序列效率高,因为序列是无序的,而字典是有序的,查找起来当然更快。这样我们只要判断md5值是否在所有的key中就可以了。下面是改进后的代码:

# -*- coding: cp936 -*-
import hashlib
import os
import timedef getmd5(filename):"""获取文件 md5 码:param filename: 文件路径:return: 文件 md5 码"""file_txt = open(filename, 'rb').read()# 调用一个md5对象m = hashlib.md5(file_txt)# hexdigest()方法来获取摘要(加密结果)return m.hexdigest()def main():# 文件夹路径path = input("path: ")# 存放文件的 md5 码all_md5 = {}  # 改为字典total_file = 0total_delete = 0# 开始时间start = time.time()# 遍历文件夹下的所有文件for file in os.listdir(path):# 文件数量加 1total_file += 1# 文件的路径real_path = os.path.join(path, file)# 判断文件是否是文件if os.path.isfile(real_path) == True:# 获取文件的md5码filemd5 = getmd5(real_path)# 如果文件 md5 已存在,则删除此文件if filemd5 in all_md5.keys():  # 字典的键为文件 md5 码total_delete += 1print('删除', file)else:# 如果文件 md5 不存在,则将此文件的 md5 码添加到 all_md5 字典中all_md5[filemd5] = ""# 结束时间end = time.time()time_last = end - startprint('文件总数:', total_file)print('删除个数:', total_delete)print('耗时:', time_last, '秒')if __name__ == '__main__':main()

再看看运行截图:

从时间上看,确实比原来快了一点,但是还不理想。下面还要进行优化。还有什么可以优化呢?md5!上面的程序,每个文件都要计算md5,非常费时间,是不是每个文件都需要计算md5呢?能不能想办法减少md5的计算次数呢?我想到了一种方法:上面分析时我们提到,可以通过比较文件大小的方式来判断图片是否完全相同,速度快,但是这种方法是不准确的,md5是准确的,我们能不能把两者结合一下?答案是肯定的。我们可以认定:如果两个文件完全相同,那么这两个文件的大小和md5一定相同,如果两个文件的大小不同,那么这两个文件肯定不同!这样的话,我们只需要先查看文件的大小是否存在在size字典中,如果不存在,就将它加入到size字典中,如果大小存在的话,这说明有至少两张图片大小相同,那么我们只要计算文件大小相同的文件的md5,如果md5相同,那么这两个文件肯定完全一样,我们可以删除,如果md5不同,我们把它加到列表里面,避免重复计算md5。具体代码实现如下(最终版本):

# -*- coding: cp936 -*-
import hashlib
import os
import timedef getmd5(filename):"""获取文件 md5 码:param filename: 文件路径:return: 文件 md5 码"""file_txt = open(filename, 'rb').read()# 调用一个md5对象m = hashlib.md5(file_txt)# hexdigest()方法来获取摘要(加密结果)return m.hexdigest()def main():# 文件夹路径path = input("path: ")# 键为文件大小, 值为列表(文件路径、md5)all_size = {}total_file = 0total_delete = 0# 开始时间start = time.time()# 遍历文件夹下的所有文件for file in os.listdir(path):# 文件数量加 1total_file += 1# 文件的路径real_path = os.path.join(path, file)# 判断文件是否是文件if os.path.isfile(real_path) == True:# 获取文件大小size = os.stat(real_path).st_size# md5(默认为空)size_and_md5 = [""]# 如果文件大小已存在if size in all_size.keys():# 获取文件的md5码new_md5 = getmd5(real_path)# 大小相同,md5 为空,添加md5if all_size[size][0] == "":all_size[size][0] = new_md5# md5 已存在,删除if new_md5 in all_size[size]:print('删除', real_path)# os.remove(real_path)total_delete += 1else:# md5 不存在,进行添加all_size[size].append(new_md5)else:# 如果文件大小不存在,则将此文件大小添加到 all_size 字典中all_size[size] = size_and_md5# 结束时间end = time.time()time_last = end - startprint('文件总数:', total_file)print('删除个数:', total_delete)print('耗时:', time_last, '秒')if __name__ == '__main__':main()

时间效率怎样呢?看下图:

只用了0.5秒!比前两个效率提高了好几倍!

python删除重复文件相关推荐

  1. python删除重复文件代码

    整理磁盘才发现有好多重复文件,便想手写一个删除重复文件的工具.我当然可以用java轻松写一个了,只是最近看了一些python方面的东西,所以想不如拿python练练手.功能很简单,只是为了查阅资料,我 ...

  2. python 删除重复文件

    背景: 由于操作异常造成文件夹下有大量重复生成的文件, 例如 D:\mydata\data1.txt D:\mydata\data1(1).txt D:\mydata\data2.txt D:\myd ...

  3. 利用python删除重复文件和归类文件

    标题利用python实现两个功能: 1 删除重复文件:通过filecmp比较文件内容是否相同进行操作 2 归类文件:通过文件后缀名进行归类 import os import glob import s ...

  4. python 重复图片_删除重复文件或图片(去重)的python代码

    通过python爬虫或其他方式保存的图片文件通常包含一些重复的图片或文件, 通过下面的python代码可以将重复的文件删除以达到去重的目的.其中,文件目录结构如下图: # /usr/bin/env p ...

  5. 【shell】删除重复文件

    2019独角兽企业重金招聘Python工程师标准>>> #!/bin/bash  #name:remove_one.sh #用途:查找并删除重复文件,每个文件只保留一个样本 #将文件 ...

  6. python删除特定文件_python删除特定文件的方法

    python删除特定文件的方法 本文实例讲述了python删除特定文件的方法.分享给大家供大家参考.具体如下: #!/usr/bin/python # -*- coding: utf-8 -*- im ...

  7. 重复文件导出html,分享|删除重复文件的神器:dupeGuru

    最近,我需要清理我父亲的文件和文件夹.有一个难题是,里面存在很多不正确的名字的重复文件.有移动硬盘的备份,同时还为同一个文件编辑了多个版本,甚至改变的目录结构,同一个文件被复制了好几次,名字改变,位置 ...

  8. linux删除相同的文件,Linux下删除重复文件的神器:dupeGuru

    最近,我需要清理我父亲的文件和文件夹.有一个难题是,里面存在很多不正确的名字的重复文件.有移动硬盘的备份,同时还为同一个文件编辑了多个版本,甚至改变的目录结构,同一个文件被复制了好几次,名字改变,位置 ...

  9. python删除指定文件夹下文件和文件夹的方法

    python删除指定文件夹下的文件,是一个常用的功能.我找了不少地方,一直没有找到合适的模版,那只好自己倒腾一个比较实用的模版了. 基本模块 这里面会用到几个模块,一个是目录下所有文件的的函数:lis ...

  10. python保存代码需要删除头部信息吗_用python删除java文件头上版权信息的方法

    在使用他人代码时,为不保留文件头部版权信息,需要一个个删掉,费时费力, 写了个脚本,简单清除掉目录下所有的文件的头部版权信息.# -*- coding: utf8 -*- ''''' 删除java文件 ...

最新文章

  1. mysql创建数据库并且指定utf-8编码
  2. 机器学习(part2)--线性方程组的列表达
  3. numpy基础(part14)--积分
  4. 多个 VUE 前端工程部署设置、nginx 代理配置
  5. android性能测试工具之dumpsys
  6. Linux安装中文字体_宋体
  7. php的socket,PHP Socket范例
  8. Linux桌面版横评:一、评测背景
  9. 反射(一)动态加载类
  10. IIS5 IIS6 IIS7区别
  11. Tables[0].Rows.count是什么意思
  12. mac常见问题(五) Mac 无法开机
  13. P4017 最大食物链计数
  14. 输出由1、2、3、4四个数字组成的每位都不相同的所有三位数
  15. 使用STM32CubeMX新建小熊派的STM32L431RCT6工程实现LED灯闪烁
  16. 多重假设检验与Bonferroni校正、FDR校正
  17. PTA乙级 1069 微博转发抽奖——20分
  18. AD18使用的简单介绍,添加库,添加PCB封装库,快速添加引脚,绘制器件
  19. 哔哩哔哩自动生成视频上传,B站发布软件使用教程
  20. skimage库的安装

热门文章

  1. python微分几何_十一年磨一剑:中科大数学教授成功证明微分几何学两大猜想
  2. 微分几何笔记(1)——参数曲线、内积、外积
  3. 美国加州计算机专业大学排名,USNews美国计算机专业大学排名
  4. 最后一个道士 第二章
  5. 正弦定理和余弦定理_高中数学,正弦定理和余弦定理的应用举例,含高频考点及详细解析...
  6. CCNet:Criss-Cross Attention for semantic Segmentation
  7. linux系统忘记密码之破解密码
  8. 适量消极情绪有助于提高工作能力
  9. Unity3D 图集分割
  10. 微软招 HR 了!看来是招聘需求太旺盛,忙不过来了