사진찍는 프로그래머

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

개발/파이썬

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

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


프로그램은 간단합니다. 

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



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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
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(30030)
        #        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(300300400, 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_()
cs


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

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

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

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