Обзор Python SDK
Обзор Python SDK
Заголовок раздела «Обзор 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 (синхронный) | Линейный поток управления; нет цикла событий, которым нужно управлять. |
| Блокноты Jupyter | NextPDF (синхронный) | run_sync обнаруживает работающий цикл событий и передаёт выполнение в рабочий поток, поэтому блокирующие вызовы работают внутри ячеек. |
Интерфейс nextpdf CLI | NextPDF (синхронный, внутренний) | 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_key | AsyncNextPDF(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 AsyncNextPDFfrom 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 asyncioimport osfrom 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 | Базовый тип для всех ошибок SDK | status_code |
NextPDFAPIError | Любая ошибка HTTP от сервера | status_code, error_code |
NextPDFLicenseError | HTTP 402; функция требует более высокого тарифа на сервере | status_code (402) |
QuotaExceededError | HTTP 429; превышен лимит частоты запросов или квота | retry_after |
AstNoStructTreeError | HTTP 422; PDF без тегов при выключенном эвристическом режиме | status_code (422) |
AstBuildTimeoutError | HTTP 504; истекло время сборки AST | status_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 asynciofrom collections.abc import Awaitable, Callablefrom 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 asyncioimport osfrom 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 osfrom contextlib import asynccontextmanager
from fastapi import FastAPI, UploadFile
from nextpdf import AsyncNextPDF
@asynccontextmanagerasync 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 для полного обзора клиента, моделей и исключений.