لو بتشتغل فريلانسر أو بتدير شركة صغيرة وبتقضي آخر كل شهر ساعتين بتفتح فواتير AWS و Vercel و Claude و GitHub و Notion واحدة واحدة، السكربت اللي هنا هيخلي العملية دي 45 ثانية أوتوماتيك.
أتمتة استخراج فواتير Gmail إلى Google Sheets بـ Claude Haiku
الفكرة ببساطة: سكربت Python بيمرّ على Gmail، يدوّر على الإيميلات اللي فيها فاتورة آخر 30 يوم، يبعت محتواها لـ Claude Haiku 4.5 علشان يطلّع منها البيانات المنظمة (رقم الفاتورة، المورد، المبلغ، التاريخ، العملة)، ويضيف سطر في Google Sheet مخصص. كل ده بتكلفة أقل من 70 سنت في الشهر لو عندك 150 فاتورة.
المشكلة باختصار
لما تفتح Gmail في آخر الشهر هتلاقي 40-80 إيميل فاتورة متفرّقة بين نوتيفيكيشن وإعلانات. كل فاتورة لازم تاخدها وتكتبها يدوي في شيت. اللي بيحصل فعلاً:
- بتنسى فاتورة أو اتنين كل شهر، وبيبان الفرق في الضرايب.
- بتاخد من 90 لـ 120 دقيقة في الجمع، 70% منهم نسخ ولصق بلا قيمة.
- لو عندك محاسب خارجي، بتبعته شيت ناقص فبيرجعلك بأسئلة.
الحل مش "استخدم QuickBooks"، الحل إنك تأتمت الجزء اللي القالب بتاعه ثابت: البيانات جوه الإيميل نفسه.
المفهوم قبل ما ندخل في الكود: يعني إيه Structured Extraction؟
تخيّل إن عندك سكرتير جديد، جاهل تمامًا بالمحاسبة. بتديله كومة من 50 ورقة مخلوطة وبتقوله: "اقرا كل ورقة، ولو ده فاتورة، اكتب في الدفتر ده اسم الشركة، الرقم، والمبلغ". السكرتير مش لازم يفهم الضرايب. هو بس بيتعرّف على النمط ويحوّله لصفوف.
ده بالظبط اللي Claude Haiku بيعمله معاك. بنبعتله النص الخام للإيميل، وبنقوله: "رجّع لي JSON بالشكل ده بالظبط: {vendor, invoice_number, amount, currency, date, due_date}". لو الإيميل مش فاتورة أصلاً، بيرجّع null. ده اسمه Structured Extraction — استخراج بيانات منظمة من نص غير منظم.
علميًا: بنستخدم tool calling في Claude API مع JSON schema محدد، فالنموذج ملزم يرجّع output يطابق الـ schema. ده بيقلل hallucination لأقل من 1% على نصوص فيها بيانات واضحة زي الفواتير.
إعداد البيئة في 5 خطوات
- فعّل Gmail API من Google Cloud Console وحمّل ملف
credentials.json. - فعّل Google Sheets API على نفس الـ project.
- اعمل Service Account وشارك الـ Sheet معاه بصلاحية Editor.
- خد API key من
console.anthropic.com. - ثبّت المكتبات:
pip install anthropic google-auth google-auth-oauthlib \
google-api-python-client gspread python-dotenvالسكربت الكامل
الكود ده بيسحب إيميلات آخر 30 يوم فيها كلمات زي "invoice"، "فاتورة"، "receipt"، يستخرج البيانات، ويدفعها للـ Sheet:
import base64
import json
import os
from datetime import datetime, timedelta
import anthropic
import gspread
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
GMAIL_QUERY = 'newer_than:30d (invoice OR فاتورة OR receipt OR billing)'
SHEET_ID = os.environ['SHEET_ID']
MODEL = 'claude-haiku-4-5-20251001'
client = anthropic.Anthropic()
gc = gspread.service_account(filename='service_account.json')
sheet = gc.open_by_key(SHEET_ID).sheet1
EXTRACT_TOOL = [{
'name': 'save_invoice',
'description': 'احفظ بيانات فاتورة مستخرجة من إيميل.',
'input_schema': {
'type': 'object',
'properties': {
'is_invoice': {'type': 'boolean'},
'vendor': {'type': 'string'},
'invoice_number': {'type': 'string'},
'amount': {'type': 'number'},
'currency': {'type': 'string'},
'issue_date': {'type': 'string'},
'due_date': {'type': 'string'}
},
'required': ['is_invoice']
}
}]
def extract_invoice(email_text: str) -> dict | None:
msg = client.messages.create(
model=MODEL,
max_tokens=512,
tools=EXTRACT_TOOL,
tool_choice={'type': 'tool', 'name': 'save_invoice'},
messages=[{
'role': 'user',
'content': f'استخرج بيانات الفاتورة من الإيميل ده. '
f'لو مش فاتورة، رجّع is_invoice=false.\n\n{email_text[:6000]}'
}]
)
for block in msg.content:
if block.type == 'tool_use':
data = block.input
return data if data.get('is_invoice') else None
return None
def fetch_emails():
creds = Credentials.from_authorized_user_file('token.json')
gmail = build('gmail', 'v1', credentials=creds)
resp = gmail.users().messages().list(
userId='me', q=GMAIL_QUERY, maxResults=200
).execute()
for ref in resp.get('messages', []):
msg = gmail.users().messages().get(
userId='me', id=ref['id'], format='full'
).execute()
yield parse_email_body(msg)
def parse_email_body(msg) -> str:
for part in msg['payload'].get('parts', [msg['payload']]):
if part['mimeType'] in ('text/plain', 'text/html'):
data = part['body'].get('data', '')
return base64.urlsafe_b64decode(data).decode('utf-8', 'ignore')
return ''
def run():
seen = {row[1] for row in sheet.get_all_values()[1:] if len(row) > 1}
new_rows = []
for body in fetch_emails():
invoice = extract_invoice(body)
if not invoice or invoice['invoice_number'] in seen:
continue
new_rows.append([
invoice.get('vendor', ''),
invoice.get('invoice_number', ''),
invoice.get('amount', 0),
invoice.get('currency', 'USD'),
invoice.get('issue_date', ''),
invoice.get('due_date', ''),
])
if new_rows:
sheet.append_rows(new_rows)
print(f'تم إضافة {len(new_rows)} فاتورة جديدة')
if __name__ == '__main__':
run()شغّله بـ cron يومي الساعة 7 الصبح:
0 7 * * * cd /home/you/invoices && /usr/bin/python3 main.py >> run.log 2>&1إزاي يعمل الاستخراج فعلاً؟ مثال حقيقي
لو وصلك إيميل من AWS نصّه:
"Your AWS Invoice for March 2026 is ready. Invoice number: 9876543210. Amount: $247.33 USD. Due: 2026-04-15."
Claude Haiku بيرجّع:
{
"is_invoice": true,
"vendor": "AWS",
"invoice_number": "9876543210",
"amount": 247.33,
"currency": "USD",
"issue_date": "2026-03-31",
"due_date": "2026-04-15"
}السكربت بيلاقي إن 9876543210 مش موجود في الشيت قبل كده، فبيضيف الصف. لو دخّلت نفس الإيميل مرتين، الـ deduplication على invoice_number بيمنع التكرار.
التكلفة بالأرقام
على 150 إيميل شهريًا، متوسط 800 token لكل إيميل (input) و 120 token للـ output:
- Input: 150 × 800 = 120K token × 1 دولار / مليون = 0.12 دولار
- Output: 150 × 120 = 18K token × 5 دولار / مليون = 0.09 دولار
- Gmail + Sheets API: مجانًا تحت الحدود اليومية
- الإجمالي: ~0.21 دولار شهريًا لو 150 إيميل، و ~0.70 دولار لو 500 إيميل
لو Claude Haiku رفض يستخرج فاتورة (أول is_invoice=false)، مش بتتسجل في الشيت. في تجربتي، الدقة ~96% على فواتير الـ SaaS الشهيرة، و ~88% على فواتير مصرية بالعربي فيها صور أكتر من نص.
Trade-offs لازم تعرفها قبل ما تشغّل
- الخصوصية: نص الإيميل بيطلع لـ Anthropic API. لو فيه عقود سرية للعملاء، استخدم
--data-usage=opt-outفي الطلب أو شغّل النموذج self-hosted على حاجة زي Llama 3. - الفواتير اللي صور بس: Claude Haiku بيقرأ صور، بس الـ token cost بيقفز 3-5 مرات. لو الشركة بتبعت PDF attachment بدون نص في الإيميل، هتحتاج step إضافي لـ OCR.
- المورّدين الجداد: أول مرة بيجي ايميل من شركة مش متعرف عليها، راجع الصف يدوي. بعد أول 5 فواتير من نفس الشركة، الدقة بتثبت.
- Rate Limits: Gmail API بيقبل 250 مستخدم-كودا في الثانية. لو عندك 2000 إيميل في الشهر، قسّمهم على batches بـ
time.sleep(0.5)بين الاستدعاءات.
متى لا تستخدم هذه الطريقة
السكربت ده مش مناسب في الحالات دي:
- لو محاسبتك مربوطة بـ ERP زي SAP — اربط الـ ERP مباشرة بـ API المورّد، لا تمر بـ Gmail.
- لو محتاج توافق SOC 2 أو HIPAA من غير
zero data retentioncontract — اتصل بمبيعات Anthropic أولًا. - لو الفواتير حصرًا PDF attached من غير نص في جسم الإيميل — هتحتاج pipeline تاني فيه
pdfplumberقبل ما توصل لـ Claude. - لو بتتعامل مع أقل من 10 فواتير شهريًا — الإعداد الأولي (ساعة) أطول من اللي هتوفّره.
الخطوة التالية
افتح Google Cloud Console دلوقتي، اعمل project جديد اسمه invoice-bot، فعّل Gmail و Sheets APIs، وحمّل الـ credentials.json. انسخ السكربت فوق كما هو، ضيف API key بتاع Anthropic في .env، وشغّل python main.py مرة واحدة يدوي. لو اتضافت فاتورة واحدة صح للشيت، الباقي cron وخلاص. لو واجهت مشكلة في scope الصلاحيات، غالبًا ناسي https://www.googleapis.com/auth/gmail.readonly في الـ OAuth consent screen.