Используйте python для автоматизации офисной рутины

В повседневной офисной работе вам часто приходится что-то делать с кучей файлов. Разархивируйте, переместите, переименуйте, обновите, снова заархивируйте и отправьте или загрузите на FTP. Все это просто сделать, но вам потребуется очень много времени, чтобы повторить их вручную с десятками или сотнями файлов.

К счастью, вы можете использовать языки программирования для автоматизации этих повторяющихся задач. Python - идеальный выбор, потому что его легко установить на компьютере, и даже новичок в вычислениях может создать программу с помощью небольших проб и ошибок. Python - это универсальный язык программирования, с которым вы можете решить практически любую проблему.

Давайте рассмотрим довольно распространенную задачу во многих отраслях. Мы будем:

  1. Разархивируйте группу файлов
  2. Обновите их содержимое - в нашем примере мы изменим содержимое узла XML.
  3. Снова заархивируйте результат - используя zipfile и shutil libraries

Вы можете выполнить это упражнение в блокноте github - Unzip_Update_Zip_full.ipynb.

Установка Python

Есть несколько способов настроить python на свой компьютер. Мне лично нравится пользоваться Anaconda и Jupyter notebook. Вы загружаете и устанавливаете пакет conda, призванный упростить управление средой Python со всеми возможными улучшениями, и запускаете ноутбук, который является важной частью пакета.

В записной книжке вы можете запустить отдельную ячейку, содержащую одну или несколько строк кода, и сразу увидеть результаты. Такой подход идеально подходит для прототипирования. С помощью Google, stackoverflow и руководств, например здесь, на medium, вы можете быстро комбинировать фрагменты кода, необходимые для того, что вам нужно сделать.

Распаковка

Давайте посмотрим, как вы подойдете к задаче по распаковке файла в Python. Просто зайдите в Google и введите unzip python. Он быстро выводит список результатов, и вы выбираете, скажем, один из крупнейших сайтов вопросов и ответов - Распаковка файлов stackoverflow в Python. Вы узнаете, как это сделать, в трех строчках кода:

import zipfile
with zipfile.ZipFile(path_to_zip_file, 'r') as zip_ref:
    zip_ref.extractall(directory_to_extract_to)

Вам import библиотека, необходимая для работы с .zip файлами - zipfile. Это позволяет вам использовать его функции и методы для распаковки архивов и их повторного заархивирования.

Сначала вы открываете архив для чтения с помощью zipfile.ZipFile(path_to_zip_file, 'r'), а затем извлекаете содержимое пакета в каталог zip_ref.extractall(directory_to_extract_to).

Я использовал обозначение with, чтобы убедиться, что архив открыт, выполняется действие, а затем архив закрывается, чтобы его содержимое было освобождено из памяти. Это предотвращает утечку памяти в случае обработки сотен или тысяч файлов.

Распаковка папки с большим количеством файлов

Чтобы распаковать файл на Python, вы используете три строки кода. Чтобы распаковать кучу файлов, нужно добавить еще несколько строк. Вы должны идентифицировать zip-файлы в папке и передать их описанной выше процедуре.

Библиотека os позволяет одновременно получать содержимое папки os.listdir() и комбинировать имена папок и файлов с помощью os.path.join(folder,file). Listdir возвращает как файлы, так и подпапки. Вы можете добавить file.endswith(".zip"), чтобы идентифицировать только архивы.

В качестве альтернативы вы можете использовать os.path.splitext(). Второй вывод - это расширение файла.

Зная все файлы в папке, вы можете применить предыдущий код, чтобы распаковать их. Опять же, вы можете перебирать файлы и разархивировать каждый из них или обернуть разархивирование в функцию, которая вызывается в однострочном исчерпывающем обозначении.

def unzip(folder: str, file: str, folder_to_extract: str) -> list:
    """unzips a file in a folder into folder_to_extract
    returns a list of files in the zip archive"""
    with zipfile.ZipFile(os.path.join(folder,file), 'r') as zip_ref:
        zip_ref.extractall(folder_to_extract)
        return zip_ref.namelist()
# applying a function to the output can be squeezed into the list comprehension
[unzip(folder, f, "temp") for f in os.listdir(folder) if f.endswith(".zip")]

Обновите извлеченный XML

После того, как мы извлекли XML-файл в папку temp, мы можем работать с ним. Python содержит библиотеку xml.etree для обработки файлов XML. В нашем случае мы знаем, что zip-архив содержал один XML-файл a.xml, хотя вы всегда можете перечислить все файлы в архиве, используя - zip_ref.namelist().

Чтобы обновить XML-файл, вам необходимо импортировать древовидную библиотеку и загрузить XML-документ.

from xml.etree import ElementTree as ET
# parse the XML document into a variable
tree = ET.parse(path)
# get the root emelement containing all the XML nodes
root = tree.getroot()

Помните, что любая переменная, производная от tree , является ссылкой. Когда вы обновляете содержимое этих переменных, вы также обновляете tree.

Допустим, мы хотим обновить узел <id>...</id> в нашем XML. Сначала мы должны его найти. В библиотеке etree есть несколько методов поиска узлов XML.

  • .find() обнаружил первое появление элемента XML
  • .findall() перечислить все появления подэлемента

Оба метода принимают имя элемента или его можно найти с помощью xpath. Xpath - это своего рода адрес внутри XML для поиска нужного элемента. Когда узел обнаружен, используется .text аргумент для получения значения, записанного между тегами.

# in the <data> find the <id>...</id> node and show its content (.text)
id = root.find("id").text

Наш идентификатор состоит из трех частей, разделенных подчеркиванием «_»:

  • Префикс
  • Версия
  • Дата

Один из XML имеет идентификатор - test_001_20201026. .text возвращает значения в виде строки, и вы можете применить .split("_") функцию, чтобы разбить текст на детали.

[In]: 
  split_id = id.split("_")
  print(split_id)
[Out]: ["test","001","20201026"]

Результатом является список Python, который можно легко обновлять поэлементно. Сначала меняем префикс split_id[0] = new_prefix. Потом обновляем версию.

split_id[0] = new_prefix
split_id[1] = "{:03d}".format(new_version) 

Мы использовали "{:03d}".format(new_version), чтобы добавить ведущие нули до 3 символов, если необходимо, чтобы превратить 1 в 001.

Обновленные значения могут быть объединены в одну строку с помощью .join("_"), и исходный компонент XML может быть обновлен с его помощью:

root.find("id").text = "_".join(split_id)

Как я уже упоминал, любое обновление переменной, полученной из tree, также обновляет tree. Мы достигли желаемого обновления XML, и нам нужно только экспортировать его, используя tree.write(output_path).

Давайте посмотрим на полный код обновления XML:

Заархивируйте обновленный XML

Теперь обновленный a.xml лежит в папке temp. Нам нужно только заархивировать его и поместить этот архив в processed folder. Давайте сначала создадим папку вывода, если она не существует. Мы будем использовать Path из pathlib библиотеки.

# create the output folder if it doesn't exists
Path(output_folder).mkdir(parents=True, exist_ok=True)

Затем мы упаковываем XML в zip-архив, используя zipfile lib. Теперь надо открыть архив для записи 'w'. Тогда воспользуемся ссылкой на этот архив и .write(path_to_file, name_of_this_file).

Если вы не укажете второй параметр name_of_the_zipped_file, будет заархивирован весь путь, включая папки и подпапки.

Если вы не знаете имя файла, os библиотека может помочь вам с ее os.path.basename() функцией.

with zipfile.ZipFile(output_path, 'w') as myzip:
    myzip.write(path_to_processed_xml, os.path.basename(path_to_processed_xml))

Иногда вам нужно также упаковать папки и файлы. В нашем примере Folder_001_20201101.zip содержит файл и другие файлы в папке.

  • folder_A / a.xml
  • efg.txt

В таком случае было бы слишком полно использовать zip-файл, но у вас есть альтернатива в shutil.make_archive(output_folder, format, folder_to_pack). Этот метод только архивирует содержимое folder_to_pack и помещает результат в output_folder. Вы можете выбирать из форматов zip, tar, gztar, bztar, xztar.

Положил все это вместе

Теперь мы знаем все составляющие процесса:

  1. разархивировать несколько архивов из папки во временную папку
  2. обновил каждый из распакованных файлов
  3. и снова заархивировал этот обновленный файл в архив

В этом последнем упражнении мы должны сделать несколько небольших настроек. Мы будем использовать имя исходного архива, обновлять его, а также обновлять содержимое всех содержащихся XML-файлов. Причина в том, что в архиве может быть два или более XML-файла, каждый с разными идентификаторами, и мы заархивируем их все обратно в обновленный zip-архив (который не может иметь двух разных имен).

Полный код можно найти в этой записной книжке - Unzip_Update_Zip_full.ipynb

Вывод

В этом простом руководстве вы увидели мощь Python (или любого другого языка программирования). С помощью нескольких строк кода вы можете автоматизировать задачи, выполнение которых вручную заняло бы много времени. Я использую этот принцип, когда решаю, стоит ли тратить время на кодирование:

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

Операция в этом примере может иметь множество вариаций. Возможно, вы захотите обновить другой узел XML. ZIP, возможно, содержит файлы CSV. Вы скорее обновите дату, а не префикс и версию. Возможно, вам потребуется изменить ввод - например, увеличьте версию на 1 или добавьте неделю к дате.

Все это возможно с небольшими изменениями кода. Вы также можете просмотреть полный пример в блокноте jupyter на Github.

Я надеюсь, что вам понравился этот учебник по архивированию и обработке XML, и что вам нравится то, чего вы можете достичь с помощью python. Возможности безграничны.

Other articles:
* Plotly Express complete tutorial
* Very illustrative highlighted line chart
* Plotly Histogram - Complete Guide
* Everything you wanted to know about Kfold train-test split
* How to turn a list of addreses into a map