Bỏ qua để đến nội dung

Tổng quan Python SDK

Bộ công cụ phát triển phần mềm (SDK) Python của NextPDF dành cho các ứng dụng Python cần trích xuất Portable Document Format (PDF) có nguồn gốc rõ ràng. SDK trả về các khối có cấu trúc kèm neo trích dẫn, bao gồm chỉ mục trang, độ tin cậy, hộp giới hạn tùy chọn và mã định danh nút ngữ nghĩa khi PDF nguồn cung cấp cấu trúc đó.

Dùng SDK khi quy trình của bạn cần trả lời các câu hỏi như “Văn bản này lấy từ trang nào?”, “Bảng nào chứng minh cho giá trị này?”, hoặc “Có gì thay đổi giữa hai tệp PDF này?” mà không xem kết quả trích xuất PDF như văn bản thuần không rõ nguồn.

  • Một client NextPDF đồng bộ cho script, tác vụ theo lô và notebook.
  • Một client AsyncNextPDF bất đồng bộ cho asyncio, FastAPI và các runtime bất đồng bộ khác.
  • Một giao diện dòng lệnh (CLI) nextpdf để trích xuất một lần từ đường dẫn tệp hoặc đầu vào chuẩn, rồi ghi ra đầu ra chuẩn hoặc một tệp.
  • Một máy chủ Model Context Protocol (MCP) tùy chọn để các tác nhân trí tuệ nhân tạo (AI) có thể gọi trực tiếp các công cụ trích xuất PDF.
  • Một backend từ xa để dùng cho môi trường sản xuất với NextPDF Connect.
  • Một backend cục bộ để trích xuất ngoại tuyến, chỉ dùng thư viện, thông qua pypdf.

Backend từ xa gửi byte PDF tới máy chủ NextPDF Connect. Đây là hướng được khuyến nghị cho môi trường sản xuất vì nó tập trung hóa hành vi trích xuất, xác thực, hạn mức và các kiểm soát vận hành.

Backend cục bộ chạy bên trong tiến trình Python và đọc PDF thông qua pypdf. Backend này hữu ích cho phát triển ngoại tuyến và các PDF có gắn thẻ, nhưng không thể cung cấp hộp giới hạn chính xác và dùng cách trích xuất theo đoạn văn dựa trên heuristic cho các PDF không gắn thẻ. Backend cục bộ chỉ dùng được qua thư viện: hãy đưa một LocalBackend vào AsyncNextPDF để sử dụng. CLI nextpdf và máy chủ MCP không thể dùng backend này. Xem Ma trận lựa chọn backend để có bảng so sánh đầy đủ.

SDK không thực hiện nhận dạng ký tự quang học (OCR). Các PDF được quét hoặc chỉ chứa hình ảnh cần qua một bước OCR trước khi NextPDF có thể trích xuất văn bản nhúng. Bố cục phức tạp, văn bản chồng lấn và các trình tạo PDF bất thường cũng có thể làm giảm chất lượng trích xuất.

CLI nextpdf chỉ hoạt động với backend từ xa và không phải là giao diện dạng luồng. Mỗi lệnh đọc toàn bộ PDF vào bộ nhớ (từ đường dẫn tệp hoặc đầu vào chuẩn), gửi PDF đó tới máy chủ NextPDF Connect, dựng kết quả hoàn chỉnh trong bộ nhớ rồi tuần tự hóa trong một lần ghi duy nhất. Bạn có thể chuyển hướng đầu ra đó tới một tệp bằng --output (hoặc -o) hoặc tới đầu ra chuẩn, nhưng kết quả được đệm toàn bộ, không được tạo theo từng phần. CLI không thể dùng backend pypdf cục bộ.

Chọn client: đồng bộ hay bất đồng bộ

Phần tiêu đề “Chọn client: đồng bộ hay bất đồng bộ”

Cả hai client dùng chung không gian tên phương thức ast cho các thao tác trên cây cú pháp trừu tượng (AST) và trả về cùng các mô hình Pydantic. Điểm khác biệt chỉ nằm ở mô hình đồng thời.

Bối cảnh của bạnDùngLý do
Script và tác vụ theo lôNextPDF (đồng bộ)Luồng điều khiển tuyến tính; không có vòng lặp sự kiện cần quản lý.
Notebook JupyterNextPDF (đồng bộ)run_sync phát hiện vòng lặp sự kiện đang chạy và điều phối tới một luồng worker, nên các lệnh gọi chặn vẫn hoạt động bên trong các ô lệnh.
Công cụ nextpdf CLINextPDF (đồng bộ, nội bộ)CLI dựng sẵn một client đồng bộ cho bạn.
asyncio dịch vụAsyncNextPDFHỗ trợ await nguyên bản; không có bàn giao luồng.
FastAPI, Starlette, Asynchronous Server Gateway Interface (ASGI)AsyncNextPDFDùng chung vòng lặp sự kiện của yêu cầu và cùng một nhóm kết nối.
Phân tán đồng thời caoAsyncNextPDFChạy nhiều lần trích xuất đồng thời bằng asyncio.gather trên một client dùng chung nhóm kết nối.

NextPDF bao bọc một AsyncNextPDF nội bộ và chạy mỗi lệnh gọi qua run_sync. Bên trong một vòng lặp sự kiện đang chạy, chẳng hạn như trong notebook, run_sync điều phối coroutine tới một luồng worker riêng với vòng lặp của chính nó, nên bạn không gặp lỗi asyncio.run lồng nhau. Trong dịch vụ asyncio hoặc ASGI, hãy gọi AsyncNextPDF trực tiếp thay vì chịu chi phí bàn giao luồng đó trên mỗi lệnh gọi.

Client bất đồng bộ sở hữu một httpx.AsyncClient để dùng chung nhóm kết nối, vì vậy hãy tái sử dụng một thực thể AsyncNextPDF và đóng nó một lần. Client NextPDF đồng bộ không cung cấp phương thức close(). Với các tải công việc bất đồng bộ chạy lâu, hãy ưu tiên AsyncNextPDF và quản lý vòng đời của nó một cách rõ ràng (xem Mô hình vận hành sản xuất).

Một backend triển khai giao thức PdfBackend. Backend từ xa (RemoteBackend) được chọn tự động khi bạn truyền base_urlapi_key. Bạn phải đưa backend cục bộ (LocalBackend) vào một cách rõ ràng thông qua tham số backend= của AsyncNextPDF; backend này không được xuất ra từ gói nextpdf cấp cao nhất và không thể truy cập từ CLI hoặc máy chủ MCP.

Khả năngTừ xa (RemoteBackend)Cục bộ (LocalBackend)
Được chọn bởibase_url + api_keyAsyncNextPDF(backend=LocalBackend(...))
MạngNextPDF Connect qua Hypertext Transfer Protocol Secure (HTTPS)Không có; chạy trong tiến trình
Xác thực, hạn mức, đo lườngTập trung ở máy chủKhông có
Khả năng quan sát và các kiểm soát vận hànhPhía máy chủKhông có
Trích xuất PDF có gắn thẻ (StructTree)
Trích xuất PDF không gắn thẻEngine của máy chủTách đoạn văn theo heuristic, độ tin cậy 0.5
Hộp giới hạnCó (khi máy chủ cung cấp chúng)Không (bboxNone)
Trích xuất bảng trên các PDF không gắn thẻEngine của máy chủKhông trả về bảng nào
Truy cập được từ CLI / máy chủ MCPKhông (chỉ dùng thư viện)
Khuyến nghị dùng choSản xuấtPhát triển ngoại tuyến, kiểm thử PDF có gắn thẻ

Dùng backend từ xa cho môi trường sản xuất vì đó là hướng duy nhất có xác thực tập trung, thực thi hạn mức, đo lường và khả năng quan sát. Dùng backend cục bộ cho phát triển ngoại tuyến và kiểm thử trên các PDF có gắn thẻ, đồng thời chấp nhận kết quả theo heuristic, không có hộp giới hạn và không có bảng trên đầu vào không gắn thẻ.

"""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)

Trong môi trường sản xuất, hãy chạy backend từ xa với NextPDF Connect. Các ví dụ dưới đây bao gồm tái sử dụng client, xử lý lỗi, thử lại, xử lý hạn mức và thời gian chờ. Mọi ký hiệu được dùng ở đây đều tồn tại trong SDK. SDK không tự thử lại thay cho bạn, nên vòng lặp thử lại là trách nhiệm của bạn.

Tái sử dụng client và dùng chung nhóm kết nối

Phần tiêu đề “Tái sử dụng client và dùng chung nhóm kết nối”

RemoteBackend giữ một httpx.AsyncClient thường trực để dùng chung nhóm kết nối. Hãy khởi tạo AsyncNextPDF một lần, dùng chung cho các yêu cầu và đóng khi tắt. Đừng tạo một client cho mỗi yêu cầu.

"""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())

Trình quản lý ngữ cảnh bất đồng bộ gọi close() khi thoát, qua đó đóng tầng truyền tải bên dưới. Nếu không dùng trình quản lý ngữ cảnh, hãy tự gọi await client.close().

Xử lý lỗi bằng cây phân cấp ngoại lệ

Phần tiêu đề “Xử lý lỗi bằng cây phân cấp ngoại lệ”

SDK phát sinh một cây phân cấp ngoại lệ có kiểu. Mọi lỗi đều bắt nguồn từ NextPDFError; các lỗi ở tầng Hypertext Transfer Protocol (HTTP) bắt nguồn từ NextPDFAPIError và mang theo một status_code. Hãy bắt các kiểu cụ thể mà bạn có thể xử lý, rồi quay về kiểu cơ sở.

Ngoại lệPhát sinh khiThuộc tính chính
NextPDFErrorKiểu cơ sở cho mọi lỗi của SDKstatus_code
NextPDFAPIErrorMọi lỗi HTTP từ máy chủstatus_code, error_code
NextPDFLicenseErrorHTTP 402; tính năng cần một bậc máy chủ cao hơnstatus_code (402)
QuotaExceededErrorHTTP 429; vượt quá giới hạn tốc độ hoặc hạn mứcretry_after
AstNoStructTreeErrorHTTP 422; PDF không gắn thẻ với chế độ heuristic đang tắtstatus_code (422)
AstBuildTimeoutErrorHTTP 504; quá trình dựng AST hết thời gian chờ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 không tự động thử lại. Hãy bọc các lệnh gọi trong vòng lặp của riêng bạn để thử lại khi gặp lỗi HTTP tạm thời và tôn trọng giá trị Retry-After của máy chủ, giá trị mà QuotaExceededError cung cấp dưới dạng retry_after (một số nguyên giây, hoặc None). Dùng backoff theo cấp số nhân cho các trạng thái tạm thời khác, và đừng thử lại 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")

Quản lý hạn mức, giới hạn tốc độ và thời gian chờ

Phần tiêu đề “Quản lý hạn mức, giới hạn tốc độ và thời gian chờ”

Việc thực thi hạn mức và giới hạn tốc độ nằm trên máy chủ. Khi gặp HTTP 429, SDK phát sinh QuotaExceededError và phân tích tiêu đề Retry-After thành retry_after. Backend từ xa cũng hiển thị các tiêu đề X-RateLimit-* trên các phản hồi kết xuất, nên bạn có thể chủ động điều tiết trước khi chạm giới hạn cứng.

Thời gian chờ yêu cầu dùng mặc định cố định là 60 giây tổng cộng, với thời gian chờ kết nối 10 giây (httpx.Timeout(60.0, connect=10.0)). Để giới hạn các lần dựng AST kéo dài, hãy thu hẹp công việc bằng page_range_start, page_range_end, hoặc token_budget thay vì chỉ dựa vào thời gian chờ; một lần dựng quá lâu sẽ trả về AstBuildTimeoutError (HTTP 504).

Một worker theo lô đọc các tệp PDF, trích xuất văn bản có trích dẫn và ghi ra đầu ra có cấu trúc. Hãy tái sử dụng một client dùng chung nhóm kết nối, giới hạn mức độ đồng thời bằng một semaphore, và áp dụng helper thử lại ở trên.

"""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")))

Dịch vụ FastAPI dùng chung một AsyncNextPDF cho các yêu cầu trong suốt vòng đời của ứng dụng, nên mỗi yêu cầu đều tái sử dụng nhóm kết nối. Hãy đọc thông tin xác thực từ môi trường và coi API key là bí mật.

"""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]}

Đối với các tác nhân AI, hãy chạy máy chủ MCP. Máy chủ hiển thị các công cụ PDF (ví dụ nextpdf_extract_text, nextpdf_extract_tables, nextpdf_get_ast, nextpdf_info, nextpdf_search, nextpdf_get_outline, nextpdf_diff, và nextpdf_health) qua đầu vào và đầu ra chuẩn. Máy chủ đọc NEXTPDF_BASE_URLNEXTPDF_API_KEY từ môi trường, nên được hỗ trợ bởi backend từ xa; giống như CLI, máy chủ không thể dùng backend cục bộ. Hãy cài đặt phần mở rộng tùy chọn và chạy module.

Terminal window
pip install "nextpdf[mcp]"
python -m nextpdf.mcp

Xem Máy chủ Python MCP để biết hướng dẫn tích hợp tác nhân, Python CLI để biết cách dùng trong terminal, và Tài liệu tham khảo API Python để xem toàn bộ bề mặt client, mô hình và ngoại lệ.