Нейросеть для перевода PDF в Excel: таблицы без ручного копирования
Разбираем рабочий конвейер: достаём таблицу из PDF, просим модель вернуть JSON, пишем в xlsx и сверяем итоги, чтобы не поймать тихую ошибку.
4 мин · · автоматизация · разработка
Если у вас десятки счетов, выписок или прайс-листов в PDF, ручное копирование в таблицу съедает вечер. Нейросеть для перевода PDF в Excel решает это за несколько строк кода, но только если не доверять ей вслепую: модель может пропустить строку или перепутать запятую в сумме. Ниже конвейер, в котором результат проверяется арифметикой, и расчёт стоимости на страницу.
Два вида PDF и почему это важно
Сначала определите, что лежит внутри файла.
- Текстовый слой есть. Текст в документе выделяется мышью. Его можно достать программно и отправить модели как обычную строку. Это дешевле и точнее.
- Это скан или фото. Выделить текст нельзя. Страницу нужно превратить в картинку и отправить модели, которая принимает изображения на вход.
Быстрая проверка в коде: если extract_text() вернул пустую строку или мусор, страница скорее всего является картинкой.
Шаг 1. Достаём текст или картинку
Для текстового слоя подойдёт pdfplumber, для растеризации страниц PyMuPDF:
pip install pdfplumber pymupdf openpyxl openaiimport base64
import pdfplumber
import fitz # PyMuPDF
def page_text(path: str, page_no: int) -> str:
with pdfplumber.open(path) as pdf:
return pdf.pages[page_no].extract_text() or ""
def page_png_b64(path: str, page_no: int, dpi: int = 150) -> str:
doc = fitz.open(path)
pix = doc[page_no].get_pixmap(dpi=dpi)
return base64.b64encode(pix.tobytes("png")).decode("ascii")Разрешение 150 dpi обычно хватает для печатного текста. Для мелкого шрифта поднимайте, но помните: больше картинка, больше входных токенов.
Шаг 2. Просим модель вернуть JSON
Главное правило: не просите «сделай таблицу», просите строго заданную структуру. Так результат легко проверить кодом.
import json
from openai import OpenAI
client = OpenAI(base_url="https://api.zukko.pro/v1", api_key="fetch_ваш_ключ")
PROMPT = """Извлеки из страницы таблицу позиций. Верни только JSON без пояснений:
{"rows": [{"name": "...", "qty": 0, "price": 0.0, "total": 0.0}],
"document_total": 0.0}
Числа пиши как числа с точкой, без пробелов и символа валюты.
Если значения нет в документе, ставь null. Ничего не придумывай."""
FENCE = "`" * 3 # обрамление markdown, если модель его добавила
Для текстового пути взята deepseek-v4.1-flash: она без картинок, зато самая дешёвая в нашей таблице. Для сканов нужна модель с картинками на вход, например claude-sonnet-5-5 или gpt-6.1-sol. Подробнее про форматы запросов в документации по chat completions.
Шаг 3. Проверяем суммы до записи в файл
Это самый важный шаг. Модель могла ошибиться в одной цифре, а таблица выглядит убедительно.
from decimal import Decimal
def validate(data: dict) -> list[str]:
problems = []
rows = data["rows"]
for i, row in enumerate(rows):
if None in (row["qty"], row["price"], row["total"]):
problems.append(f"строка {i}: пустое значение")
continue
calc = Decimal(str(row["qty"])) * Decimal(str(row["price"]))
if abs(calc - Decimal(str(row["total"]))) > Decimal("0.01"):
problems.append(f"строка {i}: {calc} != {row['total']}")
s = sum(Decimal(str(r["total"])) for r in rows if r["total"] is not None)
doc_total = data.get("document_total")
if doc_total is not None and abs(s - Decimal(str(doc_total))) > Decimal("0.01"):
problems.append(f"сумма строк {s} не равна итогу {doc_total}")
return problemsДве проверки ловят большинство ошибок: количество на цену равно сумме строки, а сумма строк равна итогу документа. Страницы с замечаниями отправляйте на повторный проход (например, на более сильную модель) или в ручную проверку.
Шаг 4. Пишем xlsx через openpyxl
from openpyxl import Workbook
def write_xlsx(pages: list[dict], out_path: str) -> None:
wb = Workbook()
ws = wb.active
ws.append(["Наименование", "Кол-во", "Цена", "Сумма"])
for data in pages:
for r in data["rows"]:
ws.append([r["name"], r["qty"], r["price"], r["total"]])
last = ws.max_row
ws.append(["Итого", None, None, f"=SUM(D2:D{last})"])
wb.save(out_path)Формула SUM в конце даёт второй независимый контроль: сравните её результат с итогом из PDF глазами.
Сколько это стоит
Цены на 18 октября 2026, долларов за 1M токенов. Формула: вход × цена входа + выход × цена выхода.
Текстовая страница, deepseek-v4.1-flash (0.013 / 0.038). Допустим, 1500 токенов на входе и 800 на выходе:
1500 × 0.013 / 1 000 000 + 800 × 0.038 / 1 000 000 = 0.0000195 + 0.0000304 ≈ $0.00005
На 100 страниц выходит около $0.005.
Страница-картинка, claude-sonnet-5-5 (0.4 / 2). Сколько токенов займёт изображение, зависит от размера, поэтому возьмём допущение 1600 токенов входа и 800 выхода:
1600 × 0.4 / 1 000 000 + 800 × 2 / 1 000 000 = 0.00064 + 0.0016 = $0.00224
На 100 страниц около $0.22. Реальное число токенов на картинку смотрите в поле usage ответа и замерьте на трёх-пяти своих страницах, прежде чем запускать пачку. Актуальные цены: GET https://api.zukko.pro/v1/models или раздел «Модели» в кабинете.
Практические советы
- Не отправляйте в модель персональные данные, если они не нужны для задачи: шаблоны часто содержат ФИО и реквизиты.
- Многостраничные таблицы режьте по страницам и склеивайте в коде, а не просите модель обработать 80 страниц сразу.
- Сложные многоуровневые шапки и объединённые ячейки лучше описать в промпте словами: какие колонки есть и в каком порядке.
- Разбор документов других типов описан в статьях про обработку PDF через vision и про извлечение данных из документов.
Ключ создаётся в кабинете, код выше работает с ним без изменений. Базовый пример на Python есть в документации.
Частые вопросы
Можно ли обойтись без кода?
Да, для одного файла: загрузите страницы в чат в браузере и попросите таблицу. Но для регулярной работы скрипт с проверкой сумм надёжнее.
Что делать со сканами плохого качества?
Поднимите dpi, обрежьте поля и повторите. Если модель отвечает null, это честнее, чем выдуманное значение: такие строки проверьте вручную.
Какая модель точнее?
Мы не публикуем собственных замеров качества. Сравните две-три модели на десяти своих страницах и посчитайте, на какой меньше расхождений в проверке сумм.
Сколько страниц можно обработать за раз?
Страницы независимы, поэтому их можно отправлять параллельно. Ограничения зависят от лимитов ключа и провайдера модели; ошибку 429 обрабатывайте повтором с паузой.
zukko
Платите в 25×
меньше
за Claude Code & ChatGPT
Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.
Похожие статьи
- Перевод субтитров нейросетью: SRT без сбитых таймкодовГлавный риск при переводе SRT нейросетью — сломанная структура файла. Показываем схему, где таймкоды не попадают к модели, и скрипт на Python с проверкой.
- Редактирование текстов нейросетью: корректура, стиль и сокращениеРазные виды редактуры требуют разных промптов. Показываем формулировки для корректуры, стиля и сокращения, как получать правки с объяснением и не терять авторский голос.
- Нейросеть для написания скриптов: Bash, PowerShell и PythonМодель быстро пишет скрипты для рутины, но запускать их вслепую нельзя. Показываем, как ставить задачу, требовать безопасный режим dry-run и проверять результат до запуска.
- Dify в Docker: установка, первичная настройка и подключение моделейСтавим Dify через Docker Compose, создаём администратора и подключаем модели Claude, GPT и DeepSeek по одному ключу. Отдельно — что делать, если контейнеры не стартуют.