اعمل OCR للفواتير: من PDF إلى Excel بـ Python
هتطلع من المقال بسكربت عملي يحوّل فواتير PDF أو صور الفواتير إلى ملف Excel منظم بدل النسخ اليدوي المتكرر.
مستوى القارئ: متوسط
المشكلة باختصار
لو عندك 100 فاتورة شهريًا، وبتنقل رقم الفاتورة، التاريخ، المورد، والإجمالي يدويًا، فأنت غالبًا بتصرف من ساعتين إلى 3 ساعات على شغل ممل وسهل الغلط. الطريقة الشائعة هي فتح كل PDF ونسخ القيم في Sheet. الطريقة دي بتفشل لما الفاتورة تكون صورة ممسوحة scan، أو لما المورد يغير شكل القالب.
الحل هنا مش نظام محاسبة كامل. الحل سكربت صغير يعمل pipeline واضحة: يحوّل الصفحة لصورة، ينظفها، يشغل OCR، يستخرج الحقول المهمة، ثم يكتب Excel. الافتراض إن الفواتير عندك إنجليزي أو أرقام واضحة، وحجمها أقل من 500 ملف في الدفعة الواحدة.
الفكرة بمثال واضح
ركز في المثال ده: عندك فولدر اسمه invoices/ فيه صور فواتير بصيغة PNG أو JPG. كل فاتورة فيها سطور شبه دي:
Invoice No: INV-1042
Date: 2026-04-21
Vendor: Acme Cloud
Total: $248.50السكريبت هيقرأ النص من الصورة، ثم يستخدم regex بسيط لاستخراج القيم. لو عندك PDF ممسوح ضوئيًا، ابدأ بتحويله لصورة أولًا باستخدام أداة زي pdftoppm أو أي export من نظامك. الهدف هنا إننا نثبت pipeline، وبعدها توسعها على PDF متعدد الصفحات.
جهّز البيئة
تحتاج Python، ومحرّك Tesseract نفسه، ثم مكتبات Python. مهم تفهم الفرق: pytesseract مجرد wrapper يستدعي Tesseract، وليس بديلًا عن تثبيت Tesseract على الجهاز.
# Ubuntu / Debian
sudo apt-get update
sudo apt-get install -y tesseract-ocr
# داخل مشروع Python
python -m venv .venv
source .venv/bin/activate
pip install pytesseract pillow opencv-python pandas openpyxlعلى Windows ثبّت Tesseract من الحزمة الرسمية أو Chocolatey، ثم تأكد إن الأمر tesseract --version يعمل من الطرفية.
اكتب السكربت
أفضل طريقة تبدأ بها هي تنظيف الصورة قبل OCR. اللي بيحصل فعلاً إن OCR يتأثر جدًا بالإضاءة، الظلال، ودقة scan. OpenCV adaptive thresholding يساعد لما الإضاءة مختلفة داخل نفس الصورة، لأنه يحدد العتبة حسب منطقة صغيرة بدل قيمة واحدة ثابتة.
from pathlib import Path
import re
import cv2
import pandas as pd
import pytesseract
from PIL import Image
INPUT_DIR = Path("invoices")
OUTPUT_FILE = "invoices.xlsx"
patterns = {
"invoice_no": r"Invoice\s*(?:No|Number)[:\s#-]+([A-Z0-9-]+)",
"date": r"Date[:\s]+(\d{4}-\d{2}-\d{2}|\d{2}/\d{2}/\d{4})",
"vendor": r"Vendor[:\s]+(.+)",
"total": r"Total[:\s$]+([0-9,.]+)",
}
def clean_image(path: Path) -> Image.Image:
img = cv2.imread(str(path), cv2.IMREAD_GRAYSCALE)
img = cv2.resize(img, None, fx=1.5, fy=1.5, interpolation=cv2.INTER_CUBIC)
img = cv2.adaptiveThreshold(
img, 255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY,
31, 11
)
return Image.fromarray(img)
def extract_fields(text: str) -> dict:
row = {"invoice_no": "", "date": "", "vendor": "", "total": ""}
for key, pattern in patterns.items():
match = re.search(pattern, text, re.IGNORECASE)
if match:
row[key] = match.group(1).strip()
return row
rows = []
for path in sorted(INPUT_DIR.glob("*.png")):
image = clean_image(path)
text = pytesseract.image_to_string(image, config="--psm 6")
row = extract_fields(text)
row["source_file"] = path.name
row["needs_review"] = not all(row[k] for k in ["invoice_no", "date", "total"])
rows.append(row)
pd.DataFrame(rows).to_excel(OUTPUT_FILE, index=False)
print(f"Wrote {len(rows)} invoices to {OUTPUT_FILE}")الـ trade-off هنا واضح: السكربت أسرع من النسخ اليدوي، لكنه ليس دقيقًا 100% مع كل تصميم فاتورة. لذلك أضفنا عمود needs_review. بتكسب وقت، وتخسر جزءًا من المرونة لأن regex مرتبط بتسميات الحقول.
اختبره بأرقام
في سيناريو واقعي لفريق صغير عنده 100 فاتورة شهريًا، النسخ اليدوي ممكن يأخذ 180 دقيقة لو كل فاتورة تحتاج دقيقة ونصف إلى دقيقتين. سكربت OCR أولي قد ينزل الوقت إلى 42 دقيقة مع مراجعة النتائج. بعد تنظيف الصورة وتحديد الحقول، الرقم الأقرب يكون 24 دقيقة، بشرط إن الفواتير واضحة ومش مكتوبة بخط يد.
القياس العملي بسيط: شغّل السكربت على 20 فاتورة، واحسب عدد الصفوف التي ظهرت فيها needs_review=True. لو النسبة أعلى من 20%، لا توسع السكربت قبل تحسين جودة الصور أو تعديل الـ regex.
ما يجب الانتباه له
- جودة الصورة أهم من المكتبة. صورة 300 DPI غالبًا أفضل من صورة مضغوطة من واتساب.
- القوالب المتعددة تحتاج طبقة قواعد. مورد له قالب مختلف يعني regex مختلف أو parser منفصل.
- الأرقام المالية تحتاج مراجعة. لا تجعل السكربت يدفع أو يرحّل قيودًا محاسبية بدون اعتماد بشري.
- اللغة العربية تحتاج تدريبًا وتجربة. Tesseract يدعم لغات متعددة، لكن جودة OCR العربي تختلف حسب الخط والصورة.
متى لا تستخدم هذه الطريقة
لا تستخدمها لو الفواتير قانونية حساسة جدًا ولا يسمح نظام شركتك بمعالجتها محليًا بدون موافقة. ولا تستخدمها لو الدقة المطلوبة 99.9% من أول مرة، لأن OCR يحتاج مراجعة. كذلك لا تبدأ بها لو عندك API رسمي من الموردين؛ الـ API أنظف وأقل أخطاء من قراءة الصور.
مصادر اعتمد عليها
- Tesseract command line documentation
- pytesseract package documentation
- OpenCV adaptive thresholding
- pandas DataFrame.to_excel
الخطوة التالية
خذ 20 فاتورة حقيقية من آخر شهر، شغّل السكربت عليها، ثم عدّل الـ regex حتى تنخفض نسبة needs_review تحت 20%. بعد ذلك فقط شغّله على كل الأرشيف.