И снова здравствуй!
Вторая статья из цикла Black Python поведует тебе о написании модуля для дампа списков файлов, фильтрации файлов по формату, а также упаковке отфильтрованных файлов в архив.
Применение:
Может использоваться для сбора всех файлов jpg, txt и любых других в одном архиве. Путь откуда начинать собирать данные, указывается в конфигах (абсолютный, от текущего вверх на n уровней, все разделы). Упаковка в архив не обязательна, Проведя разведку и узнав какие файлы хранятся на компьютере жертвы, сколько места они занимают, когда какие файлы изменялись, можно вооружившись этими сведениями собрать интересующие материалы.
Tree Module
Функционал по сбору информации с файловой системы:
Дополнительный функционал:
Рубрика разберем на пальцах
Механизм основной функции модуля - сборщика имен файлов (на примере папки 'Desktop'):
В результате выполнения функции вернется список со всеми файлами (в том числе вложенными) с рабочего стола.
Для легкой работы со списком внесем его в структуру {'filename', 'size', 'type', 'date_modified'}:
Преобразуем функцию, под новую структуру данных, и добавление аргумента для каталога
Для сохранения списка, нужно добавить несколько функций:
Для преобразования размера и даты изменения в читаемый вид, в класс FileDetail добавим:
Выполнив программу, получим структуру в читаемом виде:
Отфильтровать список по типу файлов очень просто:
Добавив в main() инструкции по фильтрации и его сохранении, получится:
Получить количество файлов каждого типа, можно функцией:
Добавление файлов в zip-архив:
В main() добавим инструкции:
В итоге все найденные рекурсивным поиском файлы нужного формата будут упакованы в один архив с сохранением вложенности.
Скелет программы описан, далее разберем полноценный модуль:
Структура проекта:
tree.py - основной модуль
additional.py - подключение дополнительных функций: make_zip, make_folders, get_info
Список requirements для всего проекта содержит только:
psutil==5.4.8 - Для получения списка всех разделов диска в функции get_info
start_path - директория с которой нужно начать сбор данных.
Представляет из себя кортеж (Папка, кол-во переходов), где
папка:
'cur' - текущая папка (где запустился код)
'any\exist\path\' - любой путь(существующий)
'all' - корни всех разделов диска
кол-во переходов - кол-во переходов "вверх" от выбранной папки(кроме all)
filter - список типов для фильтрации
load_tree_filename - путь к файлу, откуда нужно загрузить дерево, структура файла такая же как у "filename" и "filter_filename" - список FileDetail.
filename - путь для сохранения дерева обработанных файлов
filter_filename - путь для сохранения дерева отфильтрованных файлов
filter_zip_filename - путь для сохранения архива с фильтрованными файлами
types_filename - путь для сохранения файла с информацией по типам
info_filename - путь для сохранения файла с общей информацией
errors_log - путь для сохранения логов ошибок
Результат:
Результат:
Результат:
Как видно в последних двух примерах, программы отфильтровали одни и те же файлы(ожидаемый и правильный результат).
Таким образом, можно запустить скрипт по всем директориям, дальше вручную отсеять файл tree/filter_tree и запустив программу по нему упаковывать в zip нужные файлы.
Возможные пути масштабирования:
Все исходники будут приложены ниже, а для эникейщиков прикрепляю исполняемый файл + конфиг. Также в файле result.txt содержится дерево отфильтрованных файлов (диск С, jpg)
Вторая статья из цикла Black Python поведует тебе о написании модуля для дампа списков файлов, фильтрации файлов по формату, а также упаковке отфильтрованных файлов в архив.
Применение:
Может использоваться для сбора всех файлов jpg, txt и любых других в одном архиве. Путь откуда начинать собирать данные, указывается в конфигах (абсолютный, от текущего вверх на n уровней, все разделы). Упаковка в архив не обязательна, Проведя разведку и узнав какие файлы хранятся на компьютере жертвы, сколько места они занимают, когда какие файлы изменялись, можно вооружившись этими сведениями собрать интересующие материалы.
Tree Module
Функционал по сбору информации с файловой системы:
- Дамп списка файлов начиная с заданного пути (json),
- Дамп списка файлов выбранных форматов (json),
- Дамп отфильтрованных по типу файлов (zip)
- Информация о типах обработанных файлов(json)
- Короткая сводка о результатах выполнения программы (txt)
Дополнительный функционал:
- возможность загрузки списка файлов из json
- гибкая настройка конфигурации
{
"filename": "C:\\Users\\passwords.txt",
"size": "1.13 KB",
"file_type": "txt",
"date_modified": "2018-12-04 13:18:40"
}
"filename": "C:\\Users\\passwords.txt",
"size": "1.13 KB",
"file_type": "txt",
"date_modified": "2018-12-04 13:18:40"
}
{'txt': 10, 'json': 4, 'exe': 2}
[C:\Users\user01\Desktop\vlmi] - Начальный путь
['txt'] - список форматов filter
[tree] - Блок всех обработанных файлов
Count: 928 - количество
Size: 27.25 MB - общий размер
[filter_tree] - Блок отфильтрованных файлов
Count: 10
Size: 7.06 KB
[types] - Количество типов
Count: 17
Size: None
['txt'] - список форматов filter
[tree] - Блок всех обработанных файлов
Count: 928 - количество
Size: 27.25 MB - общий размер
[filter_tree] - Блок отфильтрованных файлов
Count: 10
Size: 7.06 KB
[types] - Количество типов
Count: 17
Size: None
Рубрика разберем на пальцах
Механизм основной функции модуля - сборщика имен файлов (на примере папки 'Desktop'):
Python:
def get_file_list():
file_list = []
# Получаем путь к папке рабочего стола
catalog = os.path.join(os.path.expanduser('~'), 'Desktop')
# Проходим по всем папкам с помощью os.walk() начиная с catalog
for root, dirs, files in os.walk(catalog):
# Добавляем все файлы из текущей папки в список
file_list += [os.path.join(root, file) for file in files]
return file_list
В результате выполнения функции вернется список со всеми файлами (в том числе вложенными) с рабочего стола.
Для легкой работы со списком внесем его в структуру {'filename', 'size', 'type', 'date_modified'}:
Python:
from pathlib import Path
class FileDetail:
def __init__(self, filename):
# size и date_modified получим из статискики файла (os.stat())
stat_file = os.stat(filename)
self.filename = filename
self.size = stat_file.st_size
self.type = self.get_type(filename)
self.date_modified = int(stat_file.st_mtime)
@staticmethod
def get_type(filename):
return Path(filename).suffix[1:].lower()
Преобразуем функцию, под новую структуру данных, и добавление аргумента для каталога
Python:
def get_file_list(catalog):
file_list = []
for root, dirs, files in os.walk(catalog):
file_list += [FileDetail(os.path.join(root, file)) for file in files]
return file_list
def main():
catalog = os.path.join(os.path.expanduser('~'), 'Desktop')
file_list = get_file_list(catalog)
Для сохранения списка, нужно добавить несколько функций:
Python:
class FileDetail:
def get_info(self):
return {'filename': self.filename, 'size': self.size, 'file_type': self.type,
'date_modified': self.date_modified}
import json
def save_tree(file_list, filename):
transform_list = [file.get_info() for file in file_list]
with open(filename, 'w') as tree_file:
json.dump(transform_list, tree_file)
def main():
catalog = os.path.join(os.path.expanduser('~'), 'Desktop')
file_list = get_file_list(catalog)
save_tree(file_list, 'res.json')
[{
"filename": "C:\\Users\\user01\\Desktop\\Atom.lnk",
"size": 2135,
"file_type": "lnk",
"date_modified": 1543567491
}, ...]
"filename": "C:\\Users\\user01\\Desktop\\Atom.lnk",
"size": 2135,
"file_type": "lnk",
"date_modified": 1543567491
}, ...]
Для преобразования размера и даты изменения в читаемый вид, в класс FileDetail добавим:
Python:
from datetime import datetime
class FileDetail:
@staticmethod
def user_friendly_size(size):
suffix_set = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
level = 0
while size > 1024:
level += 1
size = size / 1024
suffix = suffix_set[level]
if level != 0: size = f'{size:.2f}'
return f'{size} {suffix}'
# Внесем в функцию изменения
def get_info(self):
return {'filename': self.filename, 'size': self.user_friendly_size(self.size), 'file_type': self.type,
'date_modified': str(datetime.fromtimestamp(self.date_modified))}
Выполнив программу, получим структуру в читаемом виде:
[{
"filename": "C:\\Users\\user01\\Desktop\\Atom.lnk",
"size": "2.08 KB",
"file_type": "lnk",
"date_modified": "2018-11-30 11:44:51"
},...]
"filename": "C:\\Users\\user01\\Desktop\\Atom.lnk",
"size": "2.08 KB",
"file_type": "lnk",
"date_modified": "2018-11-30 11:44:51"
},...]
Отфильтровать список по типу файлов очень просто:
Python:
def get_filter_list(file_list, filter_types):
filter_list = []
for file in file_list:
if file.type in filter_types:
filter_list.append(file)
return filter_list
Добавив в main() инструкции по фильтрации и его сохранении, получится:
Python:
def main():
catalog = os.path.join(os.path.expanduser('~'), 'Desktop')
file_list = get_file_list(catalog)
save_tree(file_list, 'res.json')
# обновление
types = ['txt']
filter_list = get_filter_list(file_list, types)
save_tree(filter_list, 'res_filter.json')
Получить количество файлов каждого типа, можно функцией:
Python:
def find_types(file_list):
type_list = {}
for file in file_list:
type_list[file.type] = 1 if file.type not in type_list else type_list[file.type] + 1
return type_list
{'txt': 10, 'json': 4, 'exe': 2}
Добавление файлов в zip-архив:
Python:
import zipfile
def make_zip(files, filename):
with zipfile.ZipFile(filename, 'w') as myzip:
for file in files:
myzip.write(file)
В main() добавим инструкции:
Python:
zip_files = [file.filename for file in filter_list]
make_zip(zip_files, 'res.zip')
В итоге все найденные рекурсивным поиском файлы нужного формата будут упакованы в один архив с сохранением вложенности.
Python:
import json
import os
import zipfile
from datetime import datetime
from pathlib import Path
class FileDetail:
def __init__(self, filename):
stat_file = os.stat(filename)
self.filename = filename
self.size = stat_file.st_size
self.type = self.get_type(filename)
self.date_modified = int(stat_file.st_mtime)
def get_info(self):
return {'filename': self.filename, 'size': self.user_friendly_size(self.size), 'file_type': self.type,
'date_modified': str(datetime.fromtimestamp(self.date_modified))}
@staticmethod
def get_type(filename):
return Path(filename).suffix[1:].lower()
@staticmethod
def user_friendly_size(size):
suffix_set = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
level = 0
while size > 1024:
level += 1
size = size / 1024
suffix = suffix_set[level]
if level != 0: size = f'{size:.2f}'
return f'{size} {suffix}'
def get_file_list(catalog):
file_list = []
for root, dirs, files in os.walk(catalog):
file_list += [FileDetail(os.path.join(root, file)) for file in files]
return file_list
def save_tree(file_list, filename):
transform_list = [file.get_info() for file in file_list]
with open(filename, 'w') as tree_file:
json.dump(transform_list, tree_file)
def get_filter_list(file_list, filter_types):
filter_list = []
for file in file_list:
if file.type in filter_types:
filter_list.append(file)
return filter_list
def find_types(file_list):
type_list = {}
for file in file_list:
type_list[file.type] = 1 if file.type not in type_list else type_list[file.type] + 1
return type_list
def make_zip(files, filename):
with zipfile.ZipFile(filename, 'w') as myzip:
for file in files:
myzip.write(file)
def main():
catalog = os.path.join(os.path.expanduser('~'), 'Desktop')
file_list = get_file_list(catalog)
save_tree(file_list, 'res.json')
types = ['txt']
filter_list = get_filter_list(file_list, types)
save_tree(filter_list, 'res_filter.json')
print(find_types(file_list))
zip_files = [file.filename for file in filter_list]
make_zip(zip_files, 'res.zip')
if __name__ == '__main__':
main()
Скелет программы описан, далее разберем полноценный модуль:
Структура проекта:
tree.py - основной модуль
TreeSizeCounter - Класс счетчика размера дерева
FileDetail - Класс структуры файла
TreeExplorer - Основной механизм по сбору данных, основан на предоставленном выше "скелете"
TreeManager - Класс описывающий связь основных механизмов и внешних настроек (json)
error_log.py - Класс для ведения логов ошибок.additional.py - подключение дополнительных функций: make_zip, make_folders, get_info
Список requirements для всего проекта содержит только:
psutil==5.4.8 - Для получения списка всех разделов диска в функции get_info
JSON:
{
"start_path": ('cur', 1),
"filter": ['jpg', 'png'],
"load_tree_filename": "",
"filename": "result\\tree.json",
"filter_filename": "result\\filter_tree.json",
"filter_zip_filename": "result\\filter.zip",
"types_filename": "result\\type.txt",
"info_filename": "result\\info.txt",
"errors_log": "result\\tree_errors.log"
}
Представляет из себя кортеж (Папка, кол-во переходов), где
папка:
'cur' - текущая папка (где запустился код)
'any\exist\path\' - любой путь(существующий)
'all' - корни всех разделов диска
кол-во переходов - кол-во переходов "вверх" от выбранной папки(кроме all)
filter - список типов для фильтрации
load_tree_filename - путь к файлу, откуда нужно загрузить дерево, структура файла такая же как у "filename" и "filter_filename" - список FileDetail.
filename - путь для сохранения дерева обработанных файлов
filter_filename - путь для сохранения дерева отфильтрованных файлов
filter_zip_filename - путь для сохранения архива с фильтрованными файлами
types_filename - путь для сохранения файла с информацией по типам
info_filename - путь для сохранения файла с общей информацией
errors_log - путь для сохранения логов ошибок
Код:
import os
import zipfile
# Функция для получения информации о разделах диска
from psutil import disk_partitions
# Создание архива (упаковка списка files) в filename
def make_zip(files, filename):
# Создание папки для результата (если надо)
make_folders(filename)
# Открываем с помощью модуля zipfile файл filename
with zipfile.ZipFile(filename, 'w') as myzip:
for file in files:
try:
# По одному файлу добавляем в архив
myzip.write(file)
except:
# Если ошибка, то игнорируем
pass
# Создание папки path
def make_folders(path):
try:
# Получаем путь к нужной папке
folder = os.path.dirname(path)
# Если ее нет, то создаем
if folder and not os.path.exists(folder):
os.makedirs(folder)
except Exception as e:
raise FileNotFoundError(f'Folder not create! {e}')
# функция получения info
def get_info(mode):
# Если режим получения инфы о дисках
if mode == 'get_partitions':
# то возвращаем необработанный результат выполнения функции
return disk_partitions()
Код:
from additional import make_folders
# класс для логгирования ошибок
class ErrorLog:
def __init__(self):
self.errors = []
# добавление ошибки (просто как текст)
def add(self, module, error):
self.errors.append(f'{module} - {error}')
# Вывод списка
def error_list(self):
return self.errors
# Сохранения списка ошибок в файл (если есть ошибки и указано имя файла)
def save_log(self, filename='errors.log'):
make_folders(filename)
if self.errors and filename:
with open(filename, 'w') as error_log_file:
for error in self.errors:
error_log_file.write(f'{error}\n')
Код:
import json
import os
from datetime import datetime
from pathlib import Path
# Импорт дополнительных функций и классов
from additional import make_folders, make_zip, get_info
from error_log import ErrorLog
# Счетчик количества и размера списка файлов
class TreeSizeCounter:
def __init__(self):
self.count = 0
self.size = 0
# При добавлении файла увеличивается счетчик
# количества и общий размер увеличивается на size
def add_file(self, size):
self.count += 1
self.size += float(size)
# Возврат кортежа с количеством файлов и преобразованным размером
def get_info(self):
result = (self.count, FileDetail.user_friendly_size(self.size))
return result
class FileDetail:
# Именования уровней размера файлов (мб, гб)
suffix_set = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
def __init__(self, filename=None, load_data=None):
# Если не загрузочная информация, то
if not load_data:
# Получаем статистику файла и записываем по нужным полям
stat_file = os.stat(filename)
self.filename = filename
self.size = stat_file.st_size
self.type = self.get_type(filename)
self.date_modified = int(stat_file.st_mtime)
else:
# Иначе загружаем данные
self.load(load_data)
# Возврат читаемого вида структуры файла
def get_info(self):
return {'filename': self.filename, 'size': self.user_friendly_size(self.size), 'file_type': self.type,
'date_modified': str(datetime.fromtimestamp(self.date_modified))}
# Загрузка структуры файла из словаря (с обратным преобразованием размера и времени)
def load(self, dict_info):
self.filename = dict_info['filename']
self.size = self.machine_size(dict_info['size'])
self.type = dict_info['file_type']
self.date_modified = datetime.timestamp(datetime.fromisoformat(dict_info['date_modified']))
@staticmethod
# Получение читаемого вида размера
def user_friendly_size(size):
level = 0
# Пока размер сокращается, увеличиваем уровень
while size > 1024:
level += 1
size = size / 1024
# Получаем буквы размера в списке по уровню
suffix = FileDetail.suffix_set[level]
# Если не байты, то округляем до 2х знаков (байты уже целые)
if level != 0: size = f'{size:.2f}'
return f'{size} {suffix}'
@staticmethod
# Операция преобразования к байтовому представлению размера
def machine_size(size):
for suffix in FileDetail.suffix_set:
# Для каждого суффикса ищем совпадение с текущим размером
if suffix in size:
# Берем число и умножаем его на 1024 в степени уровня суффикса
return int(float(size.split(suffix)[0].strip()) * pow(1024, FileDetail.suffix_set.index(suffix)))
return None
@staticmethod
# Получение типа(расширения) файла в нижнем регистре
def get_type(filename):
return Path(filename).suffix[1:].lower()
class TreeExplorer:
def __init__(self, start_path, file_types, load_tree_filename=None):
# Получаем путь из start_path
self.start_path = os.path.abspath(start_path)
# Для избежания разногласий между 'jpg' и 'JPG', приводим типы к младшему регистру
self.filter = [file_type.lower() for file_type in file_types]
self.tree = []
self.filter_tree = []
self.types = []
self.info = {}
if load_tree_filename and os.path.exists(load_tree_filename):
# Если пришел параметр load_tree_filename, то загружаем список файлов
self.load_tree(load_tree_filename)
else:
# иначе получаем его
self.make_tree(self.start_path)
# Загрузка дерева файлов
def load_tree(self, filename_tree):
# Открываем файл и загружаем структуру
with open(filename_tree, 'r') as file_tree:
tree = json.load(file_tree)
# Проходимся по списку файлов
for file in tree:
try:
# Пробуем добавить в список
self.tree.append(FileDetail(load_data=file))
except:
# Если не получится, игнорировать
pass
# Создание списка файлов
def make_tree(self, catalog):
# Инициализируем счетчик
counter = TreeSizeCounter()
# Проходим по всем директориям и файлам начиная с catalog
for root, dirs, files in os.walk(catalog):
for file in files:
try:
# Пробуем добавить файл и учитываем его в счетчике
new_file = FileDetail(os.path.join(root, file))
self.tree.append(new_file)
counter.add_file(new_file.size)
except:
# избегаем вылета при ошибке
pass
# Записываем данные со счетчика
self.info['tree'] = counter.get_info()
# Механизм создания фильтр-списка(если он не пуст) и таблицы типов данных
def fill_engine(self):
if self.filter: self.make_filter_tree(self.tree, self.filter)
self.types = self.find_types(self.tree)
# Создание списка отфильтрованных файлов
def make_filter_tree(self, tree, file_types):
# Подрубаем счетчик
counter = TreeSizeCounter()
for file in tree:
try:
# Пробуем добавить по файлу, для избежания вылета
if file.type in file_types:
self.filter_tree.append(file)
counter.add_file(file.size)
except:
pass
self.info['filter_tree'] = counter.get_info()
# Поиск типов
def find_types(self, tree):
type_list = {}
for file in tree:
try:
# Проходя по каждому файлу из списка добавляем 1 к уже существующему значению, или если его нет, то =1
type_list[file.type] = 1 if file.type not in type_list else type_list[file.type] + 1
except:
pass
self.info['types'] = (len(type_list), None)
return type_list
@staticmethod
# Дамп списка файлов в filename (json)
def save_tree(file_list, filename):
if file_list:
transform_list = [file.get_info() for file in file_list]
with open(filename, 'w') as tree_file:
json.dump(transform_list, tree_file)
# Сохранение таблицы типов или информации о результате работы
def save_info(self, mode, filename):
if mode == 'type':
text = str(self.types)
elif mode == 'info':
info = [f'[{info}]\nCount: {self.info[info][0]}\nSize: {self.info[info][1]}' for info in self.info]
info = "\n\n".join(info).strip()
text = f'[{self.start_path}]\n{self.filter}\n\n{info}'
else:
text = ''
if mode in ['type', 'info']:
with open(filename, 'w') as info_file:
info_file.write(text)
class TreeManager:
def __init__(self, config):
# Подрубаем конфиги и логи ошибок
self.tree_config = config
self.error_log = ErrorLog()
self.tree_engine()
# Подготовка входных данных конфигурации
def tree_engine(self):
try:
# Заменяем start_path на читаемый системой
self.tree_config['start_path'] = self.define_tree_path(self.tree_config['start_path'])
# Если выбран режим all, при этом файл load_tree_filename не указан
if self.tree_config['start_path'] == 'all' and not self.tree_config['load_tree_filename']:
# То прогоняем функцию tree_routine для каждого раздела диска
for part in get_info('get_partitions'):
self.tree_routine(self.add_part_to_path(self.tree_config.copy(), part))
else:
# Иначе запускаем один раз с нужными параметрами
self.tree_routine(self.tree_config)
except Exception as e:
# Если что-то не так, то логгируем в список ошибок
self.error_log.add('TreeManager - tree_engine', e)
# Функция замены пути на читаемый системой
def define_tree_path(self, raw_path):
try:
# Если ('cur', что-то еще), то присваиваем текущую директорию, иначе присваиваем первый параметр.
path = os.path.dirname(os.path.abspath(__file__)) if raw_path[0] == 'cur' else raw_path[0]
# Если путь не all,
if path != 'all':
for i in range(raw_path[1]):
# То осуществляем переход наверх столько раз, сколько указано во втором параметре.
path = os.path.dirname(path)
# Возвращаем полученный путь
return path
except Exception as e:
self.error_log.add('TreeManager - define_tree_path', e)
# Механизм настройки имен выходных файлов
def add_part_to_path(self, tree, part):
try:
# Начальный путь - буква диска - корень раздела C:\\ или D:\\
tree['start_path'] = part.mountpoint
# Для каждого указанного(непустого) выходного файла
for filename in ["filename", "filter_filename", "filter_zip_filename", "types_filename", "info_filename"]:
if tree[filename] != '':
# Получаем папку указанного пути
path_to_file = os.path.dirname(tree[filename])
# Убираем все межпапочные символы от диска (остается С или D)
new_folder = part.device.replace(os.sep, '').replace(':', '')
# Получаем окончание имени файла
end_filename = tree[filename].replace(path_to_file, '').replace(os.sep, '')
# Соединяем все
tree[filename] = os.path.join(path_to_file, new_folder, end_filename)
# Возвращаем полученную структуру
return tree
except Exception as e:
self.error_log.add('TreeManager - add_part_to_path', e)
# Запуск TreeExplorer с полученными конфигами
def tree_routine(self, config):
try:
# Создаем экземпляр класса с параметрами начального пути, типов файлов, и загрузочным деревом файлов
tree = TreeExplorer(config['start_path'], config['filter'], config['load_tree_filename'])
# Заполняем фильтр и типы
tree.fill_engine()
# Словарь для связи между названием путей и параметрами для вывода информации
short_link = {'filename': tree.tree, 'filter_filename': tree.filter_tree,
'types_filename': 'type', 'info_filename': 'info'}
# Для каждого непустого пути
for filename in ["filename", "filter_filename", "types_filename", "info_filename"]:
if config[filename] != '':
# Создаем папку для сохранения (если ее нет)
make_folders(config[filename])
# Если это дерево, то сохраняем
if filename in ['filename', 'filter_filename']:
tree.save_tree(short_link[filename], config[filename])
# Иначе если это типы, инфо, то их сохраняем
elif filename in ['types_filename', 'info_filename']:
tree.save_info(short_link[filename], config[filename])
# Если список отфильтрованных файлов не пуст, то сохраняем его (если файл для вывода задан)
if tree.filter_tree:
if config['filter_zip_filename'] != '':
make_zip([file.filename for file in tree.filter_tree], config['filter_zip_filename'])
except Exception as e:
self.error_log.add('TreeManager - tree_routine', e)
def tree_manager(config):
# Создаем менеджера, кормим ему конфиги, он начинает работать
tm = TreeManager(config)
# Сохраняем логи ошибок, если есть
tm.error_log.save_log(config['errors_log'])
def main():
try:
# Для изменения конфигурации "на лету" настройки будем получать из файла
with open('config.json', 'r') as config_file:
tree_config = json.load(config_file)
# Запуск механизма с загруженными настройками
tree_manager(tree_config)
except Exception as e:
print(e)
if __name__ == '__main__':
main()
Python:
if __name__ == '__main__':
tree_config = {
"start_path": ('all', 1),
"filter": ['txt'],
"load_tree_filename": "",
"filename": "result\\tree.json",
"filter_filename": "result\\filter_tree.json",
"filter_zip_filename": "result\\filter.zip",
"types_filename": "result\\type.json",
"info_filename": "result\\info.txt",
"errors_log": "result\\tree_errors.log"
}
tree_manager(tree_config)
Результат:
[C:\]
['txt']
[tree]
Count: 110822
Size: 21.90 GB
[filter_tree]
Count: 1169
Size: 56.84 MB
[types]
Count: 1738
Size: None
['txt']
[tree]
Count: 110822
Size: 21.90 GB
[filter_tree]
Count: 1169
Size: 56.84 MB
[types]
Count: 1738
Size: None
[D:\]
['txt']
[tree]
Count: 27
Size: 54.93 MB
[filter_tree]
Count: 3
Size: 2.21 KB
[types]
Count: 9
Size: None
['txt']
[tree]
Count: 27
Size: 54.93 MB
[filter_tree]
Count: 3
Size: 2.21 KB
[types]
Count: 9
Size: None
Python:
if __name__ == '__main__':
tree_config = {
"start_path": ('cur', 0),
"filter": ['py'],
"load_tree_filename": "",
"filename": "result\\tree.json",
"filter_filename": "result\\filter_tree.json",
"filter_zip_filename": "", "types_filename": "",
"info_filename": "result\\info.txt",
"errors_log": "result\\tree_errors.log"
}
tree_manager(tree_config)
Результат:
[C:\Users\user01\Desktop\vlmi\tree]
['py']
[tree]
Count: 928
Size: 27.08 MB
[filter_tree]
Count: 396
Size: 5.25 MB
[types]
Count: 18
Size: None
['py']
[tree]
Count: 928
Size: 27.08 MB
[filter_tree]
Count: 396
Size: 5.25 MB
[types]
Count: 18
Size: None
Python:
if __name__ == '__main__':
tree_config = {
"start_path": ('all', 1),
"filter": ['py'],
"load_tree_filename": "tree.json",
"filename": "result\\tree.json",
"filter_filename": "result\\filter_tree.json",
"filter_zip_filename": "result\\filter.zip",
"types_filename": "result\\type.json",
"info_filename": "result\\info.txt",
"errors_log": "result\\tree_errors.log"
}
tree_manager(tree_config)
[C:\Users\user01\Desktop\vlmi\tree\all]
['py']
[filter_tree]
Count: 396
Size: 5.25 MB
[types]
Count: 18
Size: None
['py']
[filter_tree]
Count: 396
Size: 5.25 MB
[types]
Count: 18
Size: None
Таким образом, можно запустить скрипт по всем директориям, дальше вручную отсеять файл tree/filter_tree и запустив программу по нему упаковывать в zip нужные файлы.
Возможные пути масштабирования:
- Подключив модуль по работе с изображениями(возможно, pillow подойдет), упаковывать не целиковые изображения, а сжатые(например, по коэффициенту или по размеру). PROFIT: Запустить программу с конфигом: path = all, filter = ['jpg', 'png'], сжатие = 80% (условно), zip_archive = 'img.zip'. Просмотрев архив zip, выбрать(оставить только их) подходящие изображения в файле filter_tree.json. Повторно запустить программу с конфигурацией: filter ['jpg', 'png'], сжатие = нет, load_tree_filename = filter_tree.json + упаковка. В итоге будет получен архив только с нужными данными. Профит в данном случае в том, что вы не будете терять время(и драгоценные МБ) на упаковку 10к фоток высокого разрешения дачного сада и котиков.
- Больше опций фильтрации: форматы, имена, диапазон дат, размер.
- Разделение результирующего архива на части (по предельному размеру архива) - хотя этот путь не относится напрямую к данному модулю
Все исходники будут приложены ниже, а для эникейщиков прикрепляю исполняемый файл + конфиг. Также в файле result.txt содержится дерево отфильтрованных файлов (диск С, jpg)
Вложения
Последнее редактирование: