В предыдущей статье мы рассмотрели, как извлечь текст из PDF с помощью easyocr и fitz.
Теперь, когда у нас есть "сырой" текст, пришло время привести его в порядок..
Этап "очистки" называется предобработкой текста (text preprocessing) и напрямую влияет на точность модели, если текст используется для обучения, а также на качество анализа, если текст обрабатывается напрямую.

Что включает предобработка?
Очистка текста от лишних символов
Нормализация регистра (text = text.lower())
Токенизация (разделение на слова)
Лемматизация / стемминг
Удаление стоп-слов
Опционально: POS-теггинг
В проекте EduText Analyzer (автоматизированный анализ учебников по иностранным языкам) мы используем комбинации этапов. Где-то лемматизация, стемминг, токенизация не требуются. Единственное, что, пожалуй, необходимо всегда – очистка текста от “мусора” (пунктуации, цифр, случайных комбинаций, оставшихся после конвертации из PDF).

Очистка текста от лишних символов
Учебники часто содержат:
случайные символы (", ', …, ‑, ‑);
«битые» последовательности (аа, мм, жжж, kdg, eogrfnb);
двойные пробелы;
строчные/заглавные несоответствия.
Разберем пример кода с регулярными выражениями для подготовки данных для обучения модели на базе логистической регрессии по определению типов упражнений по коммуникативной направленности (языковые/коммуникативные).
import re
def clean_sentence(text):
text = raw.lower() - нижний регистр
# удаляем короткие 2–3-буквенные фрагменты на кириллице
text = re.sub(r'\b[а-яА-Я]{2,3}\b', '', text)
# оставляем только буквы латиницы/кириллицы и пробелы
text = re.sub(r'[^а-яА-Яa-zA-Z\s]', ' ', text)
# убираем лишние пробелы
text = re.sub(r'\s+', ' ', text)
return text.strip()
Пример работы:
raw = "РасскаЖи другу1 5о… св оем любимом уроКЕ.!¶"
print(clean_sentence(raw))
# вывод: расскажи другу о любимом уроке
Почему регулярки?? re встроен в Python и не требует дополнительных зависимостей, работает быстро и адаптивен к различным паттернам.
Как вариант для очистки лишних символов можно использовать:
clean-text — быстро удаляет HTML, эмодзи, повторяющиеся символы;
ftfy — исправляет «битые» юникод-символы после конверсии PDF.
Нормализация регистра
Нормализация регистра может быть осуществлена с помощью простого встроенного метода строк в Python (text = text.lower()), так что на этом не останавливаемся подробно, идем к токенизации.
text = text.lower() #расскажи другу о любимом уроке
Токенизация
В EduText мы токенизируем тексты школьных учебников, чтобы, например, выделить лексемы для частотного анализа и классификации по CEFR (уровень сложности слов по Европейской шкале);
Для токенизации мы используем библиотеку spaCy, которая содержит готовые языковые модели (для английского: en_core_web_sm, для русского: ru_core_web_sm).
!python -m spacy download ru_core_news_sm
import spacy
# Загружаем модель spaCy
nlp = spacy.load("ru_core_news_sm") # или "en_core_web_sm" для английского
# Функция токенизации только слов (чистая лексика)
def tokenize_words(text):
doc = nlp(str(text)) # Преобразуем текст в объект Doc spaCy
# Возвращаем список слов в нижнем регистре, исключая пунктуацию и числа
return [token.text.lower() for token in doc if token.is_alpha]
# Функция токенизации с сохранением пунктуации
def tokenize_with_punct(text):
doc = nlp(str(text)) # Преобразуем текст в объект Doc spaCy
# Возвращаем список всех токенов кроме пробелов
return [token.text for token in doc if not token.is_space]
# Пример использования
text = "Послушай и найди артиста на картинке: Скажи, Что OH умеет делать"
print(tokenize_words(text)) # ['послушай', 'и', 'найди', 'артиста', 'на', 'картинке', 'скажи', 'что', 'он', 'умеет', 'делать']
print(tokenize_with_punct(text)) # ['Послушай', 'и', 'найди', 'артиста', 'на', 'картинке', ':', 'Скажи', ',', 'Что', 'OH', 'умеет', 'делать']
Функция |
Что возвращает |
Когда использовать |
tokenize_words |
Только слова |
Для частотного анализа, обучения модели |
tokenize_with_punct |
Слова + пунктуацию |
Для восстановления предложений, анализа структуры текста |
Также для токенизации можно использовать NLTK (Natural Language Toolkit). И, конечно, Hugging face transformers.
Лемматизация / стемминг
После токенизации и очистки текста важно привести слова к базовой форме (лемме), чтобы можно было объединять разные формы одного слова (runs - run).
Будем также использовать spaCy для лемматизации, пока что сохраняя стоп-слова, чтобы не разрушать фразы.
import spacy
# Загружаем модель spaCy
nlp = spacy.load("en_core_web_sm")
def lemmatize_page(text):
doc = nlp(text) # text уже в нижнем регистре
tokens = [token.lemma_.strip() for token in doc if token.is_alpha] # только буквы
return ' '.join(tokens)
page_text = "Nick's big rabbit is grey and black." # пример текста
print(lemmatize_page(page_text))
# Вывод: Nick big rabbit be grey and black
Удаление стоп-слов
Стоп-слова — это слова, которые обычно не несут смысловой нагрузки сами по себе, часто являются служебными словами: артикли, предлоги, союзы, местоимения.
Примеры на английском: the, and, is, in, on.
Примеры на русском: и, в, на, что, как.
Иногда стоп-слова оставляем, если важна структура фразы — например, при точном поиске фраз в учебнике. Но для анализа лексики их удаляем.
import re
from nltk.corpus import stopwords
import nltk
# Загружаем стоп-слова
nltk.download("stopwords")
stop_words = set(stopwords.words("english"))
def extract_words(phrase):
"""
Разбивает фразу на слова, удаляет знаки препинания и стоп-слова.
Параметры:
phrase (str):
Возвращает:
list: список значимых слов
"""
# Убираем пунктуацию и приводим к нижнему регистру
phrase = re.sub(r'[^\w\s]', '', phrase).lower()
# Разбиваем на слова и удаляем стоп-слова
return [word for word in phrase.split() if word not in stop_words]
example_phrase = "Nick's big rabbit is grey and black."
print(extract_words(example_phrase))
# Вывод: ['nicks', 'big', 'rabbit', 'grey', 'black']
POS (Part-of-Speech Tagging)
POS (Part-of-Speech Tagging) позволяет определить часть речи каждого слова — существительное, глагол, прилагательное и т.д.
Это полезно для анализа структуры предложений, выделения ключевых слов и построения грамматических шаблонов.
import spacy
nlp = spacy.load("en_core_web_sm")
doc = nlp("Nick's big rabbit is grey and black.")
for token in doc:
print(token.text, token.pos_)
#Вывод
Nick PROPN
's PART
big ADJ
rabbit NOUN
is AUX
grey ADJ
and CCONJ
black ADJ
. PUNCT
После предобработки текста данные сохраняются в форматах CSV или TXT для последующего анализа, обработки или обучения:3. CSV-файлы удобны для работы с таблицами и метаданными, обучения, а TXT-файлы — для дальнейшей текстовой работы.