نظرة عامة على حزمة تطوير برامج Python
نظرة عامة على حزمة تطوير برامج Python
قسم بعنوان «نظرة عامة على حزمة تطوير برامج Python»حزمة تطوير برامج Python (SDK) من NextPDF مخصّصة لتطبيقات Python التي تحتاج إلى استخراج محتوى ملفات تنسيق المستندات المنقولة (PDF) مع تتبّع المصدر. تُعيد كتلًا مهيكلة مزوّدة بمراسي استشهاد، وتتضمّن فهرس الصفحة، ودرجة الثقة، والمربّعات المحيطة الاختيارية، ومعرّفات العُقد الدلالية عندما يكشف ملف PDF المصدر عن تلك البنية.
استخدم حزمة SDK عندما يحتاج مسار المعالجة لديك إلى الإجابة عن أسئلة مثل “من أي صفحة أتى هذا النص؟”، و”أي جدول يدعم هذه القيمة؟”، أو “ما الذي تغيّر بين ملفي PDF هذين؟” من دون التعامل مع ناتج استخراج PDF كنص عادي مجهول المصدر.
ما الذي توفّره
قسم بعنوان «ما الذي توفّره»- عميل
NextPDFمتزامن للنصوص البرمجية، والمهام المُجمّعة، ودفاتر الملاحظات. - عميل
AsyncNextPDFغير متزامن لـasyncio، و FastAPI، وبيئات التشغيل غير المتزامنة الأخرى. - واجهة سطر أوامر (CLI) باسم
nextpdfللاستخراج بطلب واحد من مسار ملف أو من الإدخال القياسي، مع الكتابة إلى الإخراج القياسي أو إلى ملف. - خادم اختياري لبروتوكول سياق النموذج (MCP) كي يتمكّن وكلاء الذكاء الاصطناعي (AI) من استدعاء أدوات استخراج PDF مباشرةً.
- خلفية بعيدة للاستخدام في بيئة الإنتاج مع NextPDF Connect.
- خلفية محلية للاستخراج دون اتصال، باستخدام المكتبة فقط، عبر
pypdf.
خيارات الخلفية
قسم بعنوان «خيارات الخلفية»تُرسل الخلفية البعيدة بايتات PDF إلى خادم NextPDF Connect. وهذا هو مسار الإنتاج المُوصى به لأنه يجعل سلوك الاستخراج، والمصادقة، والحصص، وعناصر التحكم التشغيلية مُمركزة.
تعمل الخلفية المحلية داخل عملية Python وتقرأ ملفات PDF عبر pypdf. وهي مفيدة للتطوير دون اتصال ولملفات PDF الموسومة، لكنها لا تستطيع توفير مربّعات محيطة دقيقة، وتستخدم استخراجًا استدلاليًا على مستوى الفقرة لملفات PDF غير الموسومة. الخلفية المحلية متاحة عبر المكتبة فقط: مرّر LocalBackend إلى AsyncNextPDF لاستخدامها. لا يمكن لواجهة سطر الأوامر nextpdf ولا لخادم MCP استخدامها. راجع مصفوفة اختيار الخلفية للمقارنة الكاملة.
القيود
قسم بعنوان «القيود»لا تنفّذ حزمة SDK التعرّف الضوئي على الحروف (OCR). تحتاج ملفات PDF الممسوحة ضوئيًا أو المكوّنة من صور فقط إلى خطوة OCR قبل أن يتمكّن NextPDF من استخراج النص المُضمّن. كما يمكن للتخطيطات المعقّدة، والنصوص المتداخلة، ومُنتِجات PDF غير المعتادة أن تقلّل من جودة الاستخراج.
واجهة سطر الأوامر nextpdf بعيدة فقط وليست واجهة بثّ. يقرأ كل أمر ملف PDF بالكامل إلى الذاكرة (من مسار ملف أو من الإدخال القياسي)، ويُرسله إلى خادم NextPDF Connect، ويبني النتيجة الكاملة في الذاكرة، ويُسلسِلها في عملية كتابة واحدة. يمكنك إعادة توجيه ذلك الإخراج إلى ملف باستخدام --output (أو -o) أو إلى الإخراج القياسي، لكن النتيجة تُحفظ مؤقتًا في الذاكرة بالكامل ولا تُنتَج تدريجيًا. لا تستطيع واجهة سطر الأوامر استخدام خلفية pypdf المحلية.
اختيار عميل: متزامن مقابل غير متزامن
قسم بعنوان «اختيار عميل: متزامن مقابل غير متزامن»يشترك العميلان في مساحة أسماء واحدة لأساليب ast الخاصة بعمليات شجرة بناء الجملة المجرّدة (AST)، ويُعيدان نماذج Pydantic نفسها. ولا يختلفان إلا في نموذج التزامن.
| سياقك | الاستخدام | السبب |
|---|---|---|
| النصوص البرمجية والمهام المُجمّعة | NextPDF (متزامن) | تدفّق تحكّم خطّي؛ لا حلقة أحداث لإدارتها. |
| دفاتر ملاحظات Jupyter | NextPDF (متزامن) | يكتشف run_sync حلقة الأحداث قيد التشغيل ويوزّع العمل إلى مؤشّر ترابط عامل، فتعمل الاستدعاءات الحاجبة داخل الخلايا. |
واجهة سطر الأوامر nextpdf | NextPDF (متزامن، داخلي) | تبني واجهة سطر الأوامر عميلًا متزامنًا نيابةً عنك. |
خدمات asyncio | AsyncNextPDF | await أصلي؛ من دون تحويل العمل إلى مؤشّر ترابط. |
| FastAPI، و Starlette، وواجهة بوابة الخادم غير المتزامنة (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 العليا وغير قابلة للوصول من واجهة سطر الأوامر أو من خادم MCP.
| القدرة | بعيدة (RemoteBackend) | محلية (LocalBackend) |
|---|---|---|
| تُحدَّد بواسطة | base_url + api_key | AsyncNextPDF(backend=LocalBackend(...)) |
| الشبكة | NextPDF Connect عبر بروتوكول نقل النص التشعّبي الآمن (HTTPS) | لا شيء؛ تعمل ضمن العملية |
| المصادقة، والحصص، والقياس | مُمركزة على الخادم | لا شيء |
| إمكانية المراقبة وعناصر التحكم التشغيلية | من جانب الخادم | لا شيء |
| استخراج PDF الموسوم (StructTree) | نعم | نعم |
| استخراج PDF غير الموسوم | محرّك الخادم | تقسيم فقرات استدلالي، درجة الثقة 0.5 |
| المربّعات المحيطة | نعم (عندما يوفّرها الخادم) | لا (bbox يساوي None) |
| استخراج الجداول من ملفات PDF غير الموسومة | محرّك الخادم | لا تُعيد أي جداول |
| قابلة للوصول من واجهة سطر الأوامر / خادم MCP | نعم | لا (عبر المكتبة فقط) |
| مُوصى بها لـ | الإنتاج | التطوير دون اتصال، واختبارات ملفات 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؛ وتشتق إخفاقات مستوى بروتوكول نقل النص التشعّبي (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]}أداة وكيل
قسم بعنوان «أداة وكيل»لوكلاء الذكاء الاصطناعي، شغّل خادم 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 من البيئة، لذلك فهو مدعوم عن بُعد؛ وكواجهة سطر الأوامر، لا يستطيع استخدام الخلفية المحلية. ثبّت الإضافة الاختيارية وشغّل الوحدة.
pip install "nextpdf[mcp]"python -m nextpdf.mcpراجع خادم Python MCP لجولة تكامل الوكيل، و واجهة سطر أوامر Python لاستخدام الطرفية، و مرجع واجهة Python البرمجية لكامل واجهة العميل والنموذج والاستثناءات.