پرش به محتوا

راهنما:نویسه‌خوان نوری

از ویکی‌نبشته
اجرای دسته‌جمعی نویسه‌خوان نوری (OCR) برای ویکی‌نبشته فارسی (Google Vision + Pywikibot)

این اسکریپت پایتون یک بازه از صفحات PDF را به تصویر تبدیل می‌کند، با سرویس Google Cloud Vision (حالت Document OCR) متن فارسی استخراج می‌کند، شمارهٔ صفحه را به ارقام فارسی تبدیل می‌کند و متن هر صفحه را در ویکی‌نبشته فارسی در قالب برگه ذخیره می‌کند. همهٔ توضیحات داخل کد به فارسی نوشته شده‌اند.

چه کاری انجام می‌دهد

[ویرایش]
  1. تبدیل بازه‌ای از صفحات PDF به تصویر (با pdf2image/Poppler)
  2. اجرای OCR گوگل برای هر صفحه
  3. تبدیل شمارهٔ صفحه به ارقام فارسی
  4. ذخیرهٔ متن در ویکی‌نبشته به‌صورت برگه:نام‌پی‌دی‌اف/شمارهٔ فارسی
  5. رد کردن صفحاتی که از قبل وجود دارند

پیش‌نیازها

[ویرایش]
  • Python 3.9+
  • حساب Google Cloud + فعال‌سازی Vision API + کلید سرویس (Service Account JSON)
  • نصب Poppler (برای pdf2image)
  • نصب Pywikibot و پیکربندی برای ویکی‌نبشته فارسی
  • نصب کتابخانه‌های پایتون:
  pip install pywikibot google-cloud-vision pdf2image pillow

Poppler در ویندوز

[ویرایش]

Poppler را دانلود و نصب کنید و پوشهٔ bin آن را به PATH اضافه کنید یا مسیر آن را به تابع convert_from_path بدهید.

پیکربندی گوگل ویژن

[ویرایش]
  1. یک Service Account بسازید و کلید JSON آن را دریافت کنید.
  2. متغیر محیطی زیر را ست کنید:
GOOGLE_APPLICATION_CREDENTIALS=/path/to/your-key.json
  1. یا مسیر کلید را مستقیم در اسکریپت بگذارید.

پیکربندی Pywikibot

[ویرایش]
  1. یک فایل user-config.py بسازید و مقادیر زیر را قرار دهید:
mylang = 'fa'
family = 'wikisource'
  1. یک بار ورود موفق داشته باشید (اسکریپت هم login را فراخوانی می‌کند).
  2. اگر پرچم ربات ندارید، botflag=True را از ذخیرهٔ صفحه حذف کنید.

متغیرهای اصلی

[ویرایش]
  • PDF_FILE: نام/مسیر فایل PDF
  • START_PAGE, END_PAGE: بازهٔ صفحات
  • GOOGLE_CREDENTIAL_PATH: مسیر فایل کلید گوگل
  • PAGE_NAMESPACE: فضای نام «برگه:» در ویکی‌نبشته فارسی

اجرا

[ویرایش]

اسکریپت را اجرا کنید. برای هر صفحه:

۱) تبدیل به تصویر (۳۰۰ dpi)، ۲) OCR، ۳) ذخیرهٔ متن در ویکی‌نبشته

نکات

[ویرایش]
  • مصرف Vision API ممکن است هزینه داشته باشد.
  • صفحات بدون خروجی OCR گزارش می‌شوند و ذخیره نمی‌شوند.
  • خلاصهٔ ویرایش پیش‌فرض: «افزودن متن با Google OCR»
  • فایل‌های تصویری موقت بعد از پردازش حذف می‌شوند.

مشکلات رایج

[ویرایش]
  • خطا در pdf2image: Poppler نصب نشده یا در PATH نیست.
  • ذخیره در Pywikibot انجام نمی‌شود: مشکل ورود یا دسترسی.
  • خطا در Vision API: کلید اشتباه یا سهمیه تمام شده است.
import os
import io
from typing import List, Optional

import pywikibot
from google.cloud import vision
from pdf2image import convert_from_path
from PIL import Image

# ------------------------------
# پیکربندی کاربر (این بخش را تغییر دهید)
# ------------------------------

# نام یا مسیر فایل PDF
PDF_FILE = "Tarikh-shhryary-shahnshah-rza-shah-phlvy.pdf"

# بازهٔ صفحات مورد نظر برای OCR (شامل ابتدا و انتها)
START_PAGE = 131
END_PAGE = 153

# مسیر کلید JSON سرویس گوگل (در صورت تنظیم متغیر محیطی می‌توان خالی گذاشت)
GOOGLE_CREDENTIAL_PATH = "E:/Lexeme/google_key.json"

# مسیر Poppler در ویندوز (در صورت نیاز)
POPLER_BIN: Optional[str] = None

# فضای نام برگه در ویکی‌نبشته فارسی
PAGE_NAMESPACE = "برگه:"

# کیفیت رندر صفحات (dpi)
RENDER_DPI = 300

# خلاصهٔ ویرایش
EDIT_SUMMARY = "افزودن متن با Google OCR"

# ------------------------------
# راه‌اندازی محیط و سرویس‌ها
# ------------------------------

# اگر مسیر کلید مشخص شده باشد، به‌صورت متغیر محیطی ست می‌شود
if GOOGLE_CREDENTIAL_PATH:
    os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = GOOGLE_CREDENTIAL_PATH

# ساخت کلاینت سرویس OCR گوگل
client = vision.ImageAnnotatorClient()

# اتصال به ویکی‌نبشته فارسی و ورود
site = pywikibot.Site("fa", "wikisource")
site.login()


# ------------------------------
# توابع کمکی
# ------------------------------

def latin_to_persian(number: int) -> str:
    """
    تبدیل اعداد لاتین به ارقام فارسی
    """
    persian_digits = {
        '0': '۰', '1': '۱', '2': '۲', '3': '۳', '4': '۴',
        '5': '۵', '6': '۶', '7': '۷', '8': '۸', '9': '۹'
    }
    return ''.join(persian_digits.get(d, d) for d in str(number))


def convert_pdf_to_images(pdf_path: str, start: int, end: int) -> List[Image.Image]:
    """
    تبدیل بازه‌ای از صفحات PDF به تصویر (با استفاده از Poppler)
    """
    kwargs = dict(dpi=RENDER_DPI, first_page=start, last_page=end)
    if POPLER_BIN:
        kwargs["poppler_path"] = POPLER_BIN
    return convert_from_path(pdf_path, **kwargs)


def run_ocr(image_path: str) -> str:
    """
    اجرای OCR گوگل روی تصویر و برگرداندن متن کامل
    """
    with io.open(image_path, 'rb') as img_file:
        content = img_file.read()
    image = vision.Image(content=content)

    response = client.document_text_detection(image=image)

    if response.error.message:
        raise RuntimeError(f"خطای سرویس گوگل: {response.error.message}")

    annotation = response.full_text_annotation
    return (annotation.text or "").strip() if annotation else ""


# ------------------------------
# اجرای اصلی
# ------------------------------

def main() -> None:
    # تبدیل صفحات PDF به تصویر
    images = convert_pdf_to_images(PDF_FILE, START_PAGE, END_PAGE)

    for idx, image in enumerate(images):
        page_number = START_PAGE + idx
        persian_number = latin_to_persian(page_number)

        # نام برگه در ویکی‌نبشته
        page_title = f"{PAGE_NAMESPACE}{PDF_FILE}/{persian_number}"
        print(f"🔎 OCR صفحه {page_number}{page_title}")

        page = pywikibot.Page(site, page_title)

        # اگر برگه از قبل وجود دارد، رد شود
        if page.exists():
            print(f"⏭ برگه وجود دارد: {page_title}")
            continue

        # ذخیرهٔ موقت تصویر برای OCR
        img_path = f"temp_page_{page_number}.jpg"
        image.save(img_path, "JPEG")

        try:
            text = run_ocr(img_path)
            if not text:
                print(f"⚠ نتیجهٔ OCR برای صفحه {page_number} خالی بود.")
                continue

            # ذخیرهٔ متن در ویکی‌نبشته
            page.text = text
            page.save(summary=EDIT_SUMMARY, botflag=True)
            print(f"✅ ذخیره شد: {page_title}")

        except Exception as e:
            print(f"❌ خطا در صفحه {page_number}: {e}")

        finally:
            # حذف تصویر موقت
            try:
                os.remove(img_path)
            except FileNotFoundError:
                pass

    print("🎉 عملیات OCR و بارگذاری به پایان رسید.")


if __name__ == "__main__":
    main()