본문 바로가기

개발/파이썬

[파이썬] 폴더 크기 구하기

파이썬으로 이것저것 만들고 공부 중입니다 ^^

얼마 전에 C 드라이브 하드디스크 공간이 없길래, 좀 더 자세히 알고 싶기도 하고 공부 삼아 만들어 봤습니다 (이미 폴더 크기 구하는 프로그램들이 많음에도 ~)


프로그램은 간단합니다. 

실행하면 폴더(하위 포함) 크기를 구하고 싶은 드라이브나 폴더를 입력하고 Run 해 주면 됩니다.



결과는 엑셀 파일로 만드는데요 

그림처럼 폴더와 파일 2개의 sheet 를 만들고 각 sheet 에 폴더 크기와 파일 크기 역순으로 10,000 개씩 기록 합니다 (너무 많으면 엑셀 파일에 쓰다 죽기도 해서 만개로 제한)

프로그램 코드는 아래와 같습니다 

import operator
import sys
import os
from PyQt5.QtWidgets import *
 
import pandas as pd
 
gExcelFile = 'foldersize.xlsx'
 
 
class CFolderSize:
    def getFolderSize(self, path, caller, parentPath):
        if path not in caller.dicFolder:
            caller.dicFolder[path] = {'size': 0, 'folders': 0, 'files': 0, 'error': False}
        if (parentPath != ''):
            if parentPath not in caller.dicFolder:
                caller.dicFolder[parentPath] = {'size': 0, 'folders': 0, 'files': 0, 'error': False}
 
        try:
            for fname in os.listdir(path):
                fullname = os.path.join(path, fname)
 
                if os.path.isdir(fullname):
                    caller.dicFolder[path]['folders'] += 1
                    if False == self.getFolderSize(fullname, caller, path):
                        # if parentPath != '@root@':
                        #     caller.dicFolder[parentPath]['error'] = True
                        continue
 
                if os.path.isfile(fullname):
                    caller.dicFolder[path]['files'] += 1
                    size = os.path.getsize(fullname)
                    caller.dicFolder[path]['size'] += size
                    caller.dicFile[fullname] = size
        except:
            print('exception 발생', path, parentPath)
            caller.dicFolder[path]['error'] = True
            return False
 
        if (parentPath != '@root@'):
            caller.dicFolder[parentPath]['size'] += caller.dicFolder[path]['size']
        print(path, 'folder size=', caller.dicFolder[path]['size'],
              'folders#= ', caller.dicFolder[path]['folders'],
              'files#= ', caller.dicFolder[path]['files'])
        return True
 
    def convertSizeToString(self, size):
        if size == 0:
            return ''
        gb, rest = divmod(size, 1000000000)
        if (gb > 0):
            return '%s GB (%s)' % (format(gb, ','), format(size, ','))
        mb, rest = divmod(rest, 1000000)
        if (mb > 0):
            return '%s MB (%s)' % (format(mb, ','), format(size, ','))
 
        kb, rest = divmod(rest, 1000000)
        if (kb > 0):
            return '%s KB (%s)' % (format(kb, ','), format(size, ','))
 
        return '%s' % (format(size, ','))
 
 
class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
 
        self.dicFolder = {}
        self.dicFile = {}
 
        self.dicSortedFolder = []
        self.dicSortedFile = []
 
        # 윈도우 버튼 배치
        self.setWindowTitle("Folder Size")
        nH = 20
 
        self.pathEdit = QLineEdit("", self)
        self.pathEdit.move(20, nH)
        self.pathEdit.resize(300, 30)
        #        self.pathEdit.textChanged.connect(self.pathEditChanged)
        self.pathEdit.setText('00660')
        self.label = QLabel('폴더', self)
        self.label.move(340, nH)
        nH += 50
 
        btnRun = QPushButton("Run", self)
        btnRun.move(20, nH)
        btnRun.clicked.connect(self.btnRun_clicked)
        nH += 50
 
        self.setGeometry(300, 300, 400, nH)
        self.pathEdit.setText('c:\\')
 
    def btnRun_clicked(self):
        findPath = self.pathEdit.text()
        if findPath[-1] != "\\":
            findPath += "\\"
 
        if not os.path.isdir(findPath):
            print('존재하지 않는 폴더입니다.', findPath)
            return
 
        objFolder = CFolderSize()
        self.dicFolder = {}
        self.dicFile = {}
        self.dicSortedFolder = []
        self.dicSortedFile = []
 
        objFolder.getFolderSize(findPath, self, '@root@')
 
        # print('-----------------------------------------')
        # max = 20
        # if max > len(self.dicFolder) :
        #     max = len(self.dicFolder)
        # print('TOP 20 BIG SIZE FOLDERS')
        self.dicSortedFolder = sorted(self.dicFolder.items(), key=lambda x: x[1]['size'], reverse=True)
        # for i in range(0, max) :
        #     print(self.dicSortedFolder[i][0], objFolder.convertSizeToString(self.dicSortedFolder[i][1]['size']))
 
        # print('-----------------------------------------')
        # print('TOP 20 BIG SIZE FILES')
        # max = 20
        # print(self.dicFile)
        # if max > len(self.dicFile) :
        #     max = len(self.dicFile)
        self.dicSortedFile = sorted(self.dicFile.items(), key=operator.itemgetter(1), reverse=True)
        # for i in range(0, max) :
        #     print(self.dicSortedFile[i][0], objFolder.convertSizeToString(self.dicSortedFile[i][1]))
 
        print('엑셀 내보내기 시작')
        self.exportExcel(self.dicSortedFolder, self.dicSortedFile)
 
    def exportExcel(self, folderlist, filelist):
        i = 0
        writer = pd.ExcelWriter(gExcelFile, engine='xlsxwriter')
        df = pd.DataFrame(columns=['path', 'size', '폴더수', '파일수'])
        for folder in folderlist:
            i += 1
            if (i > 10000) :
                print('폴더리스트 저장은 10000개만 저장, 전체 폴더 개수 %d' %(len(folderlist)))
                break
            item = {}
            item['path'] = folder[0]
            item['size'] = folder[1]['size']
            item['폴더수'] = folder[1]['folders']
            item['파일수'] = folder[1]['files']
            df.loc[len(df)] = item
 
        # Convert the dataframe to an XlsxWriter Excel object.
        df.to_excel(writer, sheet_name='folders')
 
        i = 0
        df2 = pd.DataFrame(columns=['filename', 'size', 'fullpath'])
        for file in filelist:
            i += 1
            if (i > 10000) :
                print('파일리스트 저장은 10000 개만 저장, 전체 파일 수 %d' %(len(filelist)))
                break
 
            item = {}
            path = file[0]
            item['filename'] = os.path.basename(path)
            item['size'] = file[1]
            item['fullpath'] = path
            df2.loc[len(df2)] = item
        df2.to_excel(writer, sheet_name='files')
 
        # Close the Pandas Excel writer and output the Excel file.
        writer.save()
        os.startfile(gExcelFile)
        return
 
 
if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWindow = MyWindow()
    myWindow.show()
    app.exec_()




♨ 예전에 프로그램 공부 할 때 재귀 함수의 단점을 열심히 들었던 거 같은데, 위 코드처럼 아름답게(?) 하위 폴더를 탐색하는 코드에서는 재귀 함수가 멋지게 보이네요. 단점은 내 알바 아니고 ^^

♨ 폴더 접근 시 오류가 나는 폴더들이 있습니다. 요건  예외 처리하도록 되어 있습니다. 특히 최근 OS 에서는 탐색기에서 조차 제대로 진입 못하는 폴더가 많네요. 그래서 프리웨어로 유명한 폴더 구하기 프로그램도 에러를 내 뱉는 경우가 많습니다(제대로 업데이트가 안되다 보니)

♨  파이썬으로 엑셀 다루기는 참 쉽습니다. 이 예제에서는 SHEET 를 여러개 쓰는 코드가 포함되어 있으니 참고 하세요 ~

♨ 하드디스크에 폴더 수나 파일 수는 예상보다 훨씬 많습니다. 요걸 모두 딕셔너리에 담고, 소팅도 하고 엑셀에도 씁니다. 파이썬이 올매나 하드한 작업을 견뎌내는 지 고문용(?) 으로 좋네요