Заметка Дорабатываем сортировку книг по папкам. Сортировка Epub с помощью Python

Темы, которые НЕ подходят по объему под префикс "Статья"
В статье «Сортировка книг в формате PDF по годам выпуска с помощью Python» я поделился кодом, с помощью которого происходит сортировка книг в формате pdf по годам выпуска. Но, нужно больше. Ведь книги есть также и в формате epub. И их довольно много. Открывать каждую – значит просто потерять кучу времени. Следовательно, нужно как-то дополнить код, который был в статье так, чтобы сортировка велась также и по формату epub.

Image-25_1.jpg



Что понадобиться?

Для работы сортировки epub нужно будет импортировать модуль:

import zipfile

А также установить, если у вас еще не установлена, библиотеку для парсинга xml, lxml:

pip install lxml

И импортировать из нее etree:

from lxml import etree


Дополняем сортировщик

Не скажу, что я сам придумал решение. Нет, я нашел его в интернете, уж даже и не помню, на каком форуме. Но, суть в том, что каждый файл в формате epub – это zip-архив. И перед тем, как что-то в нем искать, его нужно распаковать. Ну, а далее, просто парсить xml. Давайте дополним код из предыдущей статьи дополнительной функцией def get_epub_info(fname), которая будет получать имя файла с директорией, открывать его, распаковывать и производить поиск нужной информации. После чего возвращать ее в переменную, из под которой была вызвана функция.

Python:
def get_epub_info(fname):
    ns = {
        'n': 'urn:oasis:names:tc:opendocument:xmlns:container',
        'pkg': 'http://www.idpf.org/2007/opf',
        'dc': 'http://purl.org/dc/elements/1.1/'
    }

    try:
        zip = zipfile.ZipFile(fname)
    except:
        print(f'Не могу прочитать файл {fname}')

    try:
        txt = zip.read('META-INF/container.xml')
        tree = etree.fromstring(txt)
        cfname = tree.xpath('n:rootfiles/n:rootfile/@full-path', namespaces=ns)[0]

        cf = zip.read(cfname)
        tree = etree.fromstring(cf)
        p = tree.xpath('/pkg:package/pkg:metadata', namespaces=ns)[0]
        return p.xpath('dc:%s/text()'%('date'), namespaces=ns)[0][0:4]
    except:
        return '0000'

Ну и дополним функцию main собственно определением расширения файла и вызовом соответствующей функции для поиска в нем информации.

Python:
def main():
    ins = get_target_path()
    for item in os.listdir(ins):
        if item.endswith('.pdf'):
            print(item)
            date_f = get_date_file(os.path.join(ins, item))
            move_file(ins, item, date_f)
        elif item.endswith('.epub'):
            print(item)
            date_f = get_epub_info(os.path.join(ins, item))
            move_file(ins, item, date_f)

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

Вот папка до перемещения книг epub:

screenshot1.png

И эта же папка после перемещения книг:

screenshot2.png

Как видите, создалась папка с именем 0101. Это значит, что в книгах содержалось некорректное значение даты. Я проверил данные, которые получаются до обрезки, во избежание того, что я обрезаю что-то нужное. Но нет, такая дата и содержится в получаемом значении.

Ну и полный код скрипта:

Python:
import os
import zipfile

from PyPDF2 import PdfFileReader
from lxml import etree


def get_date_file(file_name):
    try:
        file = open(file_name, "rb")
    except:
        print(f'Не могу открыть файл {file_name}')
        file.close()
    try:
        pdf_toread = PdfFileReader(file)
        if pdf_info := pdf_toread.getDocumentInfo():
            file.close()
            return pdf_info['/CreationDate'][2:6]
        else:
            file.close()
            return '0000'
    except:
        file.close()
        return '0000'


def get_epub_info(fname):
    ns = {
        'n': 'urn:oasis:names:tc:opendocument:xmlns:container',
        'pkg': 'http://www.idpf.org/2007/opf',
        'dc': 'http://purl.org/dc/elements/1.1/'
    }

    try:
        zip = zipfile.ZipFile(fname)
    except:
        print(f'Не могу прочитать файл {fname}')

    try:
        txt = zip.read('META-INF/container.xml')
        tree = etree.fromstring(txt)
        cfname = tree.xpath('n:rootfiles/n:rootfile/@full-path', namespaces=ns)[0]

        cf = zip.read(cfname)
        tree = etree.fromstring(cf)
        p = tree.xpath('/pkg:package/pkg:metadata', namespaces=ns)[0]
        return p.xpath('dc:%s/text()'%('date'), namespaces=ns)[0][0:4]
    except:
        return '0000'


def move_file(ins, item, date_f):
    if not os.path.isdir(os.path.join(ins, date_f)):
        os.mkdir(os.path.join(ins, date_f))
    os.rename(os.path.join(ins, item), os.path.join(ins, date_f, item))


def get_target_path():
    while not os.path.isdir(user_input := input("Введите путь к папке: ")):
        print(f"Папка {user_input} не найдена")
    print(f"\nРаботаем с папкой {user_input}\n")
    return user_input


def main():
    ins = get_target_path()
    for item in os.listdir(ins):
        if item.endswith('.pdf'):
            print(item)
            date_f = get_date_file(os.path.join(ins, item))
            move_file(ins, item, date_f)
        elif item.endswith('.epub'):
            print(item)
            date_f = get_epub_info(os.path.join(ins, item))
            move_file(ins, item, date_f)


if __name__ == "__main__":
    main()

А вот о том, как переместить книги в формате fb2, думаю, что разберемся уже в следующей статье. Тут мне пришлось немного повозиться. Так как все предлагаемые решения, которые я находил, сводились к тому, что надо парсить xml. И это верно. Так я и сделал. Но, как? Это уже другой вопрос.

Спасибо за внимание. Надеюсь, что данная информация была кому-нибудь полезна

Первая часть
Третья часть
 
Последнее редактирование:
  • Нравится
Реакции: ROP
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!