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

Python CLI: интерфейс командной строки

Используйте команду nextpdf, чтобы извлекать содержимое из файлов Portable Document Format (PDF) прямо в терминале. Укажите конечную точку NextPDF Connect, передайте PDF и получите структурированный вывод — цитируемый текст, таблицы, полное семантическое абстрактное синтаксическое дерево (AST) или сводку метаданных — в стандартный вывод (stdout) или файл.

Команда nextpdf — это группа команд Click. Параметры подключения и сеанса — --base-url, --api-key, --log-level, --output/-o и --strict — применяются к группе, поэтому размещайте их перед подкомандой. Подкоманду и её параметры, такие как --format или --page, указывайте после:

nextpdf [GROUP OPTIONS] COMMAND [SUBCOMMAND] PDF_PATH [COMMAND OPTIONS]

Если поместить параметр группы после подкоманды, команда завершится с ошибкой. Например, nextpdf info document.pdf --base-url ... сообщает Error: No such option: --base-url и завершается со статусом 2, потому что Click уже разбирает подкоманду info, когда встречает --base-url, а для info этот параметр не определён.

Чтобы избежать ловушки с порядком, передавайте учётные данные через переменные окружения (см. Настройка один раз для оболочки). В примерах ниже сначала показан вариант с явными флагами, чтобы был понятен правильный порядок.

Извлечь текст в формате JavaScript Object Notation (JSON):

Окно терминала
nextpdf --base-url http://localhost:8080 --api-key "$NEXTPDF_API_KEY" extract text document.pdf

Извлечь таблицы в формате значений, разделённых запятыми (CSV):

Окно терминала
nextpdf --base-url http://localhost:8080 --api-key "$NEXTPDF_API_KEY" extract tables invoice.pdf --format csv

Просмотреть метаданные и структуру документа:

Окно терминала
nextpdf --base-url http://localhost:8080 --api-key "$NEXTPDF_API_KEY" info document.pdf

Получить полное семантическое AST:

Окно терминала
nextpdf --base-url http://localhost:8080 --api-key "$NEXTPDF_API_KEY" ast document.pdf

Вывести версию установленного SDK без обращения к серверу:

Окно терминала
nextpdf version

Команда version — единственная, которой не нужны ни --base-url, ни --api-key. Все остальные команды обращаются к серверу и требуют оба значения.

В каждом примере ключ API считывается из переменной окружения NEXTPDF_API_KEY, а не передаётся напрямую в командной строке. Относитесь к ключу как к секрету. Ключ, указанный в командной строке, попадает в историю оболочки и список процессов (ps), где его могут увидеть другие пользователи узла.

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

ПараметрПеременная окруженияПо умолчаниюНазначение
--base-urlNEXTPDF_BASE_URL(обязательно)URL сервера NextPDF Connect.
--api-keyNEXTPDF_API_KEY(обязательно)Ключ API для аутентификации по токену.
--log-levelwarningУровень детализации журнала: debug, info, warning или error. Журналы выводятся в стандартный поток ошибок (stderr).
--output, -oстандартный вывод (stdout)Записывать вывод команды в файл вместо stdout.
--strictвыключеноЗарезервировано для будущего использования. Сейчас флаг разбирается, но не меняет поведение.
--help, -hПоказать справку и выйти.

Значения --base-url и --api-key обязательны для каждой команды, кроме version. Если отсутствует любое из этих значений — не задан ни флаг, ни переменная окружения — команда выводит ошибку и завершается со статусом 1.

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

nextpdf [GROUP OPTIONS] extract text PDF_PATH [--format FORMAT] [--page N] [--headings-only]
ПараметрЗначенияПо умолчаниюНазначение
--formatjson, markdown, plainjsonФормат вывода.
--pageцелое числовсе страницыИзвлекает только страницу с этим индексом, начиная с 0.
--headings-onlyфлагвыключеноИзвлекает только узлы заголовков.

PDF_PATH — путь к файлу или - для чтения байтов PDF из стандартного ввода (stdin).

Извлекает каждую таблицу с якорями цитат и структурой на уровне ячеек.

nextpdf [GROUP OPTIONS] extract tables PDF_PATH [--format FORMAT] [--page-start N] [--page-end N]
ПараметрЗначенияПо умолчаниюНазначение
--formatjson, csvjsonФормат вывода.
--page-startцелое числопервая страницаИндекс начальной страницы, начиная с 0.
--page-endцелое числопоследняя страницаИндекс конечной страницы, начиная с 0.

PDF_PATH — путь к файлу или - для чтения из stdin.

Возвращает полное семантическое AST в виде JSON: иерархическое дерево узлов, включая заголовки, абзацы, таблицы, списки и рисунки, с ограничивающими прямоугольниками и текстовым содержимым.

nextpdf [GROUP OPTIONS] ast PDF_PATH [--page-start N] [--page-end N] [--token-budget N]
ПараметрЗначенияПо умолчаниюНазначение
--page-startцелое числопервая страницаИндекс начальной страницы, начиная с 0.
--page-endцелое числопоследняя страницаИндекс конечной страницы, начиная с 0.
--token-budgetцелое числобез ограниченийПриблизительный предел токенов для возвращаемого AST.

PDF_PATH — путь к файлу или - для чтения из stdin. Команда ast строит одно дерево документа; она не сравнивает два PDF. Для структурного сравнения см. Рецепт: сравнение двух редакций PDF.

Выводит компактную сводку одного документа в формате JSON: версию схемы, хеш источника, число страниц, приблизительное число токенов, тип корневого узла и количество дочерних элементов корня.

nextpdf [GROUP OPTIONS] info PDF_PATH

PDF_PATH — путь к файлу или - для чтения из stdin.

Выводит версию установленного SDK, например nextpdf 1.1.0, и завершает работу. Эта команда не обращается к серверу и не требует учётных данных.

nextpdf version

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

Окно терминала
export NEXTPDF_BASE_URL=http://localhost:8080
export NEXTPDF_API_KEY=your-key
nextpdf extract text document.pdf

В Windows PowerShell:

Окно терминала
$env:NEXTPDF_BASE_URL = "http://localhost:8080"
$env:NEXTPDF_API_KEY = "your-key"
nextpdf extract text document.pdf

Лучше загружать ключ из хранилища секретов или файла .env, который хранится вне системы контроля версий. Не вставляйте рабочий ключ в общий сеанс терминала или сценарий, который вы фиксируете в репозитории.

Выбирайте формат вывода для каждой команды с помощью --format. Команды для текста и таблиц поддерживают больше одного формата; ast и info всегда выдают JSON.

КомандаФорматыПо умолчанию
extract textjson, markdown, plainjson
extract tablesjson, csvjson
astjsonjson
infojsonjson

Выбирайте JSON, когда следующей программе нужны индексы страниц, оценки достоверности или идентификаторы узлов. Выбирайте CSV, когда таблицы будет обрабатывать электронная таблица или конвейер табличных данных. Выбирайте plain или markdown, когда результат будет читать человек или текстовый инструмент.

Команда извлечения текста выдаёт JSON-массив цитируемых блоков. Каждый блок содержит text, объект citation (node_id, page_index, bbox, confidence) и необязательный node_type. Запишите вывод в файл с помощью --output или перенаправьте stdout, затем разберите его.

В примере ниже для оболочки используется jq, чтобы оставить только заголовки на странице 0:

Окно терминала
nextpdf --output blocks.json extract text report.pdf --format json
jq '[.[] | select(.citation.page_index == 0 and .node_type == "heading") | .text]' blocks.json

Те же данные можно без проблем разобрать в Python. CLI записывает JSON-массив, поэтому загрузите его средствами стандартной библиотеки и считывайте типизированные поля:

"""Parse cited text blocks emitted by `nextpdf extract text --format json`."""
import json
from pathlib import Path
def load_headings(blocks_path: Path) -> list[str]:
"""Return the text of every heading block on page 0.
Args:
blocks_path: Path to the JSON file written by `nextpdf extract text`.
Returns:
The text of each heading-type block whose citation is on page 0.
"""
raw = blocks_path.read_text(encoding="utf-8")
blocks: list[dict[str, object]] = json.loads(raw)
headings: list[str] = []
for block in blocks:
citation = block["citation"]
if block.get("node_type") == "heading" and citation["page_index"] == 0:
headings.append(str(block["text"]))
return headings
if __name__ == "__main__":
for heading in load_headings(Path("blocks.json")):
print(heading)

Когда нужны проверенные типизированные модели вместо необработанных словарей, вызывайте SDK напрямую, а не разбирайте вывод CLI. См. Обзор Python для клиента NextPDF и его возвращаемого типа CitedTextBlock.

С --format csv команда извлечения таблиц записывает по одному блоку CSV на таблицу. Строка-комментарий # Table N (pM) предшествует каждой таблице и указывает её номер (начиная с 1) и индекс страницы (начиная с 0). Пустая строка отделяет соседние таблицы. CLI заключает значения ячеек в кавычки и экранирует их с помощью модуля csv Python, поэтому значения, содержащие запятые, кавычки или переводы строк, сохраняются без потерь.

Окно терминала
nextpdf --output tables.csv extract tables statement.pdf --format csv

Так как файл содержит несколько блоков CSV, сначала разделите его по строкам-комментариям, а затем разбирайте каждый блок как отдельную таблицу:

"""Split multi-table CSV output from `nextpdf extract tables --format csv`."""
import csv
import io
from pathlib import Path
def read_tables(csv_path: Path) -> list[list[list[str]]]:
"""Parse the multi-block CSV file into a list of tables.
Each table is a list of rows; each row is a list of cell strings.
The leading `# Table N (pM)` comment row is dropped from every table.
Args:
csv_path: Path to the file written by `nextpdf extract tables`.
Returns:
One parsed table per `# Table` block in the file.
"""
text = csv_path.read_text(encoding="utf-8")
tables: list[list[list[str]]] = []
current: list[str] = []
for line in text.splitlines(keepends=True):
if line.startswith("# Table ") and current:
tables.append(_parse_block(current))
current = []
current.append(line)
if current:
tables.append(_parse_block(current))
return tables
def _parse_block(lines: list[str]) -> list[list[str]]:
"""Parse one CSV block, discarding its leading comment row."""
reader = csv.reader(io.StringIO("".join(lines)))
rows = [row for row in reader if row]
return rows[1:] if rows and rows[0] and rows[0][0].startswith("# Table ") else rows
if __name__ == "__main__":
for index, table in enumerate(read_tables(Path("tables.csv")), start=1):
print(f"table {index}: {len(table)} rows")

CLI использует три кода завершения. Проверяйте $? в сценариях оболочки или $LASTEXITCODE в PowerShell, чтобы выполнять разные ветки при успехе и ошибке. Читайте диагностические сообщения из stderr: он отделён от данных в stdout.

Код завершенияЗначениеПримеры
0Успех.Команда выполнена; version вывела результат.
1Ошибка времени выполнения. CLI выводит Error: <message> в stderr.Входной файл не найден или не является обычным файлом, пустой stdin, отсутствующие или недопустимые --base-url/--api-key, любая ошибка на стороне сервера (требуется лицензия, превышена квота, PDF без тегов, тайм-аут построения или другой сбой API).
2Ошибка использования, сообщённая Click.Неизвестная команда или параметр (включая параметр группы, размещённый после подкоманды), отсутствующий обязательный аргумент, такой как PDF_PATH.

Любой сбой на стороне сервера возвращает код завершения 1 с читаемым сообщением в stderr. SDK выбрасывает типизированное исключение — NextPDFLicenseError (Hypertext Transfer Protocol (HTTP) 402), QuotaExceededError (HTTP 429), AstNoStructTreeError (HTTP 422, PDF без тегов), AstBuildTimeoutError (HTTP 504) или базовое NextPDFAPIError. CLI перехватывает все эти исключения через общий базовый класс NextPDFError, выводит сообщение и завершается с кодом 1. CLI не предоставляет отдельных кодов завершения для каждого типа сбоя. Чтобы различить, например, ошибку квоты и ошибку лицензии в сценарии, изучите текст сообщения в stderr или вызовите SDK напрямую (см. Обзор Python для классов исключений).

Используйте такой шаблон сценария, чтобы отделить данные от диагностики:

#!/usr/bin/env bash
set -euo pipefail
# Credentials come from the environment, not the command line.
: "${NEXTPDF_BASE_URL:?set NEXTPDF_BASE_URL}"
: "${NEXTPDF_API_KEY:?set NEXTPDF_API_KEY}"
if nextpdf --output contract.ast.json ast contract.pdf; then
echo "AST written to contract.ast.json"
else
status=$?
echo "nextpdf failed with exit code ${status}" >&2
exit "${status}"
fi

С --output CLI записывает данные в указанный файл и выводит в stderr только строку подтверждения Written to <path>, поэтому stdout остаётся пустым. Без --output данные выводятся в stdout, и вы можете их перенаправить.

Каждый рецепт ниже использует только проверенные команды и флаги. В каждом случае учётные данные берутся из окружения.

Преобразуйте папку со счетами в отдельный файл CSV для каждого документа — для электронной таблицы или бухгалтерского конвейера:

#!/usr/bin/env bash
set -euo pipefail
: "${NEXTPDF_BASE_URL:?set NEXTPDF_BASE_URL}"
: "${NEXTPDF_API_KEY:?set NEXTPDF_API_KEY}"
mkdir -p out
for pdf in invoices/*.pdf; do
name="$(basename "${pdf}" .pdf)"
nextpdf --output "out/${name}.csv" extract tables "${pdf}" --format csv
done

Каждый файл out/<name>.csv содержит по одному блоку CSV для каждой обнаруженной таблицы, с заголовком # Table N (pM) перед каждым блоком. Разбирайте блоки с помощью средства чтения CSV, показанного выше.

Сочетайте --headings-only с форматом markdown, чтобы получить быструю структуру, которую можно прочитать или вставить в заметки:

Окно терминала
nextpdf --output outline.md extract text whitepaper.pdf --headings-only --format markdown

CLI-команда ast возвращает дерево для одного документа; у неё нет подкоманды diff. Структурное сравнение доступно в SDK как client.ast.get_ast_diff(...) и на сервере Model Context Protocol (MCP) как инструмент nextpdf_diff. Запустите сравнение через SDK:

"""Compare two PDF revisions structurally with the NextPDF SDK.
The API key is read from the environment, never hard-coded.
"""
import os
from pathlib import Path
from nextpdf import NextPDF
def diff_revisions(original: Path, modified: Path) -> None:
"""Print a structural diff summary between two PDF revisions.
Args:
original: Path to the earlier PDF revision.
modified: Path to the later PDF revision.
"""
base_url = os.environ["NEXTPDF_BASE_URL"]
api_key = os.environ["NEXTPDF_API_KEY"]
client = NextPDF(base_url=base_url, api_key=api_key)
result = client.ast.get_ast_diff(
original.read_bytes(),
modified.read_bytes(),
)
summary = result.summary
print(f"added: {summary.added_node_count}")
print(f"removed: {summary.removed_node_count}")
print(f"changed: {summary.changed_node_count}")
for entry in result.diff:
preview = entry.text_preview or ""
print(f" {entry.type:<8} {entry.node_type:<12} p{entry.page_index} {preview}")
if __name__ == "__main__":
diff_revisions(Path("contract-v1.pdf"), Path("contract-v2.pdf"))

Чтобы выполнить такое же сравнение из агента искусственного интеллекта (AI), а не из сценария, зарегистрируйте сервер MCP и вызовите инструмент nextpdf_diff. См. страницу Сервер Python MCP.

Рецепт: передача PDF потоком из другого инструмента

Заголовок раздела «Рецепт: передача PDF потоком из другого инструмента»

Читайте байты PDF из stdin с помощью -, чтобы поставить nextpdf после инструмента, который выводит PDF в свой stdout:

Окно терминала
curl --silent https://example.com/report.pdf | nextpdf info -

Аргумент - указывает команде читать документ из stdin. Если байты не поступают, команда сообщает об ошибке и завершается с кодом 1.

CLI формирует каждый ответ в памяти и записывает его один раз. Вывод легко перенаправлять или передавать по конвейеру, но он формируется не потоково. Для большого AST или набора таблиц весь ответ полностью буферизуется, прежде чем первый байт достигнет stdout или файла --output. Планируйте потребление памяти и задержку для ответов на уровне всего документа, а не для потока.

Каждый вызов nextpdf создаёт новый клиент и соединение HTTP, поэтому цикл по многим файлам открывает и закрывает соединение для каждого файла. Накладные расходы на соединение обычно малы по сравнению со временем извлечения на стороне сервера, но в большом масштабе они становятся заметными.

  • Повторно используйте одну конечную точку. Направляйте каждый вызов на одно и то же развёртывание NextPDF Connect, чтобы сервер мог повторно использовать прогретые кеши и пулы соединений. Не распределяйте пакетную задачу по разным конечным точкам, если только вы не балансируете нагрузку намеренно.
  • Ограничивайте объём работы на вызов. Используйте --page, --page-start/--page-end или --token-budget, чтобы обрабатывать только нужные вам страницы. Меньшие диапазоны страниц сокращают и время на сервере, и размер ответа; --token-budget ограничивает объём AST, который приходится читать вашему агенту.
  • Пакетная обработка в одном процессе для больших задач. Для больших объёмов предпочитайте Python SDK повторяющимся вызовам CLI. Один долгоживущий клиент NextPDF или AsyncNextPDF повторно использует одно HTTP-соединение из пула для каждого документа, что устраняет издержки запуска процесса и настройки соединения, которые цикл CLI несёт каждый раз. Обзор Python показывает жизненный цикл клиента, а AsyncNextPDF поддерживает параллельное извлечение из многих PDF.
  • Не смешивайте журналы с данными. Оставляйте --log-level на значении по умолчанию для пакетных запусков. Диагностические журналы выводятся в stderr и не смешиваются с данными stdout, но повышение уровня до debug добавляет шум и небольшие накладные расходы.