• 15 апреля стартует «Курс «SQL-injection Master» ©» от команды The Codeby

    За 3 месяца вы пройдете путь от начальных навыков работы с SQL-запросами к базам данных до продвинутых техник. Научитесь находить уязвимости связанные с базами данных, и внедрять произвольный SQL-код в уязвимые приложения.

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

    Запись на курс до 25 апреля. Получить промодоступ ...

Заметка Дорабатываем сортировщик PDF и EPUB. Сортируем FB2 по датам с помощью Python

Темы, которые НЕ подходят по объему под префикс "Статья"
Всем привет. В двух предыдущих статьях «Сортировка книг в формате PDF по годам выпуска с помощью Python» и «Дорабатываем сортировку книг по папкам. Сортировка Epub с помощью Python» был сделан сортировщик, который раскладывает книги в форматах pdf и epub по годам выпуска. Но, есть еще формат, который был не затронут в предыдущих статьях, это fb2. У данного формата также есть метаданные, и он представляет собой файл в формате xml. А значит, извлечение метаданных из него вполне возможно, как и последующая сортировка по папкам.

Image-25_2.jpg



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

Установить библиотеку для парсинга данных BeautifulSoup:

pip install bs4

Возможно, использование данной библиотеки здесь избыточно, но, именно с ее помощью получилось довольно просто получить данные из файла данного формата. Все остальные решения либо сильно усложнены, либо требуют более детального изучения. А мне нужно было сделать все как можно быстрее и проще. Что я и реализовал на практике.


Дополнение сортировщика файлов

Давайте приступим к дополнению сортировщика файлов, который был сделан в прошлых статьях. Для начала импортируем библиотеку BeautifulSoup:

from bs4 import BeautifulSoup

Теперь необходимо сделать функцию, которая будет получать данные из книги в формате fb2. Назову ее get_fb2_info(fname). В данную функцию передается путь к файлу для получения данных. А проще говоря, для парсинга. Далее, данный файл открывается для чтения и данные из него передаются в переменную src. После создается объект BeautifulSoup, в который и передаются данные из переменной src, а так же указывается парсер, с помощью которого будет выполняться поиск по файлу – xml. Далее пишем условие для поиска данных. Дело в том, что дата в файлах попадается в разных тегах. В зависимости от того, в какой программе создавалась книга. Поэтому делаем простую проверку на их наличие и если они присутствуют, получаем данные о дате. Ну и далее, возвращаем полученную дату из функции.

Python:
def get_fb2_info(fname):
    with open(fname, 'r', encoding='utf-8') as file:
        src = file.read()
    soup = BeautifulSoup(src, 'xml')
    if soup.find('date', {'value': True}):
        return soup.find('date', {'value': True})['value'][0:4]
    elif soup.find('date'):
        return soup.find('date').text[0:4]

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

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)
        elif item.endswith('.fb2'):
            print(item)
            date_f = get_fb2_info(os.path.join(ins, item))
            move_file(ins, item, date_f)

Вот собственно и все. Теперь, с помощью дополненной программы можно быстро разложить загруженные книги в форматах pdf, epub и fb2 по годам издания. Вручную это, полагаю, заняло бы достаточно много времени. Я, к тому времени, как озаботился сортировкой книг, успел скачать их более 2000 штук. И просматривать все их вручную для сортировки по годам было очень утомительно, откуда и возникла идея создать сортировщик в python. Автоматизация :)

Ну и вот скрин папки до работы скрипта:

screenshot1.png

Ну и после сортировки:

screenshot2.png

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

Python:
import os
import zipfile

from PyPDF2 import PdfFileReader
from bs4 import BeautifulSoup
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 get_fb2_info(fname):
    with open(fname, 'r', encoding='utf-8') as file:
        src = file.read()
    soup = BeautifulSoup(src, 'xml')
    if soup.find('date', {'value': True}):
        return soup.find('date', {'value': True})['value'][0:4]
    elif soup.find('date'):
        return soup.find('date').text[0:4]


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)
        elif item.endswith('.fb2'):
            print(item)
            date_f = get_fb2_info(os.path.join(ins, item))
            move_file(ins, item, date_f)


if __name__ == "__main__":
    main()

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

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

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