Детектор плагиата на Python — Tkinter и Difflib

О чём эта статья
Здесь мы разберём:
- базовый GUI на Tkinter для загрузки и отображения текстов;
- сравнение строк и файлов с помощью difflib.SequenceMatcher;
- подсветку одинаковых фрагментов и вывод процента сходства;
- ограничения метода и способы улучшения с помощью NLP;
- рекомендации по тестированию, приёмке и эксплуатационной проверке.
Важно: цель — образец для обучения и прототипирования, а не решение для промышленного обнаружения плагиата.
Короткая техническая справка
SequenceMatcher — алгоритм сравнения последовательностей из стандартной библиотеки Python. Он работает на уровне символов и возвращает коэффициент сходства (ratio) и набор операций (opcodes), описывающих различия между последовательностями.
Tkinter — кроссплатформенная библиотека для создания простых GUI-приложений в Python.
Определения в одну строку:
- SequenceMatcher: класс для сравнения двух последовательностей и получения коэффициента схожести.
- opcode: операция, описывающая участок сравнения (replace, delete, insert, equal).
Полный пошаговый пример: логика и код
Ниже — собранный пример приложения. Код разбит на функции с пояснениями. Сохраните файл, например, как detector.py и запустите в окружении с Python 3.
import tkinter as tk
from tkinter import filedialog
from difflib import SequenceMatcher
def load_file_or_display_contents(entry, text_widget):
"""Загружает файл: если в entry указан путь, использует его, иначе открывает диалог."""
file_path = entry.get()
if not file_path:
file_path = filedialog.askopenfilename()
if file_path:
entry.delete(0, tk.END)
entry.insert(tk.END, file_path)
# Открываем файл в текстовом режиме и отображаем содержимое
with open(file_path, 'r', encoding='utf-8', errors='ignore') as file:
text = file.read()
text_widget.delete(1.0, tk.END)
text_widget.insert(tk.END, text)
def compare_text(text1, text2):
"""Возвращает процент сходства (int) и список opcode для подсветки."""
d = SequenceMatcher(None, text1, text2)
similarity_ratio = d.ratio()
similarity_percentage = int(similarity_ratio * 100)
diff = list(d.get_opcodes())
return similarity_percentage, diff
def show_similarity():
"""Считывает тексты из двух полей, вычисляет сходство и подсвечивает равные сегменты."""
text1 = text_textbox1.get(1.0, tk.END)
text2 = text_textbox2.get(1.0, tk.END)
similarity_percentage, diff = compare_text(text1, text2)
text_textbox_diff.delete(1.0, tk.END)
text_textbox_diff.insert(tk.END, f"Similarity: {similarity_percentage}%")
# Снимаем предыдущие метки
text_textbox1.tag_remove("same", "1.0", tk.END)
text_textbox2.tag_remove("same", "1.0", tk.END)
# Проходим по opcodes и отмечаем равные участки
for opcode in diff:
tag = opcode[0]
start1 = opcode[1]
end1 = opcode[2]
start2 = opcode[3]
end2 = opcode[4]
if tag == "equal":
text_textbox1.tag_add("same", f"1.0+{start1}c", f"1.0+{end1}c")
text_textbox2.tag_add("same", f"1.0+{start2}c", f"1.0+{end2}c")
# Инициализация GUI
root = tk.Tk()
root.title("Text Comparison Tool")
frame = tk.Frame(root)
frame.pack(padx=10, pady=10)
text_label1 = tk.Label(frame, text="Text 1:")
text_label1.grid(row=0, column=0, padx=5, pady=5)
text_textbox1 = tk.Text(frame, wrap=tk.WORD, width=40, height=10)
text_textbox1.grid(row=0, column=1, padx=5, pady=5)
text_label2 = tk.Label(frame, text="Text 2:")
text_label2.grid(row=0, column=2, padx=5, pady=5)
text_textbox2 = tk.Text(frame, wrap=tk.WORD, width=40, height=10)
text_textbox2.grid(row=0, column=3, padx=5, pady=5)
file_entry1 = tk.Entry(frame, width=50)
file_entry1.grid(row=1, column=2, columnspan=2, padx=5, pady=5)
load_button1 = tk.Button(frame, text="Load File 1", command=lambda: load_file_or_display_contents(file_entry1, text_textbox1))
load_button1.grid(row=1, column=0, padx=5, pady=5, columnspan=2)
file_entry2 = tk.Entry(frame, width=50)
file_entry2.grid(row=2, column=2, columnspan=2, padx=5, pady=5)
load_button2 = tk.Button(frame, text="Load File 2", command=lambda: load_file_or_display_contents(file_entry2, text_textbox2))
load_button2.grid(row=2, column=0, padx=5, pady=5, columnspan=2)
compare_button = tk.Button(root, text="Compare", command=show_similarity)
compare_button.pack(pady=5)
text_textbox_diff = tk.Text(root, wrap=tk.WORD, width=80, height=1)
text_textbox_diff.pack(padx=10, pady=10)
# Настройка стиля подсветки одинаковых участков
text_textbox1.tag_configure("same", foreground="red", background="lightyellow")
text_textbox2.tag_configure("same", foreground="red", background="lightyellow")
root.mainloop()Пояснения к коду
- load_file_or_display_contents: запрашивает путь в поле или показывает диалог выбора файла; затем читает файл и вставляет текст в соответствующий Text.
- compare_text: использует SequenceMatcher для вычисления коэффициента и получения opcodes — набора операций, которые удобны для подсветки фрагментов.
- show_similarity: обновляет поле с процентом сходства и помечает одинаковые фрагменты тегом “same”.
Обратите внимание на кодировку при чтении файла: часто полезно указывать encoding=’utf-8’ и errors=’ignore’ для устойчивости к файлам с разной кодировкой.
Как работает SequenceMatcher (технически)
SequenceMatcher сравнивает последовательности символов и ищет длинные совпадающие подстроки, затем строит на их основе разметку (opcodes). Типичные значения opcode:
- replace — обе последовательности имеют участок, но они разные;
- delete — участок есть в первой последовательности, отсутствует во второй;
- insert — участок отсутствует в первой, присутствует во второй;
- equal — участки идентичны.
Этот подход чувствителен к перестановкам и небольшим правкам. SequenceMatcher более эффективен для сравнений текстов небольшого и среднего размера.
Примеры вывода
При полном совпадении программа показывает “Similarity: 100%” и подсвечивает весь текст как одинаковый. Если изменить одну строку, подсветятся совпадающие фрагменты, а процент снизится.
Ограничения подхода difflib
- чувствителен к орфографическим/пунктуационным правкам: замена слов синонимами снижает сходство;
- не учитывает семантику: перефразирование с сохранением смысла часто обходит детектор;
- производительность: для очень больших документов сравнение по символам может быть медленным и требовать большой памяти;
- нечувствительность к языковой структуре: для других языков может требоваться предобработка.
Когда difflib не подойдёт:
- когда нужно обнаруживать перефразированный текст;
- для сопоставления источников в разных форматах (PDF, DOCX) без предварительной унификации;
- когда требуется сопоставлять большие корпуса (миллионы документов).
Улучшения и альтернативные подходы (кратко)
- Предобработка текста: нормализация регистра, удаление пунктуации, стемминг/лемматизация, удаление стоп-слов.
- N-gram-подход: сравнение множеств n-грам (например, 3- и 5-грам) с Jaccard или cosine similarity.
- Векторные представления (word embeddings): cosine similarity между векторными представлениями предложений или документов.
- Регулярные выражения и критерии для исключения цитат или общих фраз (boilerplate removal).
- Sequence-to-sequence и модели типа BERT для семантического сравнения; использование sentence-transformers для извлечения эмбеддингов.
- Индексация больших корпусов с помощью векторных поисков (FAISS, Annoy) для быстрого поиска похожих документов.
Мини-методология для повышения качества детектора
- Сбор корпуса: собрать репрезентативную выборку источников, где возможен плагиат.
- Предобработка: нормализация, удаление метаданных и boilerplate.
- Модель сходства: начать с n-gram + Jaccard, затем перейти на векторные эмбеддинги для семантики.
- Валидация: создать тестовый набор пар с разными типами изменений (копирование/перефразирование/пересказ).
- Порог и интерфейс: выставить пороги для предупреждений и для обязательного ручного анализа.
Критерии приёмки
- Приложение загружает и отображает текстовые файлы (.txt) корректно;
- Кнопка Compare возвращает числовой процент сходства и обновляет текст с подсветкой;
- Подсветка равных фрагментов соответствует opcodes от SequenceMatcher;
- Обработка ошибок: при попытке загрузить несуществующий файл приложение не падает;
- Документация: краткое руководство внутри репозитория о запуске и ограничениях.
Тестовые случаи и примеры приёма
- Полное совпадение: два одинаковых файла → 100% и полная подсветка.
- Частичное совпадение: файл A + файл B, где добавлена одна строка → подсветка совпадающих фрагментов, процент снижен.
- Перефразирование: тот же смысл, иные слова → низкий процент у difflib; при использовании эмбеддингов — выше.
- Пустые поля: оба поля пусты → процент 0 или сообщение об отсутствии данных (зависит от реализации).
- Некорректная кодировка файла → приложение должно не падать, лучше показывать предупреждение.
Роли и чек-лист для развертывания
Разработчик:
- добавить логирование ошибок чтения файлов;
- поддержать кодировки UTF-8 и Windows-1251 при чтении;
- добавить тесты на opcodes.
Тестировщик:
- проверить граничные случаи (пустой файл, очень большой файл, бинарный файл с .txt расширением);
- проверить корректность подсветки для разных шифтов.
Оператор/администратор:
- следить за правами доступа к папкам с исходниками;
- по необходимости настроить ограничение максимального размера файла на загрузку.
Риск-матрица и рекомендации по смягчению
- Ложноположительные срабатывания (false positives): поставить порог и требовать ручной проверки;
- Ложноотрицательные (false negatives): комбинировать синтаксические и семантические методы;
- Утечка конфиденциальных данных: не хранить переданные документы дольше, чем требуется; реализовать удаление.
Конкретные идеи для локализации и масштабирования
- Для русского языка полезна лемматизация (pymorphy2) и нормализация кириллицы;
- Для наставнических систем (университеты) добавить интеграцию с LMS через API;
- Для больших корпусов использовать инкрементальную индексацию и векторный поиск.
Краткое руководство по безопасности и приватности
- Не сохраняйте пользовательские тексты на сервере без согласия;
- Логи не должны содержать фрагментов текста пользователей;
- Предусмотрите возможность удаления всех загруженных материалов пользователем по требованию.
Сравнение подходов (кратко)
- difflib (символьный): прост в реализации, точен для буквального копирования, слаб при перефразировании.
- n-gram + Jaccard/cosine: лучше устойчив к вставкам/удалениям, но чувствителен к перестановкам.
- эмбеддинги + cosine: позволяет улавливать семантику, требует моделей и вычислительных ресурсов.
Рекомендации по производительности
- Ограничьте максимальный размер загружаемых файлов при работе в UI;
- Для пакетной проверки документов используйте потоковую обработку и индексацию;
- Для семантических методов применяйте батчинг эмбеддингов и ANN-поиск.
Примерный план развития (roadmap)
- Версия 0.1: базовый GUI + difflib (этот пример);
- Версия 0.2: предобработка и поддержка нескольких кодировок;
- Версия 0.3: n-gram анализ и пороговые показатели;
- Версия 1.0: семантические эмбеддинги и поиск по индексу;
- Версия 2.0: интеграции с LMS и масштабируемая backend-часть.
Заключение
Этот пример демонстрирует фундаментальную идею процедуры сравнения текстов: показать, где тексты совпадают, и дать числовую оценку сходства. Для практического детектора плагиата стоит комбинировать несколько подходов: синтаксический (difflib, n-gram) и семантический (эмбеддинги), а также внедрять процессы ручной верификации для спорных случаев.
Важно: даже самые продвинутые алгоритмы не заменят экспертной оценки, особенно в академических и юридически значимых контекстах.
Ключевые шаги на старте: собрать примеры, реализовать предобработку и провести валидацию на наборах реальных кейсов.
Похожие материалы
Обновление Apple TV до tvOS 11 — пошагово
Частный DNS на Android: настройка и зачем нужен
Распознать поддельные отзывы на Amazon
Отключение Samsung Calendar на Galaxy
Как сделать Google Chrome браузером по умолчанию