پکیج 'email' پایتون را باز کنید. یاد بگیرید چگونه پیامهای پیچیده MIME را بسازید و ایمیلهای ورودی را برای استخراج داده به طور مؤثر و جهانی تجزیه کنید.
تسلط بر پکیج ایمیل پایتون: هنر ساخت پیام MIME و تجزیه قدرتمند
ایمیل همچنان یکی از سنگ بناهای ارتباطات جهانی است و برای مکاتبات شخصی، عملیات تجاری و اعلانهای خودکار سیستم، ضروری است. پشت هر ایمیل با متن غنی (rich-text)، هر پیوست و هر امضای با فرمت دقیق، پیچیدگیهای افزونههای چندمنظوره پست اینترنتی (MIME) نهفته است. برای توسعهدهندگان، بهویژه کسانی که با پایتون کار میکنند، تسلط بر نحوه ساخت و تجزیه برنامهریزیشده این پیامهای MIME یک مهارت حیاتی است.
پکیج داخلی email
پایتون یک چارچوب قدرتمند و جامع برای مدیریت پیامهای ایمیل فراهم میکند. این پکیج فقط برای ارسال متن ساده نیست؛ بلکه برای پنهان کردن جزئیات پیچیده MIME طراحی شده است و به شما امکان میدهد ایمیلهای پیشرفتهای بسازید و دادههای خاصی را از ایمیلهای ورودی با دقت قابل توجهی استخراج کنید. این راهنما شما را به یک بررسی عمیق از دو جنبه اصلی این پکیج میبرد: ساخت پیامهای MIME برای ارسال و تجزیه آنها برای استخراج داده، با ارائه یک دیدگاه جهانی در مورد بهترین شیوهها.
درک هر دو فرآیند ساخت و تجزیه بسیار مهم است. وقتی پیامی را میسازید، در واقع ساختار و محتوای آن را برای تفسیر توسط یک سیستم دیگر تعریف میکنید. وقتی تجزیه میکنید، در حال تفسیر ساختار و محتوایی هستید که توسط سیستم دیگری تعریف شده است. درک عمیق از یکی، به تسلط بر دیگری کمک شایانی میکند و منجر به ایجاد برنامههای ایمیل انعطافپذیرتر و با قابلیت همکاری بیشتر میشود.
درک MIME: ستون فقرات ایمیل مدرن
قبل از پرداختن به جزئیات پایتون، درک اینکه MIME چیست و چرا اینقدر حیاتی است، ضروری است. در ابتدا، پیامهای ایمیل به متن ساده (کاراکترهای 7 بیتی ASCII) محدود بودند. MIME که در اوایل دهه 1990 معرفی شد، قابلیتهای ایمیل را برای پشتیبانی از موارد زیر گسترش داد:
- کاراکترهای غیر-ASCII: امکان استفاده از متن به زبانهایی مانند عربی، چینی، روسی یا هر زبان دیگری که از کاراکترهای خارج از مجموعه ASCII استفاده میکند.
- پیوستها: ارسال فایلهایی مانند اسناد، تصاویر، صدا و ویدئو.
- قالببندی متن غنی: ایمیلهای HTML با قابلیت ضخیم کردن، کج کردن، رنگها و طرحبندیها.
- بخشهای چندگانه: ترکیب متن ساده، HTML و پیوستها در یک ایمیل واحد.
MIME این کار را با افزودن هدرهای خاص به پیام ایمیل و ساختاربندی بدنه آن به «بخشهای» مختلف انجام میدهد. هدرهای کلیدی MIME که با آنها مواجه خواهید شد عبارتند از:
Content-Type:
نوع داده در یک بخش را مشخص میکند (مثلاًtext/plain
،text/html
،image/jpeg
،application/pdf
،multipart/alternative
). این هدر اغلب شامل یک پارامترcharset
نیز میشود (مثلاًcharset=utf-8
).Content-Transfer-Encoding:
نشان میدهد که کلاینت ایمیل چگونه باید محتوا را رمزگشایی کند (مثلاًbase64
برای دادههای باینری،quoted-printable
برای متنی که بیشتر شامل کاراکترهای ASCII است اما تعدادی کاراکتر غیر-ASCII نیز دارد).Content-Disposition:
پیشنهاد میکند که کلاینت ایمیل گیرنده چگونه باید بخش را نمایش دهد (مثلاًinline
برای نمایش در بدنه پیام،attachment
برای فایلی که باید ذخیره شود).
پکیج email
پایتون: یک بررسی عمیق
پکیج email
پایتون یک کتابخانه جامع است که برای ایجاد، تجزیه و تغییر پیامهای ایمیل به صورت برنامهریزیشده طراحی شده است. این پکیج بر اساس مفهوم اشیاء Message
ساخته شده است که ساختار یک ایمیل را نشان میدهند.
ماژولهای کلیدی در این پکیج عبارتند از:
email.message:
شامل کلاس اصلیEmailMessage
است که رابط اصلی برای ایجاد و دستکاری پیامهای ایمیل است. این یک کلاس بسیار انعطافپذیر است که جزئیات MIME را به طور خودکار مدیریت میکند.email.mime:
کلاسهای قدیمیتری (مانندMIMEText
،MIMEMultipart
) را فراهم میکند که کنترل صریحتری بر ساختار MIME ارائه میدهند. در حالی کهEmailMessage
به دلیل سادگی آن برای کدهای جدید ترجیح داده میشود، درک این کلاسها میتواند مفید باشد.email.parser:
کلاسهایی مانندBytesParser
وParser
را برای تبدیل دادههای خام ایمیل (بایت یا رشته) به اشیاءEmailMessage
ارائه میدهد.email.policy:
سیاستهایی را تعریف میکند که نحوه ساخت و تجزیه پیامهای ایمیل را کنترل میکنند و بر جنبههایی مانند انکودینگ هدر، پایان خطوط و مدیریت خطا تأثیر میگذارند.
برای اکثر موارد استفاده مدرن، شما عمدتاً با کلاس email.message.EmailMessage
هم برای ساخت و هم به عنوان یک شیء پیام تجزیه شده تعامل خواهید داشت. متدهای آن فرآیندی را که قبلاً با کلاسهای قدیمی email.mime
طولانیتر بود، بسیار ساده میکند.
ساخت پیام MIME: ساخت ایمیل با دقت
ساخت ایمیل شامل مونتاژ اجزای مختلف (متن، HTML، پیوستها) در یک ساختار معتبر MIME است. کلاس EmailMessage
این فرآیند را به طور قابل توجهی ساده میکند.
ایمیلهای متنی ساده
سادهترین ایمیل، متن ساده است. میتوانید یکی از آنها را ایجاد کرده و هدرهای اصلی را به راحتی تنظیم کنید:
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Greetings from Python'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
msg.set_content('Hello, this is a plain text email sent from Python.\n\nBest regards,\nYour Python Script')
print(msg.as_string())
توضیح:
EmailMessage()
یک شیء پیام خالی ایجاد میکند.- دسترسی شبهدیکشنری (
msg['Subject'] = ...
) هدرهای رایج را تنظیم میکند. set_content()
محتوای اصلی ایمیل را اضافه میکند. به طور پیشفرض، این متدContent-Type: text/plain; charset="utf-8"
را استنباط میکند.as_string()
پیام را به فرمت رشتهای مناسب برای ارسال از طریق SMTP یا ذخیره در یک فایل، سریالسازی میکند.
افزودن محتوای HTML
برای ارسال یک ایمیل HTML، کافی است نوع محتوا را هنگام فراخوانی set_content()
مشخص کنید. ارائه یک جایگزین متن ساده برای گیرندگانی که کلاینت ایمیل آنها HTML را رندر نمیکند یا به دلایل دسترسیپذیری، یک عمل خوب است.
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Your HTML Newsletter'
msg['From'] = 'newsletter@example.com'
msg['To'] = 'subscriber@example.com'
html_content = """
<html>
<head></head>
<body>
<h1>Welcome to Our Global Update!</h1>
<p>Dear Subscriber,</p>
<p>This is your <strong>latest update</strong> from around the world.</p>
<p>Visit our <a href="http://www.example.com">website</a> for more.</p>
<p>Best regards,<br>The Team</p>
</body>
</html>
"""
# Add the HTML version
msg.add_alternative(html_content, subtype='html')
# Add a plain text fallback
plain_text_content = (
"Welcome to Our Global Update!\n\n"
"Dear Subscriber,\n\n"
"This is your latest update from around the world.\n"
"Visit our website for more: http://www.example.com\n\n"
"Best regards,\nThe Team"
)
msg.add_alternative(plain_text_content, subtype='plain')
print(msg.as_string())
توضیح:
add_alternative()
برای افزودن نمایشهای مختلف از *همان* محتوا استفاده میشود. کلاینت ایمیل «بهترین» موردی را که میتواند مدیریت کند (معمولاً HTML) نمایش میدهد.- این کار به طور خودکار یک ساختار MIME از نوع
multipart/alternative
ایجاد میکند.
مدیریت پیوستها
پیوست کردن فایلها با استفاده از add_attachment()
ساده است. میتوانید هر نوع فایلی را پیوست کنید و پکیج، انواع MIME و انکودینگهای مناسب (معمولاً base64
) را مدیریت میکند.
from email.message import EmailMessage
from pathlib import Path
# Create dummy files for demonstration
Path('report.pdf').write_bytes(b'%PDF-1.4\n1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj\n2 0 obj<</Count 0>>endobj\nxref\n0 3\n0000000000 65535 f\n0000000009 00000 n\n0000000052 00000 n\ntrailer<</Size 3/Root 1 0 R>>startxref\n104\n%%EOF') # A very basic, invalid PDF placeholder
Path('logo.png').write_bytes(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\x0cIDAT\x08\x99c`\x00\x00\x00\x02\x00\x01\xe2!\x00\xa0\x00\x00\x00\x00IEND\xaeB`\x82') # A 1x1 transparent PNG placeholder
msg = EmailMessage()
msg['Subject'] = 'Important Document and Image'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
msg.set_content('Please find the attached report and company logo.')
# Attach a PDF file
with open('report.pdf', 'rb') as f:
file_data = f.read()
msg.add_attachment(
file_data,
maintype='application',
subtype='pdf',
filename='Annual_Report_2024.pdf'
)
# Attach an image file
with open('logo.png', 'rb') as f:
image_data = f.read()
msg.add_attachment(
image_data,
maintype='image',
subtype='png',
filename='CompanyLogo.png'
)
print(msg.as_string())
# Clean up dummy files
Path('report.pdf').unlink()
Path('logo.png').unlink()
توضیح:
add_attachment()
بایتهای خام محتوای فایل را میگیرد.maintype
وsubtype
نوع MIME را مشخص میکنند (مثلاًapplication/pdf
،image/png
). اینها برای شناسایی و مدیریت صحیح پیوست توسط کلاینت ایمیل گیرنده حیاتی هستند.filename
نامی را که پیوست تحت آن توسط گیرنده ذخیره میشود، ارائه میدهد.- این کار به طور خودکار یک ساختار
multipart/mixed
ایجاد میکند.
ایجاد پیامهای چندبخشی
وقتی پیامی دارید که هم بدنه HTML، هم یک جایگزین متن ساده، و هم تصاویر درونخطی یا فایلهای مرتبط دارد، به یک ساختار چندبخشی پیچیدهتر نیاز دارید. کلاس EmailMessage
این کار را به طور هوشمندانه با add_related()
و add_alternative()
مدیریت میکند.
یک سناریوی رایج، یک ایمیل HTML با تصویری است که مستقیماً در داخل HTML تعبیه شده است (یک تصویر «درونخطی» یا "inline"). این کار از multipart/related
استفاده میکند.
from email.message import EmailMessage
from pathlib import Path
# Create a dummy image file for demonstration (a 1x1 transparent PNG)
Path('banner.png').write_bytes(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\x0cIDAT\x08\x99c`\x00\x00\x00\x02\x00\x01\xe2!\x00\xa0\x00\x00\x00\x00IEND\xaeB`\x82')
msg = EmailMessage()
msg['Subject'] = 'Inline Image Example'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
# Plain text version (fallback)
plain_text = 'Check out our amazing banner!\n\n[Image: Banner.png]\n\nVisit our site.'
msg.set_content(plain_text, subtype='plain') # Set initial plain text content
# HTML version (with CID for inline image)
html_content = """
<html>
<head></head>
<body>
<h1>Our Latest Offer!</h1>
<p>Dear Customer,</p>
<p>Don't miss out on our special global promotion:</p>
<img src="cid:my-banner-image" alt="Promotion Banner">
<p>Click <a href="http://www.example.com">here</a> to learn more.</p>
</body>
</html>
"""
msg.add_alternative(html_content, subtype='html') # Add HTML alternative
# Add the inline image (related content)
with open('banner.png', 'rb') as img_file:
image_data = img_file.read()
msg.add_related(
image_data,
maintype='image',
subtype='png',
cid='my-banner-image' # This CID matches the 'src' in HTML
)
print(msg.as_string())
# Clean up dummy file
Path('banner.png').unlink()
توضیح:
set_content()
محتوای اولیه را ایجاد میکند (در اینجا، متن ساده).add_alternative()
نسخه HTML را اضافه میکند و یک ساختارmultipart/alternative
ایجاد میکند که شامل بخشهای متن ساده و HTML است.add_related()
برای محتوایی استفاده میشود که به یکی از بخشهای پیام «مرتبط» است، معمولاً تصاویر درونخطی در HTML. این متد یک پارامترcid
(Content-ID) میگیرد که سپس در تگ HTML<img src="cid:my-banner-image">
به آن ارجاع داده میشود.- ساختار نهایی
multipart/mixed
خواهد بود (اگر پیوستهای خارجی وجود داشت) که شامل یک بخشmultipart/alternative
است، که به نوبه خود شامل یک بخشmultipart/related
است. بخشmultipart/related
شامل HTML و تصویر درونخطی است. کلاسEmailMessage
این پیچیدگی تودرتو را برای شما مدیریت میکند.
انکودینگ و مجموعههای کاراکتری برای دسترسی جهانی
برای ارتباطات بینالمللی، انکودینگ صحیح کاراکترها از اهمیت بالایی برخوردار است. پکیج email
، به طور پیشفرض، به شدت بر استفاده از UTF-8 اصرار دارد که استاندارد جهانی برای مدیریت مجموعههای کاراکتری متنوع از سراسر جهان است.
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Global Characters: こんにちは, Привет, नमस्ते'
msg['From'] = 'global_sender@example.com'
msg['To'] = 'global_recipient@example.com'
# Japanese, Russian, and Hindi characters
content = "This message contains diverse global characters:\n"
content += "こんにちは (Japanese)\n"
content += "Привет (Russian)\n"
content += "नमस्ते (Hindi)\n\n"
content += "The 'email' package handles UTF-8 gracefully."
msg.set_content(content)
print(msg.as_string())
توضیح:
- وقتی
set_content()
یک رشته پایتون دریافت میکند، به طور خودکار آن را به UTF-8 انکود کرده و هدرContent-Type: text/plain; charset="utf-8"
را تنظیم میکند. - اگر محتوا نیاز داشته باشد (مثلاً شامل کاراکترهای غیر-ASCII زیادی باشد)، ممکن است
Content-Transfer-Encoding: quoted-printable
یاbase64
را نیز برای اطمینان از انتقال امن بر روی سیستمهای ایمیل قدیمیتر اعمال کند. پکیج این کار را به طور خودکار مطابق با سیاست انتخاب شده انجام میدهد.
هدرها و سیاستهای سفارشی
شما میتوانید هر هدر سفارشی را به یک ایمیل اضافه کنید. سیاستها (از email.policy
) نحوه مدیریت پیامها را تعریف میکنند و بر جنبههایی مانند انکودینگ هدر، پایان خطوط و مدیریت خطا تأثیر میگذارند. سیاست پیشفرض به طور کلی خوب است، اما میتوانید `SMTP` را برای انطباق دقیق با SMTP انتخاب کنید یا سیاستهای سفارشی تعریف کنید.
from email.message import EmailMessage
from email import policy
msg = EmailMessage(policy=policy.SMTP)
msg['Subject'] = 'Email with Custom Header'
msg['From'] = 'info@example.org'
msg['To'] = 'user@example.org'
msg['X-Custom-Header'] = 'This is a custom value for tracking'
msg['Reply-To'] = 'support@example.org'
msg.set_content('This email demonstrates custom headers and policies.')
print(msg.as_string())
توضیح:
- استفاده از
policy=policy.SMTP
انطباق دقیق با استانداردهای SMTP را تضمین میکند که میتواند برای قابلیت تحویل ایمیل حیاتی باشد. - هدرهای سفارشی دقیقاً مانند هدرهای استاندارد اضافه میشوند. آنها اغلب با
X-
شروع میشوند تا هدرهای غیراستاندارد را مشخص کنند.
تجزیه پیام MIME: استخراج اطلاعات از ایمیلهای ورودی
تجزیه شامل گرفتن دادههای خام ایمیل (معمولاً از طریق IMAP یا از یک فایل دریافت میشود) و تبدیل آن به یک شیء `EmailMessage` است که سپس میتوانید به راحتی آن را بررسی و دستکاری کنید.
بارگیری و تجزیه اولیه
شما معمولاً ایمیلها را به صورت بایتهای خام دریافت میکنید. برای این کار از email.parser.BytesParser
(یا توابع کمکی email.message_from_bytes()
) استفاده میشود.
from email.parser import BytesParser
from email.policy import default
raw_email_bytes = b"""
From: sender@example.com
To: recipient@example.com
Subject: Test Email with Basic Headers
Date: Mon, 1 Jan 2024 10:00:00 +0000
Content-Type: text/plain; charset="utf-8"
This is the body of the email.
It's a simple test.
"""
# Using BytesParser
parser = BytesParser(policy=default)
msg = parser.parsebytes(raw_email_bytes)
# Or using the convenience function
# from email import message_from_bytes
# msg = message_from_bytes(raw_email_bytes, policy=default)
print(f"Subject: {msg['subject']}")
print(f"From: {msg['from']}")
print(f"Content-Type: {msg['Content-Type']}")
توضیح:
BytesParser
دادههای بایت خام (که ایمیلها به این شکل منتقل میشوند) را گرفته و یک شیءEmailMessage
برمیگرداند.policy=default
قوانین تجزیه را مشخص میکند.
دسترسی به هدرها
هدرها به راحتی از طریق کلیدهای شبهدیکشنری قابل دسترسی هستند. پکیج به طور خودکار رمزگشایی هدرهای انکود شده (مانند موضوعات با کاراکترهای بینالمللی) را انجام میدهد.
# ... (using the 'msg' object from the previous parsing example)
print(f"Date: {msg['date']}")
print(f"Message ID: {msg['Message-ID'] if 'Message-ID' in msg else 'N/A'}")
# Handling multiple headers (e.g., 'Received' headers)
# from email.message import EmailMessage # If not imported yet
# from email import message_from_string # For a quick string example
multi_header_email = message_from_string(
"""
From: a@example.com
To: b@example.com
Subject: Multi-header Test
Received: from client.example.com (client.example.com [192.168.1.100])
by server.example.com (Postfix) with ESMTP id 123456789
for <b@example.com>; Mon, 1 Jan 2024 10:00:00 +0000 (GMT)
Received: from mx.another.com (mx.another.com [192.168.1.101])
by server.example.com (Postfix) with ESMTP id 987654321
for <b@example.com>; Mon, 1 Jan 2024 09:59:00 +0000 (GMT)
Body content here.
"""
)
received_headers = multi_header_email.get_all('received')
if received_headers:
print("\nReceived Headers:")
for header in received_headers:
print(f"- {header}")
توضیح:
- دسترسی به یک هدر، مقدار آن را به عنوان یک رشته برمیگرداند.
get_all('header-name')
برای هدرهایی که میتوانند چندین بار ظاهر شوند (مانندReceived
) مفید است.- پکیج رمزگشایی هدرها را انجام میدهد، بنابراین مقادیری مانند
Subject: =?utf-8?Q?Global_Characters:_=E3=81=93=E3=82=93=E3=81=AB=E3=81=A1=E3=81=AF?=
به طور خودکار به رشتههای قابل خواندن تبدیل میشوند.
استخراج محتوای بدنه
استخراج بدنه واقعی پیام نیازمند بررسی این است که آیا پیام چندبخشی است یا خیر. برای پیامهای چندبخشی، شما در میان بخشهای آن پیمایش میکنید.
from email.message import EmailMessage
from email import message_from_string
multipart_email_raw = """
From: multi@example.com
To: user@example.com
Subject: Test Multipart Email
Content-Type: multipart/alternative; boundary="_----------=_12345"
--_----------=_12345
Content-Type: text/plain; charset="utf-8"
Hello from the plain text part!
--_----------=_12345
Content-Type: text/html; charset="utf-8"
<html>
<body>
<h1>Hello from the HTML part!</h1>
<p>This is a <strong>rich text</strong> email.</p>
</body>
</html>
--_----------=_12345--
"""
msg = message_from_string(multipart_email_raw)
if msg.is_multipart():
print("\n--- Multipart Email Body ---")
for part in msg.iter_parts():
content_type = part.get_content_type()
charset = part.get_content_charset() or 'utf-8' # Default to utf-8 if not specified
payload = part.get_payload(decode=True) # Decode payload bytes
try:
decoded_content = payload.decode(charset)
print(f"Content-Type: {content_type}, Charset: {charset}\nContent:\n{decoded_content}\n")
except UnicodeDecodeError:
print(f"Content-Type: {content_type}, Charset: {charset}\nContent: (Binary or undecodable data)\n")
# Handle binary data, or attempt a fallback encoding
else:
print("\n--- Single Part Email Body ---")
charset = msg.get_content_charset() or 'utf-8'
payload = msg.get_payload(decode=True)
try:
decoded_content = payload.decode(charset)
print(f"Content-Type: {msg.get_content_type()}, Charset: {charset}\nContent:\n{decoded_content}\n")
except UnicodeDecodeError:
print(f"Content: (Binary or undecodable data)\n")
توضیح:
is_multipart()
تعیین میکند که آیا ایمیل دارای چندین بخش است.iter_parts()
در میان تمام زیربخشهای یک پیام چندبخشی پیمایش میکند.get_content_type()
نوع MIME کامل را برمیگرداند (مثلاًtext/plain
).get_content_charset()
مجموعه کاراکتر (charset) را از هدرContent-Type
استخراج میکند.get_payload(decode=True)
حیاتی است: این متد محتوای *رمزگشاییشده* را به صورت بایت برمیگرداند. سپس باید این بایتها را با استفاده از مجموعه کاراکتر صحیح.decode()
کنید تا یک رشته پایتون به دست آورید.
مدیریت پیوستها هنگام تجزیه
پیوستها نیز بخشهایی از یک پیام چندبخشی هستند. میتوانید آنها را با استفاده از هدر Content-Disposition
شناسایی کرده و محتوای رمزگشاییشده آنها را ذخیره کنید.
from email.message import EmailMessage
from email import message_from_string
import os
# Example email with a simple attachment
email_with_attachment = """
From: attach@example.com
To: user@example.com
Subject: Document Attached
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="_----------=_XYZ"
--_----------=_XYZ
Content-Type: text/plain; charset="utf-8"
Here is your requested document.
--_----------=_XYZ
Content-Type: application/pdf
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="document.pdf"
JVBERi0xLjQKMSAwIG9iagpbL1BERi9UZXh0L0ltYWdlQy9JbWFnZUkvSW1hZ0VCXQplbmRvYmoK
--_----------=_XYZ--
"""
msg = message_from_string(email_with_attachment)
output_dir = 'parsed_attachments'
os.makedirs(output_dir, exist_ok=True)
print("\n--- Processing Attachments ---")
for part in msg.iter_attachments():
filename = part.get_filename()
if filename:
filepath = os.path.join(output_dir, filename)
try:
with open(filepath, 'wb') as f:
f.write(part.get_payload(decode=True))
print(f"Saved attachment: {filepath} (Type: {part.get_content_type()})")
except Exception as e:
print(f"Error saving {filename}: {e}")
else:
print(f"Found an attachment without a filename (Content-Type: {part.get_content_type()})")
# Clean up the output directory
# import shutil
# shutil.rmtree(output_dir)
توضیح:
iter_attachments()
به طور خاص بخشهایی را که احتمالاً پیوست هستند (یعنی دارای هدرContent-Disposition: attachment
هستند یا به طریق دیگری طبقهبندی نشدهاند) برمیگرداند.get_filename()
نام فایل را از هدرContent-Disposition
استخراج میکند.part.get_payload(decode=True)
محتوای باینری خام پیوست را بازیابی میکند، که قبلاً ازbase64
یاquoted-printable
رمزگشایی شده است.
رمزگشایی انکودینگها و مجموعههای کاراکتری
پکیج email
در رمزگشایی خودکار انکودینگهای انتقال رایج (مانند base64
، quoted-printable
) هنگام فراخوانی get_payload(decode=True)
عملکرد بسیار خوبی دارد. برای خود محتوای متنی، سعی میکند از charset
مشخص شده در هدر Content-Type
استفاده کند. اگر هیچ مجموعه کاراکتری مشخص نشده باشد یا نامعتبر باشد، ممکن است نیاز به مدیریت مناسب آن داشته باشید.
from email.message import EmailMessage
from email import message_from_string
# Example with a potentially problematic charset
email_latin1 = """
From: legacy@example.com
To: new_system@example.com
Subject: Special characters: àéíóú
Content-Type: text/plain; charset="iso-8859-1"
This message contains Latin-1 characters: àéíóú
"""
msg = message_from_string(email_latin1)
if msg.is_multipart():
for part in msg.iter_parts():
payload = part.get_payload(decode=True)
charset = part.get_content_charset() or 'utf-8'
try:
print(f"Decoded (Charset: {charset}): {payload.decode(charset)}")
except UnicodeDecodeError:
print(f"Failed to decode with {charset}. Trying fallback...")
# Fallback to a common charset or 'latin-1' if expecting it
print(f"Decoded (Fallback Latin-1): {payload.decode('latin-1', errors='replace')}")
else:
payload = msg.get_payload(decode=True)
charset = msg.get_content_charset() or 'utf-8'
try:
print(f"Decoded (Charset: {charset}): {payload.decode(charset)}")
except UnicodeDecodeError:
print(f"Failed to decode with {charset}. Trying fallback...")
print(f"Decoded (Fallback Latin-1): {payload.decode('latin-1', errors='replace')}")
توضیح:
- همیشه سعی کنید از مجموعه کاراکتر مشخص شده در هدر
Content-Type
استفاده کنید. - برای استحکام بیشتر، به ویژه هنگام کار با ایمیلهای از منابع متنوع و بالقوه غیراستاندارد، از یک بلوک
try-except UnicodeDecodeError
استفاده کنید. errors='replace'
یاerrors='ignore'
را میتوان با.decode()
برای مدیریت کاراکترهایی که نمیتوانند به انکودینگ هدف نگاشت شوند، استفاده کرد تا از بروز خطا جلوگیری شود.
سناریوهای تجزیه پیشرفته
ایمیلهای دنیای واقعی میتوانند بسیار پیچیده باشند و ساختارهای چندبخشی تودرتو داشته باشند. ماهیت بازگشتی پکیج email
پیمایش این ساختارها را ساده میکند. شما میتوانید is_multipart()
را با iter_parts()
ترکیب کنید تا پیامهای با عمق تودرتوی زیاد را پیمایش کنید.
from email.message import EmailMessage
from email import message_from_string
def parse_email_part(part, indent=0):
prefix = " " * indent
content_type = part.get_content_type()
charset = part.get_content_charset() or 'N/A'
print(f"{prefix}Part Type: {content_type}, Charset: {charset}")
if part.is_multipart():
for subpart in part.iter_parts():
parse_email_part(subpart, indent + 1)
elif part.get_filename(): # It's an attachment
print(f"{prefix} Attachment: {part.get_filename()} (Size: {len(part.get_payload(decode=True))} bytes)")
else: # It's a regular text/html body part
payload = part.get_payload(decode=True)
try:
decoded_content = payload.decode(charset)
# print(f"{prefix} Content (first 100 chars): {decoded_content[:100]}...") # For brevity
except UnicodeDecodeError:
print(f"{prefix} Content: (Binary or undecodable text)")
complex_email_raw = """
From: complex@example.com
To: receiver@example.com
Subject: Complex Email with HTML, Plain, and Attachment
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="outer_boundary"
--outer_boundary
Content-Type: multipart/alternative; boundary="inner_boundary"
--inner_boundary
Content-Type: text/plain; charset="utf-8"
Plain text content.
--inner_boundary
Content-Type: text/html; charset="utf-8"
<html><body><h2>HTML Content</h2></body></html>
--inner_boundary--
--outer_boundary
Content-Type: application/octet-stream
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="data.bin"
SGVsbG8gV29ybGQh
--outer_boundary--
"""
msg = message_from_string(complex_email_raw)
print("\n--- Traversing Complex Email Structure ---")
parse_email_part(msg)
توضیح:
- تابع بازگشتی
parse_email_part
نشان میدهد که چگونه میتوان در کل درخت پیام پیمایش کرد و بخشهای چندبخشی، پیوستها و محتوای بدنه را در هر سطح شناسایی کرد. - این الگو برای استخراج انواع خاصی از محتوا از ایمیلهای با ساختار تودرتوی عمیق بسیار انعطافپذیر است.
ساخت در مقابل تجزیه: یک دیدگاه مقایسهای
اگرچه عملیات متمایزی هستند، ساخت و تجزیه دو روی یک سکه هستند: مدیریت پیام MIME. درک یکی به ناچار به دیگری کمک میکند.
ساخت (ارسال):
- تمرکز: مونتاژ صحیح هدرها، محتوا و پیوستها در یک ساختار MIME مطابق با استاندارد.
- ابزار اصلی:
email.message.EmailMessage
با متدهایی مانندset_content()
،add_attachment()
،add_alternative()
،add_related()
. - چالشهای کلیدی: اطمینان از صحت انواع MIME، مجموعههای کاراکتری (بهویژه UTF-8 برای پشتیبانی جهانی)، `Content-Transfer-Encoding` و قالببندی صحیح هدرها. اشتباهات میتواند منجر به نمایش نادرست ایمیلها، خراب شدن پیوستها یا پرچمگذاری پیامها به عنوان اسپم شود.
تجزیه (دریافت):
- تمرکز: جداسازی یک جریان بایت خام ایمیل به اجزای تشکیلدهنده آن، استخراج هدرهای خاص، محتوای بدنه و پیوستها.
- ابزار اصلی:
email.parser.BytesParser
یاemail.message_from_bytes()
، سپس پیمایش شیءEmailMessage
حاصل با متدهایی مانندis_multipart()
،iter_parts()
،get_payload()
،get_filename()
و دسترسی به هدرها. - چالشهای کلیدی: مدیریت ایمیلهای ناقص یا معیوب، شناسایی صحیح انکودینگهای کاراکتری (بهویژه زمانی که مبهم هستند)، برخورد با هدرهای گمشده و استخراج قدرتمند دادهها از ساختارهای متنوع MIME.
پیامی که شما با استفاده از `EmailMessage` میسازید باید به طور کامل توسط `BytesParser` قابل تجزیه باشد. به طور مشابه، درک ساختار MIME تولید شده در طول تجزیه به شما بینشی در مورد چگونگی ساخت پیامهای پیچیده میدهد.
بهترین شیوهها برای مدیریت ایمیل جهانی با پایتون
برای برنامههایی که با مخاطبان جهانی تعامل دارند یا منابع ایمیل متنوعی را مدیریت میکنند، این بهترین شیوهها را در نظر بگیرید:
- استانداردسازی بر روی UTF-8: همیشه از UTF-8 برای تمام محتوای متنی استفاده کنید، هم هنگام ساخت و هم هنگام انتظار آن در طول تجزیه. این استاندارد جهانی برای انکودینگ کاراکتر است و از متن درهمریخته (mojibake) جلوگیری میکند.
- اعتبارسنجی آدرسهای ایمیل: قبل از ارسال، آدرسهای ایمیل گیرندگان را برای اطمینان از قابلیت تحویل، اعتبارسنجی کنید. هنگام تجزیه، برای آدرسهای بالقوه نامعتبر یا معیوب در هدرهای `From`، `To` یا `Cc` آماده باشید.
- آزمایش دقیق: ساخت ایمیل خود را با کلاینتهای ایمیل مختلف (Gmail، Outlook، Apple Mail، Thunderbird) و پلتفرمهای مختلف آزمایش کنید تا از رندر یکنواخت HTML و پیوستها اطمینان حاصل کنید. برای تجزیه، با طیف گستردهای از ایمیلهای نمونه، از جمله آنهایی با انکودینگهای غیرمعمول، هدرهای گمشده یا ساختارهای تودرتوی پیچیده، آزمایش کنید.
- پاکسازی ورودی تجزیه شده: همیشه با محتوای استخراج شده از ایمیلهای ورودی به عنوان غیرقابل اعتماد رفتار کنید. محتوای HTML را برای جلوگیری از حملات XSS در صورت نمایش آن در یک برنامه وب، پاکسازی کنید. نام فایلها و انواع پیوستها را برای جلوگیری از آسیبپذیریهای امنیتی مانند پیمایش مسیر (path traversal) یا موارد دیگر هنگام ذخیره فایلها، اعتبارسنجی کنید.
- مدیریت خطای قدرتمند: بلوکهای
try-except
جامعی را هنگام رمزگشایی محتواها یا دسترسی به هدرهای بالقوه گمشده پیادهسازی کنید. خطاهایUnicodeDecodeError
یاKeyError
را به آرامی مدیریت کنید. - مدیریت پیوستهای بزرگ: به اندازه پیوستها توجه داشته باشید، هم هنگام ساخت (برای جلوگیری از تجاوز از محدودیتهای سرور ایمیل) و هم هنگام تجزیه (برای جلوگیری از مصرف بیش از حد حافظه یا فضای دیسک). در صورت پشتیبانی توسط سیستم خود، پخش جریانی (streaming) پیوستهای بزرگ را در نظر بگیرید.
- استفاده از
email.policy
: برای برنامههای حیاتی، به صراحت یک `email.policy` (مثلاً `policy.SMTP`) انتخاب کنید تا انطباق دقیق با استانداردهای ایمیل را تضمین کنید، که میتواند بر قابلیت تحویل و قابلیت همکاری تأثیر بگذارد. - حفظ فراداده (Metadata): هنگام تجزیه، تصمیم بگیرید که کدام فراداده (هدرها، رشتههای مرزی اصلی) برای حفظ کردن مهم است، به خصوص اگر در حال ساخت یک سیستم بایگانی یا ارسال مجدد ایمیل هستید.
نتیجهگیری
پکیج email
پایتون یک کتابخانه فوقالعاده قدرتمند و انعطافپذیر برای هر کسی است که نیاز به تعامل برنامهریزیشده با ایمیل دارد. با تسلط بر هر دو جنبه ساخت پیامهای MIME و تجزیه قدرتمند ایمیلهای ورودی، شما توانایی ایجاد سیستمهای اتوماسیون ایمیل پیشرفته، ساخت کلاینتهای ایمیل، تجزیه و تحلیل دادههای ایمیل و ادغام قابلیتهای ایمیل در تقریباً هر برنامهای را به دست میآورید.
این پکیج به طور متفکرانهای پیچیدگیهای زیربنایی MIME را مدیریت میکند و به توسعهدهندگان اجازه میدهد تا بر روی محتوا و منطق تعاملات ایمیل خود تمرکز کنند. چه در حال ارسال خبرنامههای شخصیسازی شده به مخاطبان جهانی باشید و چه در حال استخراج دادههای حیاتی از گزارشهای خودکار سیستم، درک عمیق از پکیج email
در ساخت راهحلهای ایمیل قابل اعتماد، با قابلیت همکاری و آگاه به مسائل جهانی، بسیار ارزشمند خواهد بود.