Перейти к содержимому

Обзор Python SDK

NextPDF SDK для Python предназначен для приложений на Python, которым нужно извлечение из Portable Document Format (PDF) с привязкой к источникам. Он возвращает структурированные блоки с данными для цитирования: индекс страницы, уверенность, необязательные ограничивающие рамки и идентификаторы семантических узлов, если исходный PDF предоставляет такую структуру.

Используйте SDK, когда вашему конвейеру нужно отвечать на вопросы вроде “С какой страницы взят этот текст?”, “Какая таблица подтверждает это значение?” или “Что изменилось между этими двумя PDF-файлами?”, а не сводить извлечение из PDF к обезличенному простому тексту.

  • Синхронный клиент NextPDF для скриптов, пакетных заданий и блокнотов.
  • Асинхронный клиент AsyncNextPDF для asyncio, FastAPI и других асинхронных сред выполнения.
  • Интерфейс командной строки (CLI) nextpdf для разового извлечения из файла по указанному пути или из стандартного ввода с записью в стандартный вывод или в файл.
  • Необязательный сервер Model Context Protocol (MCP), благодаря которому агенты искусственного интеллекта (AI) могут напрямую вызывать инструменты извлечения из PDF.
  • Удалённый бэкенд для использования в продакшене с NextPDF Connect.
  • Локальный бэкенд для офлайн-извлечения через pypdf, доступный только из библиотеки.

Удалённый бэкенд отправляет байты PDF на сервер NextPDF Connect. Это рекомендуемый вариант для продакшена, поскольку он централизует логику извлечения, аутентификацию, квоты и операционное управление.

Локальный бэкенд работает внутри процесса Python и читает PDF-файлы через pypdf. Он полезен для офлайн-разработки и PDF с тегами, но не предоставляет точные ограничивающие рамки и использует эвристическое извлечение по абзацам для PDF без тегов. Локальный бэкенд доступен только из библиотеки: чтобы использовать его, внедрите LocalBackend в AsyncNextPDF. CLI nextpdf и сервер MCP использовать его не могут. Полное сравнение см. в разделе Матрица выбора бэкенда.

SDK не выполняет оптическое распознавание символов (OCR). Для отсканированных PDF-файлов или PDF-файлов, состоящих только из изображений, требуется этап OCR, прежде чем NextPDF сможет извлечь встроенный текст. Сложные макеты, перекрывающийся текст и нестандартные генераторы PDF также могут снижать качество извлечения.

CLI nextpdf работает только в удалённом режиме и не является потоковым интерфейсом. Каждая команда считывает весь PDF в память (из файла по указанному пути или из стандартного ввода), отправляет его на сервер NextPDF Connect, формирует полный результат в памяти и сериализует его за одну запись. Вывод можно перенаправить в файл с помощью --output (или -o) либо в стандартный вывод, но результат полностью буферизуется, а не формируется постепенно. CLI не может использовать локальный бэкенд pypdf.

Выбор клиента: синхронный или асинхронный

Заголовок раздела «Выбор клиента: синхронный или асинхронный»

Оба клиента используют одно и то же пространство имён методов ast для операций с абстрактным синтаксическим деревом (AST) и возвращают одни и те же модели Pydantic. Они различаются только моделью параллелизма.

Ваш контекстИспользуйтеПочему
Скрипты и пакетные заданияNextPDF (синхронный)Линейный поток управления; нет цикла событий, которым нужно управлять.
Блокноты JupyterNextPDF (синхронный)run_sync обнаруживает работающий цикл событий и передаёт выполнение в рабочий поток, поэтому блокирующие вызовы работают внутри ячеек.
Интерфейс nextpdf CLINextPDF (синхронный, внутренний)CLI создаёт синхронный клиент за вас.
asyncio сервисыAsyncNextPDFНативный await; без передачи между потоками.
FastAPI, Starlette, асинхронный интерфейс шлюза сервера (Asynchronous Server Gateway Interface, ASGI)AsyncNextPDFИспользует тот же цикл событий запроса и тот же пул соединений.
Высокопараллельная веерная обработкаAsyncNextPDFВыполняйте множество операций извлечения параллельно с помощью asyncio.gather через один клиент с пулом соединений.

NextPDF оборачивает внутренний AsyncNextPDF и выполняет каждый вызов через run_sync. В уже работающем цикле событий, например в блокноте, run_sync передаёт сопрограмму в однопоточный рабочий поток с собственным циклом, поэтому вы не сталкиваетесь с ошибкой вложенного asyncio.run. В сервисе asyncio или ASGI вызывайте AsyncNextPDF напрямую, чтобы не тратить ресурсы на такую передачу между потоками при каждом вызове.

Асинхронный клиент владеет httpx.AsyncClient для пула соединений, поэтому повторно используйте один экземпляр AsyncNextPDF и закрывайте его один раз. Синхронный клиент NextPDF не предоставляет метод close(). Для долгоживущих асинхронных нагрузок предпочитайте AsyncNextPDF и явно управляйте его жизненным циклом (см. Операционная модель для продакшена).

Бэкенд реализует протокол PdfBackend. Удалённый бэкенд (RemoteBackend) выбирается автоматически, когда вы передаёте base_url и api_key. Локальный бэкенд (LocalBackend) нужно внедрять явно через параметр backend= у AsyncNextPDF; он не экспортируется из пакета верхнего уровня nextpdf и недоступен из CLI или сервера MCP.

ВозможностьУдалённый (RemoteBackend)Локальный (LocalBackend)
Выбирается черезbase_url + api_keyAsyncNextPDF(backend=LocalBackend(...))
СетьNextPDF Connect по Hypertext Transfer Protocol Secure (HTTPS)Отсутствует; работает внутри процесса
Аутентификация, квоты, учётЦентрализованно на сервереОтсутствует
Наблюдаемость и операционные средства управленияНа стороне сервераОтсутствует
Извлечение из PDF с тегами (StructTree)ДаДа
Извлечение из PDF без теговСерверный движокЭвристическое разбиение на абзацы, уверенность 0.5
Ограничивающие рамкиДа (когда сервер их предоставляет)Нет (bbox равно None)
Извлечение таблиц из PDF без теговСерверный движокНе возвращает таблиц
Доступность из CLI / сервера MCPДаНет (только как библиотека)
Рекомендуется дляПродакшенОфлайн-разработка, тесты с PDF с тегами

Используйте удалённый бэкенд для продакшена, поскольку это единственный путь с централизованной аутентификацией, контролем квот, учётом и наблюдаемостью. Используйте локальный бэкенд для офлайн-разработки и тестов с PDF с тегами, учитывая эвристические результаты, отсутствие ограничивающих рамок и отсутствие таблиц для PDF без тегов.

"""Inject the local backend for offline, library-only extraction."""
from nextpdf import AsyncNextPDF
from nextpdf.backends.local import LocalBackend
async def extract_offline(pdf_bytes: bytes) -> None:
"""Extract cited text without a NextPDF Connect server."""
async with AsyncNextPDF(backend=LocalBackend()) as client:
blocks = await client.ast.extract_cited_text(pdf_bytes)
for block in blocks:
# Heuristic blocks on untagged PDFs report confidence 0.5.
print(block.citation.confidence, block.text)

В продакшене используйте удалённый бэкенд с NextPDF Connect. Приведённые ниже шаблоны охватывают повторное использование клиента, обработку ошибок, повторные попытки, обработку квот и тайм-ауты. Все показанные здесь символы есть в SDK. SDK не выполняет повторные попытки автоматически, поэтому цикл повторных попыток — ваша ответственность.

Повторно используйте клиент и пул соединений

Заголовок раздела «Повторно используйте клиент и пул соединений»

RemoteBackend поддерживает один постоянный httpx.AsyncClient для пула соединений. Создайте AsyncNextPDF один раз, разделяйте его между всеми запросами и закрывайте при завершении работы. Не создавайте клиент для каждого запроса.

"""Reuse one pooled async client for the lifetime of the process."""
import asyncio
import os
from pathlib import Path
from nextpdf import AsyncNextPDF
async def main() -> None:
"""Run several extractions over a single pooled client."""
base_url = os.environ["NEXTPDF_BASE_URL"]
# Treat the API key as a secret; read it from the environment, never hard-code it.
api_key = os.environ["NEXTPDF_API_KEY"]
async with AsyncNextPDF(base_url=base_url, api_key=api_key) as client:
pdf_paths = (Path("a.pdf"), Path("b.pdf"), Path("c.pdf"))
tasks = [
client.ast.get_document_ast(path.read_bytes())
for path in pdf_paths
]
documents = await asyncio.gather(*tasks)
for document in documents:
print(document.page_count, document.estimated_tokens)
if __name__ == "__main__":
asyncio.run(main())

Асинхронный контекстный менеджер при выходе вызывает close() и тем самым закрывает базовый транспорт. Без контекстного менеджера вызывайте await client.close() самостоятельно.

Обрабатывайте ошибки с помощью иерархии исключений

Заголовок раздела «Обрабатывайте ошибки с помощью иерархии исключений»

SDK выбрасывает типизированную иерархию исключений. Все ошибки наследуются от NextPDFError; сбои на уровне Hypertext Transfer Protocol (HTTP) наследуются от NextPDFAPIError и содержат status_code. Перехватывайте конкретные типы, на которые можете отреагировать, и используйте базовый тип как резервный вариант.

ИсключениеВыбрасывается, когдаКлючевые атрибуты
NextPDFErrorБазовый тип для всех ошибок SDKstatus_code
NextPDFAPIErrorЛюбая ошибка HTTP от сервераstatus_code, error_code
NextPDFLicenseErrorHTTP 402; функция требует более высокого тарифа на сервереstatus_code (402)
QuotaExceededErrorHTTP 429; превышен лимит частоты запросов или квотаretry_after
AstNoStructTreeErrorHTTP 422; PDF без тегов при выключенном эвристическом режимеstatus_code (422)
AstBuildTimeoutErrorHTTP 504; истекло время сборки ASTstatus_code (504)
"""Map SDK exceptions to caller-facing outcomes."""
from nextpdf import (
AstBuildTimeoutError,
AstNoStructTreeError,
AsyncNextPDF,
NextPDFAPIError,
NextPDFError,
NextPDFLicenseError,
QuotaExceededError,
)
async def safe_extract(client: AsyncNextPDF, pdf_bytes: bytes) -> str:
"""Extract text, translating known failures into a stable status string."""
try:
blocks = await client.ast.extract_cited_text(pdf_bytes)
except QuotaExceededError as exc:
# exc.retry_after holds the server Retry-After value in seconds, or None.
return f"rate-limited; retry after {exc.retry_after}s"
except NextPDFLicenseError:
return "feature requires a higher server tier"
except AstNoStructTreeError:
return "untagged PDF; enable heuristic mode or use a tagged PDF"
except AstBuildTimeoutError:
return "build timed out; reduce the page range"
except NextPDFAPIError as exc:
return f"server error (status {exc.status_code})"
except NextPDFError:
return "extraction failed"
return "\n".join(block.text for block in blocks)

Повторяйте запросы при временных сбоях с задержкой

Заголовок раздела «Повторяйте запросы при временных сбоях с задержкой»

SDK не выполняет повторные попытки автоматически. Оберните вызовы в собственный цикл, который повторяет попытки при временных HTTP-сбоях и учитывает серверное значение Retry-After, которое QuotaExceededError предоставляет как retry_after (целое число секунд или None). Используйте экспоненциальную задержку для других временных HTTP-статусов и не повторяйте попытки при NextPDFLicenseError.

"""Retry transient failures with exponential backoff and Retry-After support."""
import asyncio
from collections.abc import Awaitable, Callable
from typing import TypeVar
from nextpdf import NextPDFAPIError, QuotaExceededError
_RETRYABLE_STATUS = frozenset({500, 502, 503, 504})
_T = TypeVar("_T")
async def with_retry(
coro_factory: Callable[[], Awaitable[_T]],
*,
max_attempts: int = 4,
) -> _T:
"""Call coro_factory() with bounded retries on transient server errors.
Args:
coro_factory: A zero-argument callable returning a fresh awaitable.
max_attempts: Maximum number of attempts before giving up.
Returns:
The awaited result of the first successful attempt.
Raises:
NextPDFAPIError: When all attempts fail or the error is not retryable.
"""
delay = 1.0
for attempt in range(1, max_attempts + 1):
try:
return await coro_factory()
except QuotaExceededError as exc:
if attempt == max_attempts:
raise
await asyncio.sleep(exc.retry_after if exc.retry_after is not None else delay)
delay *= 2.0
except NextPDFAPIError as exc:
if attempt == max_attempts or exc.status_code not in _RETRYABLE_STATUS:
raise
await asyncio.sleep(delay)
delay *= 2.0
raise RuntimeError("unreachable")

Управляйте квотами, лимитами частоты запросов и тайм-аутами

Заголовок раздела «Управляйте квотами, лимитами частоты запросов и тайм-аутами»

Квоты и лимиты частоты запросов контролируются на сервере. При HTTP 429 SDK выбрасывает QuotaExceededError и разбирает заголовок Retry-After в retry_after. Удалённый бэкенд также выдаёт заголовки X-RateLimit-* в ответах на отрисовку, поэтому вы можете заранее ограничивать нагрузку, прежде чем достигнете жёсткого лимита.

Для тайм-аутов запросов задано фиксированное значение по умолчанию: всего 60 секунд при тайм-ауте подключения 10 секунд (httpx.Timeout(60.0, connect=10.0)). Чтобы ограничить длительные сборки AST, сузьте объём работы с помощью page_range_start, page_range_end или token_budget, а не полагайтесь только на тайм-аут; при слишком длительной сборке возвращается AstBuildTimeoutError (HTTP 504).

Пакетный обработчик читает PDF-файлы, извлекает текст с цитированием и записывает структурированный вывод. Повторно используйте один клиент с пулом соединений, ограничьте параллелизм семафором и примените приведённый выше вспомогательный механизм повторных попыток.

"""Batch-extract a directory of PDFs over one pooled async client."""
import asyncio
import os
from pathlib import Path
from nextpdf import AsyncNextPDF
async def run_batch(input_dir: Path, concurrency: int = 8) -> None:
"""Extract cited text for every PDF in input_dir, bounded by concurrency."""
semaphore = asyncio.Semaphore(concurrency)
async def worker(client: AsyncNextPDF, path: Path) -> None:
async with semaphore:
blocks = await client.ast.extract_cited_text(path.read_bytes())
out = path.with_suffix(".txt")
out.write_text("\n".join(b.text for b in blocks), encoding="utf-8")
async with AsyncNextPDF(
base_url=os.environ["NEXTPDF_BASE_URL"],
api_key=os.environ["NEXTPDF_API_KEY"],
) as client:
await asyncio.gather(*(worker(client, p) for p in input_dir.glob("*.pdf")))

Сервис FastAPI совместно использует один AsyncNextPDF для всех запросов в течение жизненного цикла приложения, поэтому каждый запрос переиспользует пул соединений. Считывайте учётные данные из окружения и относитесь к ключу API как к секрету.

"""FastAPI service that shares one pooled NextPDF client across requests."""
import os
from contextlib import asynccontextmanager
from fastapi import FastAPI, UploadFile
from nextpdf import AsyncNextPDF
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Create the pooled client on startup and close it on shutdown."""
app.state.client = AsyncNextPDF(
base_url=os.environ["NEXTPDF_BASE_URL"],
api_key=os.environ["NEXTPDF_API_KEY"],
)
try:
yield
finally:
await app.state.client.close()
app = FastAPI(lifespan=lifespan)
@app.post("/extract")
async def extract(file: UploadFile) -> dict[str, list[str]]:
"""Return cited text blocks for an uploaded PDF."""
pdf_bytes = await file.read()
blocks = await app.state.client.ast.extract_cited_text(pdf_bytes)
return {"text": [block.text for block in blocks]}

Для AI-агентов запускайте сервер MCP. Он предоставляет инструменты для PDF (например nextpdf_extract_text, nextpdf_extract_tables, nextpdf_get_ast, nextpdf_info, nextpdf_search, nextpdf_get_outline, nextpdf_diff и nextpdf_health) через стандартные ввод и вывод. Сервер считывает NEXTPDF_BASE_URL и NEXTPDF_API_KEY из окружения, поэтому работает на удалённом бэкенде; как и CLI, он не может использовать локальный бэкенд. Установите пакет с необязательной extra-группой и запустите модуль.

Окно терминала
pip install "nextpdf[mcp]"
python -m nextpdf.mcp

См. Сервер Python MCP для пошагового руководства по интеграции агента, Python CLI для работы из терминала и Справочник по Python API для полного обзора клиента, моделей и исключений.