Почему ваш скрипт с OCRmyPDF падает уже на старте?
Вы, конечно, не первый, кто попытался «переименовать» сканированную кучу PDF в удобные для поиска документы. Но почему же каждый раз возникает «ImportError: Pillow is incompatible» или «MissingDependencyError»? Давайте разберёмся, какие подводные камни таятся в вашем коде и как их победить без бесконечных переустановок.
Краткий разбор проблемного участка
В начале скрипта происходит «чистка» импортов:
- _purge() удаляет модули
PILиocrmypdfизsys.modules. Это выглядит как попытка «обновить» их «на лету», но в реальности только ломает кеш и приводит к повторным импортам с несовместимыми версиями. - Дальше _load_ocrmypdf() сразу после _purge() пытается импортировать
ocrmypdf. Если в системе уже установлен Pillow другой версии, импорт завершается ошибкой, которую скрипт пытается «исцелить» распечаткой сообщения о переустановке. - Проблема в том, что сообщение «Repairing an incompatible Pillow…» выводится, но процесс переустановки не происходит автоматически – пользователь остаётся с тем же багом.
Что делает остальной код?
Основная часть – это набор вспомогательных функций для генерации «сканированных» PDF и запуска OCR:
- render_page() рисует текст на изображении, добавляет шум и наклон.
- build_scanned_pdf() собирает такие изображения в PDF‑файл.
- do_ocr() обёртка над
ocrmypdf.ocr(), отключающая прогресс‑бар и измеряющая время.
Всё это работает, пока ocrmypdf успешно импортирован и его зависимости (Pillow, img2pdf, pdfminer.six и пр.) удовлетворяют требуемым версиям.
Как избавиться от «постоянных переустановок»?
Самый надёжный путь – фиксировать версии зависимостей и управлять ими через виртуальное окружение.
- Создайте venv или используйте conda‑окружение.
- Запишите точные версии в
requirements.txt:
ocrmypdf==15.3.0
pillow==10.2.0
img2pdf==0.4.4
pdfminer.six==20231113
- Установите их одной командой:
pip install -r requirements.txt. - Уберите функцию _purge() – она лишь усложняет жизнь.
- Импортируйте
ocrmypdfединожды в начале скрипта.
Пример «чистого» начала скрипта
Вот как может выглядеть стартовый блок без лишних трюков:
import sys
import time
import re
import os
from pathlib import Path
import textwrap
import ocrmypdf
from ocrmypdf.exceptions import (
ExitCode, PriorOcrFoundError, EncryptedPdfError,
MissingDependencyError, TaggedPDFError, DigitalSignatureError,
DpiError, InputFileError, UnsupportedImageFormatError,
)
import img2pdf
from PIL import Image, ImageDraw, ImageFont, ImageFilter
Теперь ваш скрипт будет падать только в случае реальной ошибки (например, повреждённый PDF), а не из‑за «плохой» версии Pillow.
Обработка типичных ошибок OCRmyPDF
Когда всё уже запущено, полезно отлавливать ошибки и давать пользователю понятные рекомендации:
- MissingDependencyError – проверьте, что
ghostscriptиtesseractустановлены в системе. - EncryptedPdfError – PDF защищён паролем. Добавьте параметр
owner_passwordв вызовocrmypdf.ocr(). - PriorOcrFoundError – в документе уже есть слой текста. Включите
force_ocr=Trueили используйте--skip-text. - DpiError – слишком низкое разрешение. Установите
--output-type pdfaи--image-dpi 300.
Полный рабочий пример функции do_ocr
def do_ocr(input_file: Path, output_file: Path, **kwargs):
"""Запуск OCRmyPDF с таймингом и обработкой типовых исключений."""
kwargs.setdefault("progress_bar", False)
start = time.perf_counter()
try:
rc = ocrmypdf.ocr(str(input_file), str(output_file), **kwargs)
elapsed = time.perf_counter() - start
print(f"OCR завершён за {elapsed:.2f} с, код возврата {rc}.")
return rc
except MissingDependencyError as e:
raise SystemExit(f"Недостаёт зависимости: {e}")
except (EncryptedPdfError, PriorOcrFoundError) as e:
raise SystemExit(f"PDF не может быть обработан: {e}")
Таким образом, ваш скрипт будет «говорить» на человеческом языке, а не бросать стек‑трейсы, которые пугают даже опытных разработчиков.
Где искать ответы и свежие версии?
- Официальный репозиторий OCRmyPDF на GitHub.
- Документация по параметрам ocrmypdf.readthedocs.io.
- Список совместимых зависимостей в requirements.txt проекта.
Итого: что нужно сделать прямо сейчас
- Создайте виртуальное окружение.
- Зафиксируйте версии зависимостей в
requirements.txt. - Уберите
_purge()и «принудительный» импорт. - Обновите импорт в начале скрипта и добавьте лёгкую обработку ошибок.
- Проверьте наличие системных утилит
ghostscriptиtesseract.
После этих шагов ваш OCR‑pipeline будет работать как часы, а вы сможете спокойно отдыхать, зная, что «плохой Pillow» больше не будет вас мучить.



















