email.parser: تجزیه پیام‌های ایمیل

کد منبع: Lib/email/parser.py


ساختارهای شیء پیام را می‌توان به یکی از دو روش ایجاد کرد: می‌توان آن‌ها را از پایه با ایجاد یک شیء EmailMessage، افزودن سرآیند‌ها با استفاده از رابط دیکشنری، و افزودن بار(ها) با استفاده از set_content() و متدهای مرتبط ایجاد کرد، یا می‌توان آن‌ها را با تجزیه‌ی یک نمایش سریال‌شده از پیام ایمیل ایجاد کرد.

بسته‌ی email یک پارسر استاندارد ارائه می‌کند که بیشتر ساختارهای اسناد ایمیل، از جمله اسناد MIME را درک می‌کند. شما می‌توانید یک شیء بایتی، رشته یا پرونده را به پارسر بدهید و پارسر نمونه‌ی ریشه‌ی EmailMessage از ساختار شیء را به شما بازمی‌گرداند. برای پیام‌های ساده و غیر MIME، بار این شیء ریشه به احتمال زیاد یک رشته حاوی متن پیام خواهد بود. برای پیام‌های MIME، شیء ریشه از متد is_multipart() خود مقدار True را بازمی‌گرداند و می‌توان به زیربخش‌ها از طریق متدهای دستکاری بار، مانند get_body()، iter_parts() و walk() دسترسی یافت.

در واقع دو رابط پارسر برای استفاده در دسترس هستند: API Parser و API افزایشی FeedParser. API Parser زمانی بیشترین کاربرد را دارد که کل متن پیام را در حافظه داشته باشید، یا اینکه کل پیام در پرونده‌ای روی سیستم پرونده قرار داشته باشد. FeedParser زمانی مناسب‌تر است که پیام را از جریانی می‌خوانید که ممکن است در انتظار ورودی بیشتر مسدود شود (مانند خواندن یک پیام ایمیل از یک سوکت). FeedParser می‌تواند پیام را به‌صورت افزایشی مصرف و تجزیه کند، و تنها زمانی که پارسر را ببندید، شیء ریشه را برمی‌گرداند.

توجه داشته باشید که پارسر را می‌توان به روش‌های محدودی گسترش داد و البته می‌توانید پارسرٔ خودتان را کاملاً از صفر پیاده‌سازی کنید. تمام منطقی که پارسرٔ همراه با بسته‌ی email و کلاس EmailMessage را به هم متصل می‌کند، در کلاس Policy نهفته است، بنابراین یک پارسرٔ سفارشی می‌تواند با پیاده‌سازی نسخه‌های سفارشی متدهای مناسب Policy، درخت‌های شیء پیام را به هر روشی که لازم بداند ایجاد کند.

API مربوط به FeedParser

BytesFeedParser، که از ماژول email.feedparser ایمپورت می‌شود، یک API فراهم می‌کند که برای تجزیه تدریجی پیام‌های ایمیل مناسب است، مانند حالتی که لازم باشد متن یک پیام ایمیل از منبعی که ممکن است مسدودکننده باشد (مانند سوکت) خوانده شود. البته می‌توان از BytesFeedParser برای تجزیه پیام ایمیلی که به‌طور کامل در یک bytes-like object، رشته یا پرونده قرار دارد استفاده کرد، اما ممکن است API BytesParser برای چنین موارد استفاده‌ای مناسب‌تر باشد. معناشناسی و نتایج این دو API پارسر یکسان است.

API مربوط به BytesFeedParser ساده است؛ شما یک نمونه ایجاد می‌کنید، تعدادی بایت به آن می‌دهید تا زمانی که دیگر بایتی برای دادن به آن باقی نمانده باشد، سپس پارسر را می‌بندید تا شیء پیام ریشه را دریافت کنید. BytesFeedParser هنگام تجزیه‌ی پیام‌های مطابق با استاندارد بسیار دقیق است و در تجزیه‌ی پیام‌های غیرمطابق با استاندارد نیز بسیار خوب عمل می‌کند و اطلاعاتی درباره‌ی چگونگی معیوب تشخیص داده شدن یک پیام ارائه می‌دهد. این پارسر ویژگی defects یک شیء پیام را با فهرستی از هرگونه مشکلی که در یک پیام پیدا کرده است، پر می‌کند. برای فهرست نقص‌هایی که می‌تواند پیدا کند، ماژول email.errors را ببینید.

در اینجا API مربوط به BytesFeedParser آمده است:

class email.parser.BytesFeedParser(_factory=None, *, policy=policy.compat32)

نمونه‌ای از BytesFeedParser ایجاد کنید. _factory اختیاری، یک شیء فراخوانی‌پذیر بدون آرگومان است؛ اگر مشخص نشده باشد، از message_factory از policy استفاده می‌شود. هرگاه به یک شیء پیام جدید نیاز باشد، _factory فراخوانی می‌شود.

اگر policy مشخص شده باشد، برای به‌روزرسانی بازنمایی پیام از قواعدی که مشخص می‌کند استفاده می‌شود. اگر policy تنظیم نشده باشد، از سیاست compat32 استفاده می‌شود، که سازگاری با نسخه‌ی Python 3.2 بسته‌ی email را حفظ می‌کند و Message را به‌عنوان کارخانه‌ی پیش‌فرض فراهم می‌کند. تمام سیاست‌های دیگر EmailMessage را به‌عنوان _factory پیش‌فرض فراهم می‌کنند. برای اطلاعات بیشتر درباره‌ی سایر مواردی که policy کنترل می‌کند، به مستندات policy مراجعه کنید.

توجه: کلیدواژه‌ی policy همیشه باید تعیین شود؛ مقدار پیش‌فرض در نسخه‌ی آینده‌ی پایتون به email.policy.default تغییر خواهد کرد.

اضافه شده در نسخه‌ی 3.2.

تغییر یافته در نسخه‌ی 3.3: کلیدواژه‌ی policy افزوده شد.

تغییر یافته در نسخه‌ی 3.6: _factory به‌صورت پیش‌فرض برابر با message_factoryسیاست است.

feed(data)

داده‌های بیشتری به پارسر بدهید. data باید یک bytes-like object حاوی یک یا چند خط باشد. سطرهای می‌توانند ناقص باشند و پارسر چنین سطرهای ناقصی را به‌درستی به یکدیگر متصل خواهد کرد. سطرهای می‌توانند دارای هر یک از سه پایان‌خط رایج باشند: بازگشت به ابتدای سطر، خط جدید، یا بازگشت به ابتدای سطر و خط جدید (حتی می‌توانند به‌صورت ترکیبی باشند).

close()

تجزیه‌ی تمام داده‌های پیش‌تر خورانده‌شده را کامل می‌کند و شیء پیام ریشه را برمی‌گرداند. اینکه اگر پس از فراخوانی این متد، feed() فراخوانی شود چه اتفاقی می‌افتد، تعریف‌نشده است.

class email.parser.FeedParser(_factory=None, *, policy=policy.compat32)

مانند BytesFeedParser عمل می‌کند، به‌جز اینکه ورودی متد feed() باید یک رشته باشد. این کاربرد محدودی دارد، زیرا تنها راه برای معتبر بودن چنین پیامی این است که فقط شامل متن ASCII باشد یا، اگر utf8 برابر True باشد، هیچ پیوست دودویی‌ای نداشته باشد.

تغییر یافته در نسخه‌ی 3.3: کلیدواژه‌ی policy افزوده شد.

API پارسر

کلاس BytesParser، که از ماژول email.parser ایمپورت می‌شود، یک API فراهم می‌کند که می‌توان از آن برای تجزیه‌ی یک پیام استفاده کرد، هنگامی که محتوای کامل پیام در یک bytes-like object یا پرونده در دسترس باشد. ماژول email.parser همچنین Parser را برای تجزیه‌ی رشته‌ها، و نیز پارسرهای فقط سرآیند، BytesHeaderParser و HeaderParser، فراهم می‌کند؛ از این پارسرها می‌توان در صورتی استفاده کرد که فقط به سرآیندهای پیام علاقه‌مند باشید. BytesHeaderParser و HeaderParser می‌توانند در این شرایط بسیار سریع‌تر باشند، زیرا تلاش نمی‌کنند بدنه پیام را تجزیه کنند، بلکه در عوض بار را به بدنه خام تنظیم می‌کنند.

class email.parser.BytesParser(_class=None, *, policy=policy.compat32)

یک نمونه از BytesParser ایجاد کنید. آرگومان‌های _class و policy همان معنا و معناشناسی آرگومان‌های _factory و policy در BytesFeedParser را دارند.

توجه: کلیدواژه‌ی policy همیشه باید تعیین شود؛ مقدار پیش‌فرض در نسخه‌ی آینده‌ی پایتون به email.policy.default تغییر خواهد کرد.

تغییر یافته در نسخه‌ی 3.3: آرگومان strict که در 2.4 منسوخ شده بود، حذف شد. کلیدواژه‌ی policy افزوده شد.

تغییر یافته در نسخه‌ی 3.6: _class به‌طور پیش‌فرض برابر با سیاست message_factory است.

parse(fp, headersonly=False)

تمام داده‌ها را از شیء شبه‌پرونده دودویی fp می‌خواند، بایت‌های حاصل را تجزیه می‌کند و شیء پیام را برمی‌گرداند. fp باید از هر دو متد readline() و read() پشتیبانی کند.

بایت‌های موجود در fp باید به‌صورت بلوکی از سرآیند‌ها و سطرهای ادامه‌ی سرآیند به سبک RFC 5322 (یا اگر utf8 برابر True باشد، RFC 6532) قالب‌بندی شده باشند؛ به‌صورت اختیاری ممکن است یک سرآیند‌ی پاکت (envelope header) پیش از آن‌ها بیاید. بلوک سرآیند یا با پایان داده‌ها یا با یک خط خالی پایان می‌یابد. پس از بلوک سرآیند، بدنه‌ی پیام قرار دارد (که ممکن است شامل زیربخش‌های کدگذاری‌شده با MIME باشد، از جمله زیربخش‌هایی که Content-Transfer-Encoding آن‌ها 8bit است).

headersonly اختیاری، پرچمی است که مشخص می‌کند آیا پس از خواندن سرآیندها تجزیه متوقف شود یا خیر. مقدار پیش‌فرض False است، به این معنا که کل محتویات پرونده تجزیه می‌شود.

parsebytes(bytes, headersonly=False)

مانند متد parse() است، با این تفاوت که به‌جای یک شیء شبه‌پرونده، یک bytes-like object دریافت می‌کند. فراخوانی این متد بر روی یک bytes-like object معادل آن است که ابتدا bytes را در یک نمونه از BytesIO قرار دهید و سپس parse() را فراخوانی کنید.

آرگومان اختیاری headersonly مانند متد parse() است.

اضافه شده در نسخه‌ی 3.2.

class email.parser.BytesHeaderParser(_class=None, *, policy=policy.compat32)

دقیقاً مانند BytesParser، با این تفاوت که headersonly به‌طور پیش‌فرض True است.

اضافه شده در نسخه‌ی 3.3.

class email.parser.Parser(_class=None, *, policy=policy.compat32)

این کلاس مشابه BytesParser است، اما ورودی رشته‌ای را پردازش می‌کند.

تغییر یافته در نسخه‌ی 3.3: آرگومان strict حذف شد. کلیدواژه policy افزوده شد.

تغییر یافته در نسخه‌ی 3.6: _class به‌طور پیش‌فرض برابر با سیاست message_factory است.

parse(fp, headersonly=False)

تمام داده‌ها را از شیء شبه‌پرونده در حالت متنی fp می‌خواند، متن حاصل را تجزیه می‌کند و شیء پیام ریشه را برمی‌گرداند. fp باید از هر دو متد readline() و read() در اشیاء شبه‌پرونده پشتیبانی کند.

غیر از الزام حالت متنی، این متد مانند BytesParser.parse() عمل می‌کند.

parsestr(text, headersonly=False)

مشابه متد parse()، با این تفاوت که به‌جای یک شیء شبه‌پرونده، یک شیء رشته دریافت می‌کند. فراخوانی این متد روی یک رشته، معادل آن است که ابتدا text را درون یک نمونه از StringIO قرار دهید و سپس parse() را فراخوانی کنید.

آرگومان اختیاری headersonly مانند متد parse() است.

class email.parser.HeaderParser(_class=None, *, policy=policy.compat32)

دقیقاً مانند Parser، با این تفاوت که headersonly به‌طور پیش‌فرض True است.

از آنجا که ایجاد ساختار شیء پیام از یک رشته یا شیء پرونده کاری بسیار رایج است، چهار تابع برای سهولت ارائه شده‌اند. این توابع در فضای نام سطح بالای بسته‌ی email در دسترس هستند.

email.message_from_bytes(s, _class=None, *, policy=policy.compat32)

ساختار شیء پیام را از یک شیء شبه‌بایت (bytes-like object) برمی‌گرداند. این معادل BytesParser().parsebytes(s) است. آرگومان‌های اختیاری _class و policy همان‌گونه تفسیر می‌شوند که در سازنده کلاس BytesParser تفسیر می‌شوند.

اضافه شده در نسخه‌ی 3.2.

تغییر یافته در نسخه‌ی 3.3: آرگومان strict حذف شد. کلیدواژه policy افزوده شد.

email.message_from_binary_file(fp, _class=None, *, policy=policy.compat32)

یک درخت ساختار شیء پیام را از یک file object دودویی باز برمی‌گرداند. این معادل BytesParser().parse(fp) است. _class و policy همانند سازنده کلاس BytesParser تفسیر می‌شوند.

اضافه شده در نسخه‌ی 3.2.

تغییر یافته در نسخه‌ی 3.3: آرگومان strict حذف شد. کلیدواژه policy افزوده شد.

email.message_from_string(s, _class=None, *, policy=policy.compat32)

یک ساختار شیء پیام را از یک رشته برمی‌گرداند. این معادل Parser().parsestr(s) است. _class و policy همان‌گونه تفسیر می‌شوند که در سازنده‌ی کلاس Parser تفسیر می‌شوند.

تغییر یافته در نسخه‌ی 3.3: آرگومان strict حذف شد. کلیدواژه policy افزوده شد.

email.message_from_file(fp, _class=None, *, policy=policy.compat32)

یک درخت ساختار شیء پیام را از یک file object باز برمی‌گرداند. این معادل Parser().parse(fp) است. _class و policy همانند سازنده کلاس Parser تفسیر می‌شوند.

تغییر یافته در نسخه‌ی 3.3: آرگومان strict حذف شد. کلیدواژه policy افزوده شد.

تغییر یافته در نسخه‌ی 3.6: _class به‌طور پیش‌فرض برابر با سیاست message_factory است.

در اینجا نمونه‌ای از چگونگی استفاده از message_from_bytes() در یک خط فرمان تعاملی پایتون آورده شده است:

>>> import email
>>> msg = email.message_from_bytes(myBytes)

نکات تکمیلی

در اینجا چند نکته درباره‌ی معناشناسی تجزیه آمده است:

  • بیشتر پیام‌هایی که نوع آن‌ها غیر multipart است، به‌عنوان یک شیء پیام واحد با بار رشته‌ای تجزیه می‌شوند. این شیء‌ها برای is_multipart() مقدار False را برمی‌گردانند، و iter_parts() یک فهرست خالی تولید می‌کند.

  • همه‌ی پیام‌های نوع multipart به‌صورت یک شیء پیام ظرف تجزیه می‌شوند که بارٔ آن فهرستی از اشیاء زیرپیام است. پیام ظرف بیرونی برای is_multipart() مقدار True را برمی‌گرداند، و iter_parts() فهرستی از زیربخش‌ها را تولید می‌کند.

  • بیشتر پیام‌هایی که نوع محتوای آن‌ها message/* است (مانند message/delivery-status و message/rfc822) نیز به‌صورت یک شیء ظرف حاوی یک بار از نوع فهرست به طول ۱ تجزیه می‌شوند. متد is_multipart() این پیام‌ها مقدار True را برمی‌گرداند. تنها عنصری که توسط iter_parts() تولید می‌شود، یک شیء زیرپیام خواهد بود.

  • ممکن است برخی پیام‌های ناسازگار با استاندارد، از نظر درونی با چندبخشی بودن (multipart-edness) خود سازگار نباشند. چنین پیام‌هایی ممکن است سرآیند Content-Type از نوع multipart داشته باشند، اما متد is_multipart() آن‌ها ممکن است False برگرداند. اگر چنین پیام‌هایی با FeedParser تجزیه شده باشند، در فهرست ویژگی defects خود نمونه‌ای از کلاس MultipartInvariantViolationDefect خواهند داشت. برای جزئیات، email.errors را ببینید.