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

email.examples.md

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

1> **Источник:** https://python-all.ru/3.6/library/email.examples.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 19.1.8. [`email`](https://python-all.ru/3.6/library/email.html#module-email): Примеры89Вот несколько примеров использования пакета [`email`](https://python-all.ru/3.6/library/email.html#module-email) для чтения, записи, и отправки простых email-сообщений, а также более сложных MIME-сообщений.1011Сначала посмотрим, как создать и отправить простое текстовое сообщение (как текст, так и адреса могут содержать символы Unicode):1213```python14# Импорт smtplib для функции отправки15import smtplib1617# Импортируются необходимые модули email.18from email.message import EmailMessage1920# Открыть текстовый файл, имя которого находится в textfile, для чтения.21with open(textfile) as fp:22    # Создать сообщение text/plain23    msg = EmailMessage()24    msg.set_content(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Разбор заголовков [**RFC 822**](https://python-all.ru/3.6/library/email.examples.html) легко выполняется с помощью классов из модуля [`parser`](https://python-all.ru/3.6/library/email.parser.html#module-email.parser):3940```python41# Импортируются необходимые модули email.42from email.parser import BytesParser, Parser43from email.policy import default4445# Если заголовки e-mail находятся в файле, раскомментируйте эти две строки:46# with open(messagefile, 'rb') as fp:47#     headers = BytesParser(policy=default).parse(fp)4849#  Или для разбора заголовков из строки (это нечастая операция), используйте:50headers = Parser(policy=default).parsestr(51        'From: Foo Bar <user@example.com>\n'52        'To: <someone_else@example.com>\n'53        'Subject: Test message\n'54        '\n'55        'Body would go here\n')5657#  Теперь к элементам заголовков можно обращаться как к словарю:58print('To: {}'.format(headers['to']))59print('From: {}'.format(headers['from']))60print('Subject: {}'.format(headers['subject']))6162# Также можно обращаться к частям адресов:63print('Recipient username: {}'.format(headers['to'].addresses[0].username))64print('Sender name: {}'.format(headers['from'].addresses[0].display_name))65```6667Вот пример отправки MIME-сообщения, содержащего много семейных фотографий, которые могут находиться в каталоге:6869```python70# Импорт smtplib для функции отправки71import smtplib7273# И imghdr для определения типов изображений74import imghdr7576# Вот модули пакета email, которые нам понадобятся77from email.message import EmailMessage7879# Создаём контейнерное email-сообщение.80msg = EmailMessage()81msg['Subject'] = 'Our family reunion'82# me == адрес электронной почты отправителя83# family = список адресов электронной почты всех получателей84msg['From'] = me85msg['To'] = ', '.join(family)86msg.preamble = 'Our family reunion'8788# Откройте файлы в двоичном режиме. Используйте imghdr, чтобы определить89# MIME-подтип для каждого конкретного изображения.90for file in pngfiles:91    with open(file, 'rb') as fp:92        img_data = fp.read()93    msg.add_attachment(img_data, maintype='image',94                                 subtype=imghdr.what(None, img_data))9596# Отправляем email через собственный SMTP-сервер.97with smtplib.SMTP('localhost') as s:98    s.send_message(msg)99```100101Вот пример отправки всего содержимого каталога в виде email- сообщения: [1](https://python-all.ru/3.6/library/email.examples.html#id3)102103```python104#!/usr/bin/env python3105106"""Отправляем содержимое каталога как MIME-сообщение."""107108import os109import smtplib110# Для определения типа MIME по расширению имени файла111import mimetypes112113from argparse import ArgumentParser114115from email.message import EmailMessage116from email.policy import SMTP117118def main():119    parser = ArgumentParser(description="""\120Send the contents of a directory as a MIME message.121Unless the -o option is given, the email is sent by forwarding to your local122SMTP server, which then does the normal delivery process.  Your local machine123must be running an SMTP server.124""")125    parser.add_argument('-d', '--directory',126                        help="""Mail the contents of the specified directory,127                        otherwise use the current directory.  Only the regular128                        files in the directory are sent, and we don't recurse to129                        subdirectories.""")130    parser.add_argument('-o', '--output',131                        metavar='FILE',132                        help="""Print the composed message to FILE instead of133                        sending the message to the SMTP server.""")134    parser.add_argument('-s', '--sender', required=True,135                        help='The value of the From: header (required)')136    parser.add_argument('-r', '--recipient', required=True,137                        action='append', metavar='RECIPIENT',138                        default=[], dest='recipients',139                        help='A To: header value (at least one required)')140    args = parser.parse_args()141    directory = args.directory142    if not directory:143        directory = '.'144    # Создаём сообщение145    msg = EmailMessage()146    msg['Subject'] = 'Contents of directory %s' % os.path.abspath(directory)147    msg['To'] = ', '.join(args.recipients)148    msg['From'] = args.sender149    msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'150151    for filename in os.listdir(directory):152        path = os.path.join(directory, filename)153        if not os.path.isfile(path):154            continue155        # Определяем тип содержимого по расширению файла. Кодировка156        # будет проигнорирована, хотя стоит проверять простые вещи, такие как157        # gzip-файлы или сжатые файлы.158        ctype, encoding = mimetypes.guess_type(path)159        if ctype is None or encoding is not None:160            # Не удалось определить тип, или файл закодирован (сжат), поэтому161            # используем общий тип 'набор битов'.162            ctype = 'application/octet-stream'163        maintype, subtype = ctype.split('/', 1)164        with open(path, 'rb') as fp:165            msg.add_attachment(fp.read(),166                               maintype=maintype,167                               subtype=subtype,168                               filename=filename)169    # Теперь отправляем или сохраняем сообщение170    if args.output:171        with open(args.output, 'wb') as fp:172            fp.write(msg.as_bytes(policy=SMTP))173    else:174        with smtplib.SMTP('localhost') as s:175            s.send_message(msg)176177if __name__ == '__main__':178    main()179```180181Вот пример распаковки MIME-сообщения вроде показанного выше в каталог с файлами:182183```python184#!/usr/bin/env python3185186"""Распаковываем MIME-сообщение в каталог файлов."""187188import os189import email190import mimetypes191192from email.policy import default193194from argparse import ArgumentParser195196def main():197    parser = ArgumentParser(description="""\198Unpack a MIME message into a directory of files.199""")200    parser.add_argument('-d', '--directory', required=True,201                        help="""Unpack the MIME message into the named202                        directory, which will be created if it doesn't already203                        exist.""")204    parser.add_argument('msgfile')205    args = parser.parse_args()206207    with open(args.msgfile, 'rb') as fp:208        msg = email.message_from_binary_file(fp, policy=default)209210    try:211        os.mkdir(args.directory)212    except FileExistsError:213        pass214215    counter = 1216    for part in msg.walk():217        # multipart/* – это просто контейнеры218        if part.get_content_maintype() == 'multipart':219            continue220        # Приложениям следует тщательно проверять имя файла, чтобы221        # email-сообщение не могло перезаписать важные файлы222        filename = part.get_filename()223        if not filename:224            ext = mimetypes.guess_extension(part.get_content_type())225            if not ext:226                # Использовать универсальное расширение для набора битов.227                ext = '.bin'228            filename = 'part-%03d%s' % (counter, ext)229        counter += 1230        with open(os.path.join(args.directory, filename), 'wb') as fp:231            fp.write(part.get_payload(decode=True))232233if __name__ == '__main__':234    main()235```236237Вот пример создания HTML-сообщения с альтернативной текстовой версией. Чтобы было интереснее, мы добавляем связанное изображение в HTML-часть и сохраняем копию того, что собираемся отправить на диск, а также отправляем его.238239```python240#!/usr/bin/env python3241242import smtplib243244from email.message import EmailMessage245from email.headerregistry import Address246from email.utils import make_msgid247248# Создаём базовое текстовое сообщение.249msg = EmailMessage()250msg['Subject'] = "Ayons asperges pour le déjeuner"251msg['From'] = Address("Pepé Le Pew", "pepe", "example.com")252msg['To'] = (Address("Penelope Pussycat", "penelope", "example.com"),253             Address("Fabrette Pussycat", "fabrette", "example.com"))254msg.set_content("""\255Salut!256257Cela ressemble à un excellent recipie[1] déjeuner.258259[1] http://www.yummly.com/recipe/Roasted-Asparagus-Epicurious-203718260261--Pepé262""")263264# Добавляем HTML-версию. Это преобразует сообщение в multipart/alternative.265# контейнер, где исходное текстовое сообщение – первая часть, а новое html266# сообщение – вторая часть.267asparagus_cid = make_msgid()268msg.add_alternative("""\269<html>270  <head></head>271  <body>272    <p>Salut!</p>273    <p>Cela ressemble à un excellent274        <a href="http://www.yummly.com/recipe/Roasted-Asparagus-Epicurious-203718">275            recipie276        </a> déjeuner.277    </p>278    <img src="cid:{asparagus_cid}" />279  </body>280</html>281""".format(asparagus_cid=asparagus_cid[1:-1]), subtype='html')282# Обратите внимание: для использования в html потребовалось снять угловые скобки с msgid.283284# Теперь добавьте связанное изображение в html-часть.285with open("roasted-asparagus.jpg", 'rb') as img:286    msg.get_payload()[1].add_related(img.read(), 'image', 'jpeg',287                                     cid=asparagus_cid)288289# Создается локальная копия отправляемых данных.290with open('outgoing.msg', 'wb') as f:291    f.write(bytes(msg))292293# Отправка сообщения через локальный SMTP-сервер.294with smtplib.SMTP('localhost') as s:295    s.send_message(msg)296```297298Если бы нам отправили сообщение из предыдущего примера, вот один из способов, как его можно обработать:299300```python301import os302import sys303import tempfile304import mimetypes305import webbrowser306307# Импортируются необходимые модули email.308from email import policy309from email.parser import BytesParser310311# Воображаемый модуль, который мог бы выполнить эту работу и быть безопасным.312from imaginary import magic_html_parser313314# В реальной программе имя файла получается из аргументов.315with open('outgoing.msg', 'rb') as fp:316    msg = BytesParser(policy=policy.default).parse(fp)317318# Теперь к элементам заголовка можно обращаться как к словарю, и любые не-ASCII символы будут319# преобразованы в Unicode:320print('To:', msg['to'])321print('From:', msg['from'])322print('Subject:', msg['subject'])323324# Если нужно вывести предварительный просмотр содержимого сообщения, можно извлечь325# наименее отформатированную полезную нагрузку и вывести первые три строки. Конечно,326# если сообщение не содержит части в обычном тексте, вывод первых трех строк html327# вероятно, бесполезен, но это всего лишь концептуальный пример.328simplest = msg.get_body(preferencelist=('plain', 'html'))329print()330print(''.join(simplest.get_content().splitlines(keepends=True)[:3]))331332ans = input("View full message?")333if ans.lower()[0] == 'n':334    sys.exit()335336# Можно извлечь наиболее полную альтернативу для отображения:337richest = msg.get_body()338partfiles = {}339if richest['content-type'].maintype == 'text':340    if richest['content-type'].subtype == 'plain':341        for line in richest.get_content().splitlines():342            print(line)343        sys.exit()344    elif richest['content-type'].subtype == 'html':345        body = richest346    else:347        print("Don't know how to display {}".format(richest.get_content_type()))348        sys.exit()349elif richest['content-type'].content_type == 'multipart/related':350    body = richest.get_body(preferencelist=('html'))351    for part in richest.iter_attachments():352        fn = part.get_filename()353        if fn:354            extension = os.path.splitext(part.get_filename())[1]355        else:356            extension = mimetypes.guess_extension(part.get_content_type())357        with tempfile.NamedTemporaryFile(suffix=extension, delete=False) as f:358            f.write(part.get_content())359            # снова удалите угловые скобки, чтобы перейти от формы cid в email к форме в html.360            partfiles[part['content-id'][1:-1]] = f.name361else:362    print("Don't know how to display {}".format(richest.get_content_type()))363    sys.exit()364with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:365    # Магический_парсер_html должен переписать атрибуты href="cid:...." так, чтобы они366    # указывали на имена файлов в partfiles.  Он также должен выполнить safety-sanitize367    # HTML.  Его можно написать с помощью html.parser.368    f.write(magic_html_parser(body.get_content(), partfiles))369webbrowser.open(f.name)370os.remove(f.name)371for fn in partfiles.values():372    os.remove(fn)373374# Конечно, есть много email-сообщений, которые могут нарушить эту простую375# программу, но она справится с наиболее распространенными.376```377378До приглашения вывод из примера выше:379380```text381To: Penelope Pussycat <penelope@example.com>, Fabrette Pussycat <fabrette@example.com>382From: Pepé Le Pew <pepe@example.com>383Subject: Ayons asperges pour le déjeuner384385Salut!386387Cela ressemble à un excellent recipie[1] déjeuner.388```389390Сноски391392**[1](https://python-all.ru/3.6/library/email.examples.html#id2)**393394Спасибо Matthew Dixon Cowles за оригинальную идею и примеры.395