Как стать автором
Поиск
Написать публикацию
Обновить

Сортировка книг по тематикам скриптами Python

Уровень сложностиПростой
Время на прочтение6 мин
Количество просмотров7.6K

На момент написания этой заметки около половины из 16 тысяч книг в моей библиотеке — ИТшные, другая половина — медицинские. Две трети этих книг на английском, одна треть — на русском.

Примерно раз в месяц я с телеграм-каналов докачиваю еще 1–2 тысячи книг, из которых реально новых — не более 100–200, остальное у меня уже есть. Кроме того, попадаются сканированные книги с околонулевой пользой, если их не распознавать.

Всё это добро мне нужно регулярно дедуплицировать, раскладывать по тематическим папочкам, выкладывать в облако для коллег и при этом не тратить на это много времени. Готовых программ для таких задач я не нашел, поэтому, как мог, справлялся сам — писал скрипты на Python.

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

Всего 7 этапов:

  1. Разбираем архивы

  2. Чистим мелкие и левые файлы

  3. Дедуплицируем файлы

  4. Разбиваем все скачанные файлы на ИТ и медицину

  5. Проставляем языковые префиксы

  6. Сортируем по подпапкам

  7. Разрабатываем ключевые слова для папки

1. Разбираем архивы

В телеграме я регулярно прохожу по книжным каналам, читаю все непрочитанные сообщения, все файлы из этих сообщений валятся в папку \YandexDisk\__unsorteds. Именно с этой папки начинают свой путь вообще все файлы.

Поскольку в библиотеку я должен отправить только файлы в формате PDF или EPUB, я с помощью библиотеки patoolib все архивы *.zip, *.rar, *.7z распаковываю в папку \YandexDisk\__unsorteds\unpacked, потом отбираю все файлы PDF и EPUB в этой подпапке и забрасываю их обратно в папку \YandexDisk\__unsorteds.

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

2. Чистим мелкие и нерелевантные файлы

Здесь у меня используются три рабочих папки.

Первую вы уже знаете — \YandexDisk\__unsorteds. Здесь лежат и скачанные файлы и файлы после распаковки и очистки.

В папку \YandexDisk\4OCR перемещаются все файлы с расширением *.djvu. Я в силу своей кодерской беспомощности, так и не придумал, как нормально работать с файлами с этим расширением (они то открываются через раз, то текстовый слой там кривой), поэтому я от греха подальше забрасываю все такие файлы в эту папку. Их далее оттуда подхватывает служба HotFolder из пакета FineReader, распознает и складывает в папку \YandexDisk\OCRed.

Вот из последней папки я на этом этапе подхватываю уже распознанные PDFки с хорошим текстовым слоем и переношу в папку \YandexDisk\__unsorteds, до кучи.

Таким образом перед началом чистки я пополняю исходную папку распознанными файлами и забрасываю на распознавание заведомо неудобные, но потенциально полезные DJVU-файлы. Дальше начинаю чистку PDF и EPUB-файлов.

У меня два критерия чистки на этом этапе:

  • если файл меньше 1 МБ;

  • если файл меньше 100 страниц.

3. Дедуплицируем файлы

Дедупликацию я делаю в два этапа:

  1. удаляю дубли внутри стартовой папки \YandexDisk\__unsorteds;

  2. удаляю дубли в стартовой папке, если находятся совпадения с файлами из папок и подпапок в \YandexDisk\IT и \YandexDisk\MED.

Как вы поняли, две последние папки — это корневые тематические папки для ИТшных и медицинских книг.

Мне показалось, что вычислять дубли по хешу первых 1000 знаков будет быстрее, чем по хешу всей книги (но я не замерял), поэтому настроил вычисление хеша только по этой тысяче.

4. Разбиваем все скачанные файлы на ИТ и медицину

На этом этапе файлы из стартовой папки \YandexDisk\__unsorteds разъезжаются по трем папкам:

  • \YandexDisk\IT\#UNSORTED

  • \YandexDisk\MED\#UNSORTED

  • \YandexDisk\4OCR

Логика здесь получилась такая. Я собрал ключевые слова, которые с наибольшей вероятностью попадутся в медицинских книгах, и положил их в список keywords_med. Аналогично поступил с ИТшными ключами — keywords_it.

Я также подгружаю библиотеку spacy, а к ней две модельки — английского и русского языков..

Далее считываю из каждого файла по 100 тыс. знаков, вычисляю 35 наиболее часто используемых слов и сравниваю количество совпадений со списками keywords_med и keywords_it. По количеству совпадений скрипт принимает решение, куда переселять файл.

Если скрипт не может прочитать эти слова или говорит, что их количество одинаковое (скорее всего 0 и в том, и в другом) — значит текстовый слой в файле отсутствует или он битый. Следовательно, файл уезжает на распознавание, в папку \YandexDisk\4OCR.

5. Проставляем языковые префиксы

Это простой этап. Скрипт обходит все файлы из \YandexDisk\IT\#UNSORTED и \YandexDisk\MED\#UNSORTED, считывает из каждого первые 15 страниц, приводит считанные строки к нижнему регистру и считает из какой строки у него знаков больше:

    cyrillic_alphabet = "абвгдеёжзийклмнопрстуфхцчшщъыьэюя"
    english_alphabet = "abcdefghijklmnopqrstuvwxyz"

Если из cyrillic_alphabet, значит файлу делается префикс RU_, если из english_alphabet — префикс EN_.

6. Сортируем по подпапкам

На этом этапе файлы из папок \YandexDisk\MED\#UNSORTED и \YandexDisk\IT\#UNSORTED уже разъезжаются по своим подпапкам. ИТшные книги уезжают в папку для книг по разработке (а там далее по Python, Java, C#), DevOps, Data Science, СУБД и т. д.

Медицинские книги, соответственно, уезжают в тематические папки на тему кардиологии, неврологии, онкологии и т.д.

Логика тут получилась такая: считываю первые 30 000 знаков из каждой книги, привожу их в нижний регистр, в зависимости от префикса RU_ или EN_ обрабатываю их русской или английской моделью Spacy, причем слежу за тем, чтобы:

  • слова были не короче 4 знаков,

  • приводились к лемме (исходной форме),

  • в лемме не было цифр и небуквенных знаков (за исключением дефиса).

Получившийся текст прогоняю через алгоритм TF-IDF (TfidfVectorizer), чтобы получить список из ТОП-30 слов. Отмечу, что в ранних своих попытках решить задачу с сортировкой я считал просто часто используемые слова — в итоге в качестве ключевых выдавались наиболее употребимые слова, а это не то, что мне нужно.

Далее скрипт сопоставляет список полученных слов со списками заранее подготовленных ключевых слов (о том, как их делать — ниже). Каждому такому списку соответствует тематическая папка, и если как минимум 3 слова из списка совпало, то скрипт проверяет, с каким списком совпадений больше. Победивший список определяет целевую папку, и файл уходит в нее. Если совпадений меньше 3, значит, файл остается в исходной папке #UNSORTED, пока я не настрою под него свой список ключевых слов (тут я еще в процессе).

7. Разрабатываем ключевые слова для папки

На этом этапе я долго ломал голову, как сделать ключевые слова так, чтобы, например, книга про Python попадала именно в папку для Python, а не для Java. Списки наиболее часто встречающихся слов не помогали, более того, они еще сильнее усложняли работу.

Например, слово function может встречаться в книгах и по разработке, и по DevOps, и по матану, и по линалу. В какой список поставить это слово? Постепенно допер, что это слово вообще нельзя использовать. Нужно использовать только те слова, которые ни в какой другой тематике больше не используются (или как минимум в ТОП-100 ключевых слов по TF-IDF не попадут).

Таким образом, в список слов по Python попали такие слова, как matplotlib, finally, jupyter, ipython, pandas, а в список слов по, например, неврологииmyelin, postsynaptic, таламус, неврологический, тройничный.

Для того чтобы сформировать этот список слов, я создавал папку, складывал туда вручную отобранные 20-30 книг по нужной тематике на двух языках — желательно такие, где нужного тематического текста побольше, — получалось что-то вроде тренировочного датасета. Далее извлекал из каждой 200 топовых слов по TF-IDF и складывал их в список, причем каждое слово должно было соответствовать следующим критериям:

  • не входит в список стоп-слов (из моделей spacy, то есть не является артиклем, предлогом и т. п.);

  • состоит только из букв;

  • не менее 4 букв;

  • не встречается в других тематических списках (как я уже говорил, нельзя, чтобы слова в списках пересекались).

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

'function', :14
'datum', :13
'example', :13
'time', :13
'follow', :13
'work', :13
'need', :13
'chapter', :13
'true', :13

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

Заключение

Таким образом, сейчас алгоритм более или менее корректно раскидывает книги, практически не требуя моего внимания. Папки с отсортированными книгами выглядят примерно так:

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

P.S. Сейчас бьюсь над задачей, чтобы нормально извлекать имена книг из их содержимого и унифицировать имена файлов. Если вдруг кто-то уже решал такую задачу, не сочтите за труд, поделитесь соображениями в комментариях. ISBN не подходит, далеко не у всех книг он есть.

Теги:
Хабы:
Всего голосов 13: ↑12 и ↓1+14
Комментарии12

Публикации

Ближайшие события