Управление ресурсами с помощью временных файлов Python и Zip-файлов

Мне нужно было создать подпрограмму, которая просматривает коллекцию сообщений Outlook, открывает вложения и извлекает любые табличные данные из zip-папки в фрейм данных pandas. Чтобы получить табличные данные, я создал функцию zip_to_dfs, которая принимает вложение Outlook MailItem в качестве аргумента).

#function to extract tabluar data within zip file to pandas dataframe. returns dictionary object(key=filename;value=pandas df)
import pandas as pd, zipfile, tempfile, os

def zip_to_dfs(attachment, extract_fn=None):
    #returns diciontary object with filename for key and dataframes from attached files as values
    df_objects = {}
    tmp=tempfile.TemporaryFile().name
    attachment.SaveAsFile(tmp)
    if zipfile.is_zipfile(tmp)==True:
        zf = zipfile.ZipFile(tmp)
        #below subroutine could be made to separate function (read tablular to df) to make more readable
        for file in zf.infolist():
            extension = os.path.splitext(file.filename)[1]
            if extension in ['.xls','.xlsx','.xlsm']:
                temp_df = pd.read_excel(zf.open(file.filename), header=None)
                df_objects.update({file.filename:temp_df})
            elif file.filename.endswith(".csv"):
                temp_df = pd.read_csv(zf.open(file.filename), header=None)
                df_objects.update({file.filename:temp_df})
            else:
                raise NotImplementedError('Unexpected filetype: '+str(file.filename))
    else: 
        raise NotImplementedError('Expected zip file')
    return(df_objects)

Функция работает так, как задумано, но, вероятно, неэффективно. Кто-нибудь использовал библиотеки tempfile или zip-файлов? Если да, то знаете ли вы, очищаются ли методы Zipfile и TemporaryFile автоматически? Или эти файлы остаются открытыми на диске? Видели ли вы какие-либо другие очевидные проблемы при таком подходе?

Отредактированная версия кода:

def zipattach_to_dfs(attachment, extract_fn=None):
    #evaluates zip file attachments and returns dictionary with file name as key and dataframes as values
    df_objects = {}
    with NamedTemporaryFile(suffix='.tmp', delete=False) as tmp:
        attachment.SaveAsFile(tmp.name)
        zf = ZipFile(tmp)
        for file in zf.infolist():
            datetime = (file.date_time)
            key = (f'{file.filename}({datetime[0]}-{datetime[1]}-{datetime[2]})')
            if isexcel(file) ==True:
                temp_df = pd.read_excel(zf.open(file.filename), header=None)
                df_objects.update({key:temp_df})
            elif file.filename.endswith(".csv"):
                temp_df = pd.read_csv(zf.open(file.filename), header=None)
                df_objects.update({key:temp_df})
            else:
                raise NotImplementedError('Unexpected filetype: '+str(file.filename))
    return (df_objects)

Они очищаются автоматически, если вы используете обработчики контекста (т.е. с... как угодно :)

Dean MacGregor 27.09.2023 00:54

Спасибо за подтверждение. Я отредактировал сообщение, изменив функцию. Надеемся, что обработка zip-файла сделает эту функцию более эффективной с точки зрения ресурсов.

Possdawgers 27.09.2023 01:05

Вам тоже захочется сделать это с tempfile.TemporaryFile(). На самом деле важнее сделать это с временными файлами, поскольку вы хотите их удалить. Я не уверен, каков жизненный цикл временных файлов, которые никогда не закрываются.

Dean MacGregor 27.09.2023 01:50

Спасибо, я отредактировал редакцию с предложенной рекомендацией!

Possdawgers 27.09.2023 22:34
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
4
88
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

ZipFile также поддерживает оператор with. Итак, вот мое предложение, основанное на вашем коде (кодах):

def zip_to_dfs(attachment, extract_fn=None): # extract_fn ?
    '''
    Returns a dictionary object with filename for key
    and dataframes from attached files as values.
    '''
    with NamedTemporaryFile(delete=False) as tmp:
        attachment.SaveAsFile(tmp.name)
        
        if is_zipfile(tmp):
            with ZipFile(tmp) as zf:
                for file in zf.infolist():
                    fn, exte = file.filename.rsplit(".", 1)
                    key = (f'{fn} ({"-".join(map(str, file.date_time[:3]))})')
                    
                    if exte in {'xls', 'xlsx', 'xlsm', 'csv'}:
                        df_objects = {}
                        with zf.open(file) as zip_file:
                            if exte == 'csv':
                                df = pd.read_csv(zip_file, header=None)
                            else:
                                df = pd.read_excel(zip_file, header=None)
                            df_objects[key] = df
                    else:
                        raise NotImplementedError(
                            'Unexpected filetype: ' + file.filename
                        )
                return df_objects

Вызов этой функции будет выглядеть примерно так:

from win32com.client import Dispatch

outlook = Dispatch("Outlook.Application").GetNamespace("MAPI")
folder = outlook.Folders("[email protected]").Folders("Inbox")

out = pd.concat(
    [v.assign(date=k) for item in folder.Items for att in item.Attachments
     if zip_to_dfs(att) for k,v in zip_to_dfs(att).items()
    ]
)  # this will consolidate all the dfs in a single one

Эй, большое спасибо, что заглянул. Я определенно предпочитаю вашу переработанную версию своей. Я только что создал отдельную функцию для проверки того, является ли файл файлом Excel, так как мне, возможно, придется повторно использовать эту функцию в будущем.

Possdawgers 28.09.2023 15:59

Далее здесь. Я попробовал ваш метод, но после отладки вижу, что is_zipfile(tmp) каждый раз возвращает true, даже если он содержит файлы xls.

Possdawgers 29.09.2023 20:06

Потому что таблица Excel сама по себе является архивом. Вы можете заменить if is_zipfile(tmp): на if attachement.DisplayName.endswith('zip').

Timeless 29.09.2023 20:12

Спасибо! Очевидно, мне нужно многому научиться. Я ценю ваш вклад.

Possdawgers 29.09.2023 20:14

Кроме того, возможно, я не совсем ясно выразился. Вложения уже представляют собой zip-файлы. Они содержат разные документы Excel.

Possdawgers 29.09.2023 20:16

Другие вопросы по теме