• VLMI - форум по обмену информацией. На форуме можете найти способы заработка, разнообразную информацию по интернет-безопасности, обмен знаниями, курсы/сливы.

    После регистрации будут доступны основные разделы.

    Контент форума создают пользователи, администрация за действия пользователей не несёт ответственности, отказ от ответственности. Так же перед использованием форума необходимо ознакомиться с правилами ресурса. Продолжая использовать ресурс вы соглашаетесь с правилами.
  • Подпишись на наш канал в Telegram для информации о актуальных зеркалах форума: https://t.me/vlmiclub

Авторская статья Black Python - 2. Tree Module

gh0st4ge

Местный
Сообщения
40
Реакции
86
0 руб.
И снова здравствуй!
Вторая статья из цикла Black Python поведует тебе о написании модуля для дампа списков файлов, фильтрации файлов по формату, а также упаковке отфильтрованных файлов в архив.
bp_tree.jpg

Применение:
Может использоваться для сбора всех файлов 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"
}


{'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

Рубрика разберем на пальцах

Механизм основной функции модуля - сборщика имен файлов (на примере папки '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
}, ...]

Для преобразования размера и даты изменения в читаемый вид, в класс 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"
}
,...]

Отфильтровать список по типу файлов очень просто:
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"
}
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 - путь для сохранения логов ошибок

Код:
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)

Результат:
result1.png
[C:\]
['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

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)

Результат:
result2.png
[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

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)
Результат:
result3.png

[C:\Users\user01\Desktop\vlmi\tree\all]
['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)
 

Вложения

  • tree_exe.zip
    5,3 MB · Просмотры: 138
  • tree_source.zip
    7 KB · Просмотры: 131
  • result.txt
    27,9 KB · Просмотры: 323
Последнее редактирование:
Сверху Снизу