Документация Python неофициальный перевод

email-examples.md

472 строк · 20.2 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.4/library/email-examples.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 19.1.14. [`email`](https://python-all.ru/3.4/library/email.html#module-email): Examples89Вот несколько примеров использования пакета [`email`](https://python-all.ru/3.4/library/email.html#module-email) для чтения, записи и отправки простых email-сообщений, а также более сложных MIME-сообщений.1011Сначала посмотрим, как создать и отправить простое текстовое сообщение:1213```python14# Импорт smtplib для функции отправки15import smtplib1617# Импортируются необходимые модули email.18from email.mime.text import MIMEText1920# Открыть обычный текстовый файл для чтения. В этом примере предполагается, что21# текстовый файл содержит только символы ASCII.22with open(textfile) as fp:23    # Создать сообщение text/plain24    msg = MIMEText(fp.read())2526# me == адрес электронной почты отправителя27# you == адрес электронной почты получателя28msg['Subject'] = 'The contents of %s' % textfile29msg['From'] = me30msg['To'] = you3132# Отправить сообщение через собственный SMTP-сервер.33s = smtplib.SMTP('localhost')34s.send_message(msg)35s.quit()36```3738Разбор заголовков RFC822 легко выполняется с помощью методов parse(filename) или parsestr(message\_as\_string) класса Parser():3940```python41# Импортируются необходимые модули email.42from email.parser import Parser4344# Если заголовки e-mail находятся в файле, раскомментируйте эти две строки:45# with open(messagefile) as fp:46#     headers = Parser().parse(fp)4748#  Или для разбора заголовков из строки используйте:49headers = Parser().parsestr('From: <user@example.com>\n'50        'To: <someone_else@example.com>\n'51        'Subject: Test message\n'52        '\n'53        'Body would go here\n')5455#  Теперь к элементам заголовков можно обращаться как к словарю:56print('To: %s' % headers['to'])57print('From: %s' % headers['from'])58print('Subject: %s' % headers['subject'])59```6061Вот пример отправки MIME-сообщения, содержащего много семейных фотографий, которые могут находиться в каталоге:6263```python64# Импорт smtplib для функции отправки65import smtplib6667# Вот модули пакета email, которые нам понадобятся68from email.mime.image import MIMEImage69from email.mime.multipart import MIMEMultipart7071COMMASPACE = ', '7273# Создать контейнер (внешнее) email-сообщение.74msg = MIMEMultipart()75msg['Subject'] = 'Our family reunion'76# me == адрес электронной почты отправителя77# family = список адресов электронной почты всех получателей78msg['From'] = me79msg['To'] = COMMASPACE.join(family)80msg.preamble = 'Our family reunion'8182# Предположим, что все файлы изображений в формате PNG.83for file in pngfiles:84    # Открыть файлы в двоичном режиме. Пусть класс MIMEImage автоматически85    # определяет конкретный тип изображения.86    with open(file, 'rb') as fp:87        img = MIMEImage(fp.read())88    msg.attach(img)8990# Отправляем email через собственный SMTP-сервер.91s = smtplib.SMTP('localhost')92s.send_message(msg)93s.quit()94```9596Вот пример отправки всего содержимого каталога в виде email- сообщения: [\[1\]](https://python-all.ru/3.4/library/email-examples.html#id4)9798```python99#!/usr/bin/env python3100101"""Отправляем содержимое каталога как MIME-сообщение."""102103import os104import sys105import smtplib106# Для определения типа MIME по расширению имени файла107import mimetypes108109from argparse import ArgumentParser110111from email import encoders112from email.message import Message113from email.mime.audio import MIMEAudio114from email.mime.base import MIMEBase115from email.mime.image import MIMEImage116from email.mime.multipart import MIMEMultipart117from email.mime.text import MIMEText118119COMMASPACE = ', '120121def main():122    parser = ArgumentParser(description="""\123Send the contents of a directory as a MIME message.124Unless the -o option is given, the email is sent by forwarding to your local125SMTP server, which then does the normal delivery process.  Your local machine126must be running an SMTP server.127""")128    parser.add_argument('-d', '--directory',129                        help="""Mail the contents of the specified directory,130                        otherwise use the current directory.  Only the regular131                        files in the directory are sent, and we don't recurse to132                        subdirectories.""")133    parser.add_argument('-o', '--output',134                        metavar='FILE',135                        help="""Print the composed message to FILE instead of136                        sending the message to the SMTP server.""")137    parser.add_argument('-s', '--sender', required=True,138                        help='The value of the From: header (required)')139    parser.add_argument('-r', '--recipient', required=True,140                        action='append', metavar='RECIPIENT',141                        default=[], dest='recipients',142                        help='A To: header value (at least one required)')143    args = parser.parse_args()144    directory = args.directory145    if not directory:146        directory = '.'147    # Создать внешнее (оборачивающее) сообщение.148    outer = MIMEMultipart()149    outer['Subject'] = 'Contents of directory %s' % os.path.abspath(directory)150    outer['To'] = COMMASPACE.join(args.recipients)151    outer['From'] = args.sender152    outer.preamble = 'You will not see this in a MIME-aware mail reader.\n'153154    for filename in os.listdir(directory):155        path = os.path.join(directory, filename)156        if not os.path.isfile(path):157            continue158        # Определяем тип содержимого по расширению файла. Кодировка159        # будет проигнорирована, хотя стоит проверять простые вещи, такие как160        # gzip-файлы или сжатые файлы.161        ctype, encoding = mimetypes.guess_type(path)162        if ctype is None or encoding is not None:163            # Не удалось определить тип, или файл закодирован (сжат), поэтому164            # используем общий тип 'набор битов'.165            ctype = 'application/octet-stream'166        maintype, subtype = ctype.split('/', 1)167        if maintype == 'text':168            with open(path) as fp:169                # Примечание: нужно обработать вычисление кодировки.170                msg = MIMEText(fp.read(), _subtype=subtype)171        elif maintype == 'image':172            with open(path, 'rb') as fp:173                msg = MIMEImage(fp.read(), _subtype=subtype)174        elif maintype == 'audio':175            with open(path, 'rb') as fp:176                msg = MIMEAudio(fp.read(), _subtype=subtype)177        else:178            with open(path, 'rb') as fp:179                msg = MIMEBase(maintype, subtype)180                msg.set_payload(fp.read())181            # Закодировать полезную нагрузку с помощью Base64.182            encoders.encode_base64(msg)183        # Установить параметр filename.184        msg.add_header('Content-Disposition', 'attachment', filename=filename)185        outer.attach(msg)186    # Теперь отправляем или сохраняем сообщение187    composed = outer.as_string()188    if args.output:189        with open(args.output, 'w') as fp:190            fp.write(composed)191    else:192        with smtplib.SMTP('localhost') as s:193            s.sendmail(args.sender, args.recipients, composed)194195if __name__ == '__main__':196    main()197```198199Вот пример распаковки MIME-сообщения вроде показанного выше в каталог с файлами:200201```python202#!/usr/bin/env python3203204"""Распаковываем MIME-сообщение в каталог файлов."""205206import os207import sys208import email209import errno210import mimetypes211212from argparse import ArgumentParser213214def main():215    parser = ArgumentParser(description="""\216Unpack a MIME message into a directory of files.217""")218    parser.add_argument('-d', '--directory', required=True,219                        help="""Unpack the MIME message into the named220                        directory, which will be created if it doesn't already221                        exist.""")222    parser.add_argument('msgfile')223    args = parser.parse_args()224225    with open(args.msgfile) as fp:226        msg = email.message_from_file(fp)227228    try:229        os.mkdir(args.directory)230    except FileExistsError:231        pass232233    counter = 1234    for part in msg.walk():235        # multipart/* – это просто контейнеры236        if part.get_content_maintype() == 'multipart':237            continue238        # Приложениям следует тщательно проверять имя файла, чтобы239        # email-сообщение не могло перезаписать важные файлы240        filename = part.get_filename()241        if not filename:242            ext = mimetypes.guess_extension(part.get_content_type())243            if not ext:244                # Использовать универсальное расширение для набора битов.245                ext = '.bin'246            filename = 'part-%03d%s' % (counter, ext)247        counter += 1248        with open(os.path.join(args.directory, filename), 'wb') as fp:249            fp.write(part.get_payload(decode=True))250251if __name__ == '__main__':252    main()253```254255Here’s an example of how to create an HTML message with an alternative plain text version: [\[2\]](https://python-all.ru/3.4/library/email-examples.html#id5)256257```python258#!/usr/bin/env python3259260import smtplib261262from email.mime.multipart import MIMEMultipart263from email.mime.text import MIMEText264265# me == мой адрес электронной почты.266# you == адрес электронной почты получателя.267me = "my@email.com"268you = "your@email.com"269270# Создать контейнер сообщения – правильный тип MIME – multipart/alternative.271msg = MIMEMultipart('alternative')272msg['Subject'] = "Link"273msg['From'] = me274msg['To'] = you275276# Создать тело сообщения (версии в виде простого текста и HTML).277text = "Hi!\nHow are you?\nHere is the link you wanted:\nhttps://www.python.org"278html = """\279<html>280  <head></head>281  <body>282    <p>Hi!<br>283       How are you?<br>284       Here is the <a href="https://www.python.org">link</a> you wanted.285    </p>286  </body>287</html>288"""289290# Записать MIME-типы обеих частей – text/plain и text/html.291part1 = MIMEText(text, 'plain')292part2 = MIMEText(html, 'html')293294# Прикрепить части к контейнеру сообщения.295# Согласно RFC 2046, последняя часть многокомпонентного сообщения, в данном случае296# HTML-сообщение, является наилучшим и предпочтительным.297msg.attach(part1)298msg.attach(part2)299300# Отправка сообщения через локальный SMTP-сервер.301s = smtplib.SMTP('localhost')302# Функция sendmail принимает 3 аргумента: адрес отправителя, адрес получателя303# и сообщение для отправки – здесь оно отправляется в виде одной строки.304s.sendmail(me, you, msg.as_string())305s.quit()306```307308## 19.1.14.1. Examples using the Provisional API309310Вот переработка последнего примера с использованием временного API. Чтобы сделать пример немного интереснее, в HTML-часть включается связанное изображение, а также сохраняется копия отправляемого сообщения на диск помимо самой отправки.311312Этот пример также показывает, как легко включать символы, отличные от ASCII, и упрощает отправку сообщения с помощью метода [`send_message()`](https://python-all.ru/3.4/library/smtplib.html#smtplib.SMTP.send_message) модуля [`smtplib`](https://python-all.ru/3.4/library/smtplib.html#module-smtplib).313314```python315#!/usr/bin/env python3316317import smtplib318319from email.message import EmailMessage320from email.headerregistry import Address321from email.utils import make_msgid322323# Создаём базовое текстовое сообщение.324msg = EmailMessage()325msg['Subject'] = "Ayons asperges pour le déjeuner"326msg['From'] = Address("Pepé Le Pew", "pepe@example.com")327msg['To'] = (Address("Penelope Pussycat", "penelope@example.com"),328             Address("Fabrette Pussycat", "fabrette@example.com"))329msg.set_content("""\330Salut!331332Cela ressemble à un excellent recipie[1] déjeuner.333334[1] http://www.yummly.com/recipe/Roasted-Asparagus-Epicurious-203718335336--Pepé337""")338339# Добавляем HTML-версию. Это преобразует сообщение в multipart/alternative.340# контейнер, где исходное текстовое сообщение – первая часть, а новое html341# сообщение – вторая часть.342asparagus_cid = make_msgid()343msg.add_alternative("""\344<html>345  <head></head>346  <body>347    <p>Salut!<\p>348    <p>Cela ressemble à un excellent349        <a href="http://www.yummly.com/recipe/Roasted-Asparagus-Epicurious-203718>350            recipie351        </a> déjeuner.352    </p>353    <img src="cid:{asparagus_cid}" \>354  </body>355</html>356""".format(asparagus_cid=asparagus_cid[1:-1]), subtype='html')357# Обратите внимание: для использования в html потребовалось снять угловые скобки с msgid.358359# Теперь добавьте связанное изображение в html-часть.360with open("roasted-asparagus.jpg", 'rb') as img:361    msg.get_payload()[1].add_related(img.read(), 'image', 'jpeg',362                                     cid=asparagus_cid)363364# Создается локальная копия отправляемых данных.365with open('outgoing.msg', 'wb') as f:366    f.write(bytes(msg))367368# Отправка сообщения через локальный SMTP-сервер.369with smtplib.SMTP('localhost') as s:370    s.send_message(msg)371```372373Если бы вместо этого нам отправили сообщение из последнего примера, вот один из способов его обработки:374375```python376import os377import sys378import tempfile379import mimetypes380import webbrowser381382# Импортируются необходимые модули email.383from email import policy384from email.parser import BytesParser385386# Воображаемый модуль, который мог бы выполнить эту работу и быть безопасным.387from imaginary import magic_html_parser388389# В реальной программе имя файла получается из аргументов.390with open('outgoing.msg', 'rb') as fp:391    msg = BytesParser(policy=policy.default).parse(fp)392393# Теперь к элементам заголовка можно обращаться как к словарю, и любые не-ASCII символы будут394# преобразованы в Unicode:395print('To:', msg['to'])396print('From:', msg['from'])397print('Subject:', msg['subject'])398399# Если мы хотим вывести предварительный просмотр содержимого сообщения, мы можем извлечь любое400# наименее отформатированную полезную нагрузку и вывести первые три строки. Конечно,401# если сообщение не содержит части в обычном тексте, вывод первых трех строк html402# вероятно, бесполезен, но это всего лишь концептуальный пример.403simplest = msg.get_body(preferencelist=('plain', 'html'))404print()405print(''.join(simplest.get_content().splitlines(keepends=True)[:3]))406407ans = input("View full message?")408if ans.lower()[0] == 'n':409    sys.exit()410411# Можно извлечь наиболее полную альтернативу для отображения:412richest = msg.get_body()413partfiles = {}414if richest['content-type'].maintype == 'text':415    if richest['content-type'].subtype == 'plain':416        for line in richest.get_content().splitlines():417            print(line)418        sys.exit()419    elif richest['content-type'].subtype == 'html':420        body = richest421    else:422        print("Don't know how to display {}".format(richest.get_content_type()))423        sys.exit()424elif richest['content-type'].content_type == 'multipart/related':425    body = richest.get_body(preferencelist=('html'))426    for part in richest.iter_attachments():427        fn = part.get_filename()428        if fn:429            extension = os.path.splitext(part.get_filename())[1]430        else:431            extension = mimetypes.guess_extension(part.get_content_type())432        with tempfile.NamedTemporaryFile(suffix=extension, delete=False) as f:433            f.write(part.get_content())434            # снова удалите угловые скобки, чтобы перейти от формы cid в email к форме в html.435            partfiles[part['content-id'][1:-1]] = f.name436else:437    print("Don't know how to display {}".format(richest.get_content_type()))438    sys.exit()439with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:440    # Магический_парсер_html должен переписать атрибуты href="cid:...." так, чтобы они441    # указывали на имена файлов в partfiles.  Он также должен выполнить safety-sanitize442    # HTML.  Его можно написать с помощью html.parser.443    f.write(magic_html_parser(body.get_content(), partfiles))444webbrowser.open(f.name)445os.remove(f.name)446for fn in partfiles.values():447    os.remove(fn)448449# Конечно, есть много email-сообщений, которые могут нарушить эту простую450# программу, но она справится с наиболее распространенными.451```452453До приглашения вывод из примера выше:454455```python456To: Penelope Pussycat <"penelope@example.com">, Fabrette Pussycat <"fabrette@example.com">457From: Pepé Le Pew <pepe@example.com>458Subject: Ayons asperges pour le déjeuner459460Salut!461462Cela ressemble à un excellent recipie[1] déjeuner.463```464465Сноски466467| [\[1\]](https://python-all.ru/3.4/library/email-examples.html#id2) | Спасибо Matthew Dixon Cowles за оригинальную идею и примеры. |468| --- | --- |469470| [\[2\]](https://python-all.ru/3.4/library/email-examples.html#id3) | Автор: Martin Matejek. |471| --- | --- |472