ТранскрибаторТранскрибатор
Практическая инструкция

SQLite FTS5: локальный поиск по папке расшифровок через Python

Как собрать временный полнотекстовый индекс TXT, найти фразу в интервью и вывести контекст через SQLite FTS5. Рабочий пример, проверка русских слов и ограничения.

Транскрибатор
ЗАПИСЬПЛАН РАБОТЫСоберите папку TXTСоздайте индекс FTS5Найдите фразу и контекстПроверить результатОТ СЛОВ К РЕЗУЛЬТАТУ
Работа с реальной записьюНаглядный учебный примерШаблон для копирования
1Соберите папку TXT2Создайте индекс FTS53Найдите фразу иконтекст
Поиск выполняется по текстовым копиям; результат возвращает название документа и контекст совпадения.

Подготовьте текстовый архив

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

Получите текст через преобразование аудио в текст и сохраните отдельный TXT для каждой записи. Используйте понятные имена: например, I12-2026-10-01.txt. Сохраняйте таймкоды в самом тексте, если хотите потом быстро открыть нужную реплику в аудио.

Создайте папку transcripts рядом со скриптом. В пример входят только файлы верхнего уровня с расширением .txt. DOCX, PDF и вложенные папки этот код не обрабатывает. Если Вы ведёте расшифровку в JSONL, сначала выделите текстовую копию; не переименовывайте JSONL в TXT в надежде убрать служебные поля.

Запустите поиск на небольшой папке

Сохраните код в search_transcripts.py и выполните python search_transcripts.py из его папки. Он создаёт временный индекс, добавляет тексты и ищет точную последовательность слов «срок поставки». Для другого запроса измените значение query. Кавычки внутри этой строки относятся к синтаксису полнотекстовой фразы.

from pathlib import Path
import sqlite3

folder = Path("transcripts")
files = sorted(folder.glob("*.txt"))
query = '"срок поставки"'

with sqlite3.connect(":memory:") as con:
    con.execute("CREATE VIRTUAL TABLE docs USING fts5(filename UNINDEXED, body)")
    for path in files:
        text = path.read_text(encoding="utf-8-sig")
        con.execute("INSERT INTO docs(filename, body) VALUES (?, ?)",
                    (path.name, text))
    sql = """SELECT filename, snippet(docs, 1, '[', ']', ' … ', 24)
             FROM docs WHERE docs MATCH ? ORDER BY bm25(docs) LIMIT 20"""
    for filename, fragment in con.execute(sql, (query,)):
        print(filename)
        print(fragment)
        print()

Значения текста и запроса передаются в SQL отдельными параметрами. Не собирайте INSERT путём вставки содержимого интервью в строку SQL: кавычки в репликах способны сломать такой запрос. Колонка filename не индексируется — она служит подписью результата.

Проверьте работу на двух учебных файлах. В первом напишите «Обсудили срок поставки оборудования», во втором — «Обсудили бюджет проекта». В выводе должна появиться первая запись с выделенной фразой. Это проверка механики на известном содержимом, а не замер скорости большого архива.

Проверьте, что означает найденное совпадение

Фраза в кавычках ищет последовательность токенов, а не произвольное побуквенное совпадение вместе со всеми знаками пунктуации. Если нужен поиск буквального текста в исходных файлах, используйте ripgrep с контекстом. Здесь задача другая: выдача по словам с короткими фрагментами.

Попробуйте отдельно запросы «поставка» и «поставки» на своих примерах. Не предполагайте, что русские окончания будут автоматически сведены к одному слову. Для исследовательского разбора заранее составьте список нужных словоформ и проверяйте несколько запросов.

Порядок выдачи помогает выбрать, что открыть первым; он не показывает важность встречи или достоверность высказывания. Откройте TXT, прочитайте соседние реплики, затем вернитесь к записи по таймкоду. Короткий фрагмент может не включать отрицание или оговорку, находящуюся в предыдущем предложении.

Разберите пустой результат и ошибки

Если список пустой, проверьте, найдены ли файлы: временно добавьте print(len(files)) после строки с glob. Ноль означает проблему пути, уровня папки или расширения. Если файлы есть, вставьте в query слово, которое точно встречается в одном из них.

Сообщение no such module: fts5 означает, что используемая сборка SQLite не предоставляет этот модуль. Узнайте версию через sqlite3.sqlite_version и выберите сборку Python/SQLite с поддержкой FTS5. Не пытайтесь исправить это заменой слов запроса.

Ошибка декодирования требует проверки настоящей кодировки TXT. Не включайте игнорирование ошибочных символов: тогда индекс может получить искажённые слова. Временная база в памяти исчезает после работы программы, и следующий запуск заново читает папку. Это удобно для первого испытания; для постоянного большого архива отдельно продумайте обновление индекса и сохранение версий.

Учебный пример

Учебная проверка на двух интервью

Файлы и реплики ниже вымышлены. Сначала подтвердите ожидаемый результат на таком простом наборе.

ФайлТекстОжидание
I12.txtОбсудили срок поставки оборудованияНайдётся точная фраза
I13.txtОбсудили бюджет проектаНе найдётся эта фраза
I14.txtПоставки обсуждали отдельноПроверить другой запрос
ДокументыОдин TXT на записьПонятное имя файлаЗапросФраза «срок поставки»Известный тестРезультатИмя и отрывокПроверка по аудио
Выдача даёт адрес документа; полноту смысла проверяют в исходной реплике.

После учебной проверки добавьте несколько собственных текстов и найдите известную цитату. Полезность поиска оценивайте по тому, удаётся ли вернуться к нужной записи.

Карточка испытания поиска

Рабочая заготовкаВыделите и скопируйте
Папка TXT: [путь]
Найдено файлов: [количество]
Кодировка: [UTF-8]
Запрос FTS5: [строка]
Контрольный файл: [имя]
Ожидаемая цитата: [текст]
Результат: [совпало / не совпало]
Проверенный таймкод: [время]

Частые вопросы

Файлы отправляются на сервер?

Этот пример читает местную папку и строит индекс в памяти процесса. Сетевых обращений в коде нет.

Можно положить в папку DOCX?

Нет. Пример читает TXT. Подготовьте текстовую копию документа и сохраните её в проверенной кодировке.

Почему «поставка» не находит «поставки»?

Не рассчитывайте на автоматическую обработку русских словоформ. Проверяйте варианты запроса на известном тексте.

Почему после запуска нет файла базы?

В примере используется база :memory:. Это временный индекс, который создаётся заново при каждом запуске.