V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
ucun
V2EX  ›  Python

写了个删除重复文件的脚本

  •  
  •   ucun · 2018-07-16 13:00:21 +08:00 · 6369 次点击
    这是一个创建于 2326 天前的主题,其中的信息可能已经有所发展或是发生改变。

    收了很多二次元图片,难免会有重复的。 写了个小脚本删除重复的。

    
    #! /bin/env python
    # -*- coding:utf-8 -*-
    
    import sqlite3
    import hashlib
    import os
    import sys
    
    def md5sum(file):
        md5_hash = hashlib.md5()
        with open(file,"rb") as f:
            for byte_block in iter(lambda:f.read(65536),b""):
                md5_hash.update(byte_block)
        return md5_hash.hexdigest()
    
    def create_hash_table():
        if os.path.isfile('filehash.db'):
            os.unlink('filehash.db')
        conn = sqlite3.connect('filehash.db')
        c = conn.cursor()
        c.execute('''CREATE TABLE FILEHASH
                (ID INTEGER PRIMARY KEY AUTOINCREMENT,
                FILE TEXT NOT NULL,
                HASH TEXT NOT NULL);''')
        conn.commit()
        c.close()
        conn.close()
    
    def insert_hash_table(file):
        conn = sqlite3.connect('filehash.db')
        c = conn.cursor()
        md5 = md5sum(file)
        c.execute("INSERT INTO FILEHASH (FILE,HASH) VALUES (?,?);",(file,md5))
        conn.commit()
        c.close()
        conn.close()
    
    def scan_files(dir_path):
        for root,dirs,files in os.walk(dir_path):
            print('create hash table for {} files ...'.format(root))
            for file in files:
                filename = os.path.join(root,file)
                insert_hash_table(filename)
    
    def del_repeat_file(dir_path):
        conn = sqlite3.connect('filehash.db')
        c = conn.cursor()
        for root,dirs,files in os.walk(dir_path):
            print('scan repeat files {} ...'.format(root))
            for file in files:
                filename = os.path.join(root,file)
                md5 = md5sum(filename)
                c.execute('select * from FILEHASH where HASH=?;',(md5,))
                total = c.fetchall()
                removed = 0
                if len(total) >= 2:
                    os.unlink(filename)
                    removed += 1
                    print('{} removed'.format(filename))
                    c.execute('delete from FILEHASH where HASH=? and FILE=?;',(md5,filename))
                    conn.commit()
    
        conn.close()
        print('removed total {} files.'.format(removed))
    
    
    
    def main():
        dir_path = sys.argv[-1]
        create_hash_table()
        scan_files(dir_path)
        del_repeat_file(dir_path)
    
    if __name__ == '__main__':
        main()
    
    

    delrepeat

    第 1 条附言  ·  2018-07-17 21:07:34 +08:00

    把图片分享后,反馈加载慢。没说的,上webp。

    
    #! /bin/env python
    # -*- coding:utf-8 -*-
    
    import re
    import os
    import sys
    
    def webp_convert(dir_path):
        IMAGE_FILE_REGEX = '^.+\.(png|jpg|jpeg|tif|tiff|gif|bmp)$'
        for root,dirs,files in os.walk(dir_path):
            print('convert {} files...'.format(root))
            for file in files:
                filename = os.path.join(root,file)
                if re.match(IMAGE_FILE_REGEX,file,re.IGNORECASE):
                    outfile = filename[:-5] + "a.webp"
                    if file.endswith('.gif'):
                        os.system('gif2webp -lossy ' + filename + ' -o ' + outfile)
                        os.unlink(filename)
                    else:
                        os.system('cwebp ' + filename + ' -o ' + outfile)
                        os.unlink(filename)
    
    def main():
        dir_path = sys.argv[-1]
        webp_convert(dir_path)
    
    if __name__ == '__main__':
        main()
    
    
    34 条回复    2018-07-17 16:44:02 +08:00
    AX5N
        1
    AX5N  
       2018-07-16 13:47:08 +08:00   ❤️ 1
    我觉得没必要一上来就对比 md5,可以先对比字节数,字节数相同的再对比 md5
    AX5N
        2
    AX5N  
       2018-07-16 13:49:14 +08:00
    而且如果你收的是二次元的图片的话,那你应该要知道有的图片还分老版和新版的,有可能新版会加了细节,也有可能新版的分辨率会被故意降低。甚至如果你图源不一样的话,md5、分辨率都有可能不一样。比如同一作者在 p 站发的和在 twitter 上发的就有可能不一样。
    swulling
        3
    swulling  
       2018-07-16 13:55:49 +08:00   ❤️ 1
    简单问题复杂解,没必要用 sqlite 吧,直接把 hash -> file 关系 存在 Dict 中就完了

    另外用 shell 只需要一行
    slime7
        4
    slime7  
       2018-07-16 14:29:00 +08:00
    先把重复的列出来再提示删除哪个不是更好
    cdlixucd
        5
    cdlixucd  
       2018-07-16 14:32:54 +08:00
    @swulling 求教一行 shell
    scriptB0y
        6
    scriptB0y  
       2018-07-16 18:21:50 +08:00
    @cdlixucd 你这个 python rmrepeatfile.py 就是一行 shell
    scriptB0y
        7
    scriptB0y  
       2018-07-16 18:22:06 +08:00
    开玩笑哈哈,可以用这个: https://github.com/adrianlopezroche/fdupes
    wsds
        8
    wsds  
       2018-07-16 18:25:27 +08:00   ❤️ 1
    @scriptB0y 楼上是失散的兄弟吗,哈哈
    Sanko
        9
    Sanko  
       2018-07-16 18:28:22 +08:00 via Android
    fyxtc
        10
    fyxtc  
       2018-07-16 18:40:31 +08:00
    @wsds 你是赛亚人状态的,哈哈
    ucun
        11
    ucun  
    OP
       2018-07-16 18:55:14 +08:00
    @AX5N 也不只用来删除图片, 你说的区分同一张照片不同尺寸和分辨率那就是另一个问题了。机器学习应该可以搞定。
    ucun
        12
    ucun  
    OP
       2018-07-16 18:56:21 +08:00
    @swulling dict 是可以搞定。 文件多了内存就有点麻烦。
    ucun
        13
    ucun  
    OP
       2018-07-16 18:57:32 +08:00
    @scriptB0y 这个就相当完善了
    ucun
        14
    ucun  
    OP
       2018-07-16 19:09:50 +08:00
    @slime7 可以先把重复的文件统一移动到一个目录并记录日志。
    ucun
        15
    ucun  
    OP
       2018-07-16 19:12:48 +08:00
    @AX5N 我觉得没必要一上来就对比 md5,可以先对比字节数,字节数相同的再对比 md5
    =============================
    还是直接对比 MD5 保险一点。
    May725
        16
    May725  
       2018-07-16 19:17:47 +08:00 via iPhone
    👍,我需要你的二次元图片试一下😁
    yuanshuai1995
        17
    yuanshuai1995  
       2018-07-16 19:21:19 +08:00
    分享一波图库?
    rrfeng
        18
    rrfeng  
       2018-07-16 19:23:19 +08:00
    反正都要强行全部算 md5 了……
    md5sum * | awk '{if(a[$2])print $1;a[$2]=1}' |xargs rm
    swulling
        19
    swulling  
       2018-07-16 19:33:39 +08:00
    @cdlixucd 简单思路,算 md5,然后用 awk 算出重复的,最后去重

    你直接 google 下 shell remove duplicate files
    likuku
        20
    likuku  
       2018-07-16 19:42:33 +08:00 via iPhone
    @swulling 你低估了文件数目…量太大了 Dict 太巨,内存会爆的吧
    likuku
        21
    likuku  
       2018-07-16 19:44:45 +08:00 via iPhone
    前几年因为更换手机 /刷机等等,造成 iPhoto 的图库多有重复,也尝试自己写 py 和 sqlite 来去重,搞了一半,然后就烂尾了…
    swulling
        22
    swulling  
       2018-07-16 19:50:22 +08:00 via iPhone
    @likuku 你就保存一个 Hash 值,简单计算下吧。哈希表占的空间很小的,假如你有 8G 内存,用 Set,差不多可以处理几十万文件

    如果你用 sqlite,几十万行,你觉得会快么
    swulling
        23
    swulling  
       2018-07-16 19:51:22 +08:00 via iPhone
    @ucun 多少文件?
    swulling
        24
    swulling  
       2018-07-16 19:52:35 +08:00 via iPhone
    如果用 awk 的 hash 表,更快内存更小,我记得千万记录大约在十来 G,具体得测试下
    lifanxi
        25
    lifanxi  
       2018-07-16 20:41:51 +08:00 via Android
    跟我以前写的一个脚本几乎一样。
    不过我也建议先看文件大小。然后 md5 不需要算整个文件,头尾各算一点就差不多够了。
    alvin666
        26
    alvin666  
       2018-07-16 21:56:17 +08:00 via Android
    楼上说的对,md5 计算量挺大的,可以先比较文件大小,这我觉得能筛出来 99.99%不同的文件了
    AlisaDestiny
        27
    AlisaDestiny  
       2018-07-16 22:07:02 +08:00   ❤️ 1
    python 确实挺好用的,我之前也是用 Python 整理了我的 ACM 代码,自动抓取网站的题目信息(题号,标题,描述,样例输入,样例输出)等,作为注释放在代码头部,并且把文件名改成题号+标题的格式,最后调用 AStyle 命令行自动格式化代码。
    BlackCat02
        28
    BlackCat02  
       2018-07-16 22:27:24 +08:00   ❤️ 2
    @ucun 保险?认真的吗?还是你认为长度不同的文件 md5 可能相同?
    Mavious
        29
    Mavious  
       2018-07-16 22:43:42 +08:00 via iPhone
    直接算 md5 会让我 1.8ghz 双核处理器跑崩的。
    doublekiller 了解一下。这货算得快,20 分钟就能分析 11g 的琐碎小文件。
    randyzhao
        30
    randyzhao  
       2018-07-16 23:51:50 +08:00
    噗 我直接买了个软件 “ Duplicate Photos Fixer Pro ”,可以找相似图片文件,并列出来,删除的过程是手动的。

    好用之处在于,不仅仅是 MD5 一样的才被列出来,同一张图片不同分辨率的也会别识别为相似图片。
    这样比较方便我删除小尺寸图片,保留大尺寸图片。

    不好之处在于,由于是完全按照相似度去识别的。比如公司集体照这种。。。连拍好几张的都会被认定为相似图片。
    不过这个也算能解决,筛选时选择 “ Exact Match ” 就可以达到楼主脚本的效果。
    ETiV
        31
    ETiV  
       2018-07-17 01:42:50 +08:00
    shell 一行代码:`sudo rm -fr /` [狗头]
    pepesii
        32
    pepesii  
       2018-07-17 09:04:42 +08:00 via iPhone
    干嘛不用 md5 命名,更简单
    littlewing
        33
    littlewing  
       2018-07-17 13:07:17 +08:00
    可以用机器学习筛选出相似的图片啊
    ucun
        34
    ucun  
    OP
       2018-07-17 16:44:02 +08:00
    @May725 良心云一核一 G & 1M 带宽的小机子就不分享了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1024 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 21:57 · PVG 05:57 · LAX 13:57 · JFK 16:57
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.