| 01 | Рано или поздно практически у каждого разработчика возникает задача отправки электронной почты. Часто это связано с необходимостью информирования получателя о наступлении какого-нибудь события. |  | 
| 02 | На заметку: | 
Материалы данной статьи используются в статье о камере для Raspberry Pi 3, в разделе о построении простой системы безопасности, суть которой заключается в отправке фотографии пользователю по электронке, при детектировании движения в зоне действия датчика.
                         |  | 
| 03 | Для отправки письма по электронной почте используется штатная, предустановленная в системе, библиотека smtplib: |  | 
| 04 | Python | 1 import smtplib |  | 
| 05 | На заметку: | 
Для получения справки о любом импортированном классе или библиотеке, в интерпретаторе Python, после непосредственно импорта, нужно выполнить команду: 1 2 >>> import smtplib
>>> help(smtplib) Или из скетча — вывод в консоль через функцию print(): 1 2 import smtplib
print(help(smtplib)) |  | 
| 06 | Для отправки писем можно использовать любой имеющийся у пользователя почтовый ящик. Настройки каждого почтового провайдера открыто предоставляются каждым из них: |  | 
| 07 | Для упрощения работы с письмами понадобится пакет email. Он позволяет работать с сообщениями электронной почты как с отдельными объектами. Пакет содержит также подклассы, описывающие различные MIME-типы. Для работы понадобятся 2 из них — MIMEMultipart и MIMEText: |  | 
| 08 | Python | 1 2 3 import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText |  | 
| 09 | Общий шаблон отправки сообщения выглядит так: |  | 
| 10 | Python | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import smtplib                                      # Импортируем библиотеку по работе с SMTP
# Добавляем необходимые подклассы - MIME-типы
from email.mime.multipart import MIMEMultipart      # Многокомпонентный объект
from email.mime.text import MIMEText                # Текст/HTML
from email.mime.image import MIMEImage              # Изображения
addr_from = "from_address@mail.com"                 # Адресат
addr_to   = "to_address@mail.com"                   # Получатель
password  = "pass"                                  # Пароль
msg = MIMEMultipart()                               # Создаем сообщение
msg['From']    = addr_from                          # Адресат
msg['To']      = addr_to                            # Получатель
msg['Subject'] = 'Тема сообщения'                   # Тема сообщения
body = "Текст сообщения"
msg.attach(MIMEText(body, 'plain'))                 # Добавляем в сообщение текст
server = smtplib.SMTP('smtp-server', 587)           # Создаем объект SMTP
server.set_debuglevel(True)                         # Включаем режим отладки - если отчет не нужен, строку можно закомментировать
server.starttls()                                   # Начинаем шифрованный обмен по TLS
server.login(addr_from, password)                   # Получаем доступ
server.send_message(msg)                            # Отправляем сообщение
server.quit()                                       # Выходим |  | 
| 11 | Если необходимо добавить HTML-фрагмент, нужно присоединить к объекту msg ещё один подкласс: |  | 
| 12 | Python | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import smtplib                                      # Импортируем библиотеку по работе с SMTP
# Добавляем необходимые подклассы - MIME-типы
from email.mime.multipart import MIMEMultipart      # Многокомпонентный объект
from email.mime.text import MIMEText                # Текст/HTML
from email.mime.image import MIMEImage              # Изображения
addr_from = "from_address@gmail.com"                # Адресат
addr_to   = "to_address@gmail.com"                  # Получатель
password  = "pass"                                  # Пароль
msg = MIMEMultipart()                               # Создаем сообщение
msg['From']    = addr_from                          # Адресат
msg['To']      = addr_to                            # Получатель
msg['Subject'] = 'Тема сообщения'                   # Тема сообщения
body = "Текст сообщения"
msg.attach(MIMEText(body, 'plain'))                 # Добавляем в сообщение текст
 html = """\ <html>   <head></head>   <body>     <p>        Фрагмент HTML-кода     </p>   </body> </html> """ msg.attach(MIMEText(html, 'html', 'utf-8'))         # Добавляем в сообщение HTML-фрагментserver = smtplib.SMTP('smtp-server', 587)           # Создаем объект SMTP
server.set_debuglevel(True)                         # Включаем режим отладки - если отчет не нужен, строку можно закомментировать
server.starttls()                                   # Начинаем шифрованный обмен по TLS
server.login(addr_from, password)                   # Получаем доступ
server.send_message(msg)                            # Отправляем сообщение
server.quit()                                       # Выходим |  | 
| 13 | На заметку: | 
В случае проблем с кодировкой, для тех полей, которым это необходимо, её необходимо указывать явно: 1 2 3 msg.attach(MIMEText(body, 'html', 'utf-8'))
# или
msg['Subject'] = Header('Тема сообщения', 'utf-8') |  | 
| 14 | Важно: | 
При включении режима отладки server.set_debuglevel(True) вся информация о процессе будет выводиться в консоль оболочки Shell. Это скажется на производительности, особенно в случаях вложения в отправление файлов. При наличии вложений, использовать отладку server.set_debuglevel(True) не рекомендуется! |  | 
| 15 | По такому же принципу к сообщению добавляются файлы — создаются экземпляры соответствующих подклассов и присоединяются к объекту msg. Код для включения различных типов файлов представлен ниже. Также необходимо импортировать дополнительные MIME-типы, энкодеры и библиотеки: |  | 
| 16 | Python | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import mimetypes                                          # Импорт класса для обработки неизвестных MIME-типов, базирующихся на расширении файла
from email import encoders                                # Импортируем энкодер
from email.mime.base import MIMEBase                      # Общий тип
from email.mime.text import MIMEText                      # Текст/HTML
from email.mime.image import MIMEImage                    # Изображения
from email.mime.audio import MIMEAudio                    # Аудио
# ...
filepath="full_file_path_with_filename"                   # Имя файла в абсолютном или относительном формате
filename = os.path.basename(filepath)                     # Только имя файла
if os.path.isfile(filepath):                              # Если файл существует
  ctype, encoding = mimetypes.guess_type(filepath)        # Определяем тип файла на основе его расширения
  if ctype is None or encoding is not None:               # Если тип файла не определяется
      ctype = 'application/octet-stream'                  # Будем использовать общий тип
  maintype, subtype = ctype.split('/', 1)                 # Получаем тип и подтип
  if maintype == 'text':                                  # Если текстовый файл
      with open(filepath) as fp:                          # Открываем файл для чтения
          file = MIMEText(fp.read(), _subtype=subtype)    # Используем тип MIMEText
          fp.close()                                      # После использования файл обязательно нужно закрыть
  elif maintype == 'image':                               # Если изображение
      with open(filepath, 'rb') as fp:
          file = MIMEImage(fp.read(), _subtype=subtype)
          fp.close()
  elif maintype == 'audio':                               # Если аудио
      with open(filepath, 'rb') as fp:
          file = MIMEAudio(fp.read(), _subtype=subtype)
          fp.close()
  else:                                                   # Неизвестный тип файла
      with open(filepath, 'rb') as fp:
          file = MIMEBase(maintype, subtype)              # Используем общий MIME-тип
          file.set_payload(fp.read())                     # Добавляем содержимое общего типа (полезную нагрузку)
          fp.close()
      encoders.encode_base64(file)                        # Содержимое должно кодироваться как Base64
  file.add_header('Content-Disposition', 'attachment', filename=filename) # Добавляем заголовки
  msg.attach(file)                                        # Присоединяем файл к сообщению |  | 
| 17 | После вынесения функционала отправки сообщения в отдельную функцию, полный код с примером использования будет выглядеть так: |  | 
| 18 | Python | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 import smtplib                                              # Импортируем библиотеку по работе с SMTP
import os                                                   # Функции для работы с операционной системой, не зависящие от используемой операционной системы
# Добавляем необходимые подклассы - MIME-типы
import mimetypes                                            # Импорт класса для обработки неизвестных MIME-типов, базирующихся на расширении файла
from email import encoders                                  # Импортируем энкодер
from email.mime.base import MIMEBase                        # Общий тип
from email.mime.text import MIMEText                        # Текст/HTML
from email.mime.image import MIMEImage                      # Изображения
from email.mime.audio import MIMEAudio                      # Аудио
from email.mime.multipart import MIMEMultipart              # Многокомпонентный объект
def send_email(addr_to, msg_subj, msg_text, files):
    addr_from = "my_addr@server.ru"                         # Отправитель
    password  = "password"                                  # Пароль
    msg = MIMEMultipart()                                   # Создаем сообщение
    msg['From']    = addr_from                              # Адресат
    msg['To']      = addr_to                                # Получатель
    msg['Subject'] = msg_subj                               # Тема сообщения
    body = msg_text                                         # Текст сообщения
    msg.attach(MIMEText(body, 'plain'))                     # Добавляем в сообщение текст
    process_attachement(msg, files)
    #======== Этот блок настраивается для каждого почтового провайдера отдельно ===============================================
    server = smtplib.SMTP_SSL('smtp.server.ru', 465)        # Создаем объект SMTP
    #server.starttls()                                      # Начинаем шифрованный обмен по TLS
    #server.set_debuglevel(True)                            # Включаем режим отладки, если не нужен - можно закомментировать
    server.login(addr_from, password)                       # Получаем доступ
    server.send_message(msg)                                # Отправляем сообщение
    server.quit()                                           # Выходим
    #==========================================================================================================================
def process_attachement(msg, files):                        # Функция по обработке списка, добавляемых к сообщению файлов
    for f in files:
        if os.path.isfile(f):                               # Если файл существует
            attach_file(msg,f)                              # Добавляем файл к сообщению
        elif os.path.exists(f):                             # Если путь не файл и существует, значит - папка
            dir = os.listdir(f)                             # Получаем список файлов в папке
            for file in dir:                                # Перебираем все файлы и...
                attach_file(msg,f+"/"+file)                 # ...добавляем каждый файл к сообщению
def attach_file(msg, filepath):                             # Функция по добавлению конкретного файла к сообщению
    filename = os.path.basename(filepath)                   # Получаем только имя файла
    ctype, encoding = mimetypes.guess_type(filepath)        # Определяем тип файла на основе его расширения
    if ctype is None or encoding is not None:               # Если тип файла не определяется
        ctype = 'application/octet-stream'                  # Будем использовать общий тип
    maintype, subtype = ctype.split('/', 1)                 # Получаем тип и подтип
    if maintype == 'text':                                  # Если текстовый файл
        with open(filepath) as fp:                          # Открываем файл для чтения
            file = MIMEText(fp.read(), _subtype=subtype)    # Используем тип MIMEText
            fp.close()                                      # После использования файл обязательно нужно закрыть
    elif maintype == 'image':                               # Если изображение
        with open(filepath, 'rb') as fp:
            file = MIMEImage(fp.read(), _subtype=subtype)
            fp.close()
    elif maintype == 'audio':                               # Если аудио
        with open(filepath, 'rb') as fp:
            file = MIMEAudio(fp.read(), _subtype=subtype)
            fp.close()
    else:                                                   # Неизвестный тип файла
        with open(filepath, 'rb') as fp:
            file = MIMEBase(maintype, subtype)              # Используем общий MIME-тип
            file.set_payload(fp.read())                     # Добавляем содержимое общего типа (полезную нагрузку)
            fp.close()
            encoders.encode_base64(file)                    # Содержимое должно кодироваться как Base64
    file.add_header('Content-Disposition', 'attachment', filename=filename) # Добавляем заголовки
    msg.attach(file)                                        # Присоединяем файл к сообщению
# Использование функции send_email()
addr_to   = "xxxx@server.ru"                                # Получатель
files = ["file1_path",                                      # Список файлов, если вложений нет, то files=[]
         "file2_path",                                      
         "dir1_path"]                                       # Если нужно отправить все файлы из заданной папки, нужно указать её
send_email(addr_to, "Тема сообщения", "Текст сообщения", files) |  | 
| 19 | Результат: |  | 
| 21 | Но здесь нужно учитывать, что для разных почтовых провайдеров необходимо модифицировать строки отправки сообщения в соответствии с предоставляемыми настройками. О них далее. |  | 
| 22 | Gmail Компания Google очень беспокоится о безопасности своих пользователей, поэтому перед тем как начинать отправку сообщений, необходимо разрешить доступ к аккаунту для ненадежных приложений: |  | 
| 24 | Фрагмент кода для отправки почты, с использованием аккаунта ...@gmail.com: |  | 
| 25 | Python | 1 2 3 4 5 server = smtplib.SMTP('smtp.gmail.com', 587)        # Создаем объект SMTP
server.starttls()                                   # Начинаем шифрованный обмен по TLS
server.login(addr_from, password)                   # Получаем доступ
server.send_message(msg)                            # Отправляем сообщение
server.quit()                                       # Выходим |  | 
| 26 | Mail.ru Несмотря на то, что Mail.ru указывает использовать порт 465 для обращения к SMTP-серверу, для корректной работы необходимо указывать порт 25: |  | 
| 27 | Python | 1 2 3 4 5 server = smtplib.SMTP_SSL('smtp.mail.ru', 25)    # Создаем объект SMTP
#server.starttls()                                  # Начинаем шифрованный обмен по TLS
server.login(addr_from, password)                   # Получаем доступ
server.send_message(msg)                            # Отправляем сообщение
server.quit()                                       # Выходим |  | 
| 28 | Yandex У Yandex используется порт 465 для обращения к SMTP-серверу (TLS не используется): |  | 
| 29 | Python | 1 2 3 4 5 server = smtplib.SMTP_SSL('smtp.yandex.ru', 465)    # Создаем объект SMTP
#server.starttls()                                  # Начинаем шифрованный обмен по TLS
server.login(addr_from, password)                   # Получаем доступ
server.send_message(msg)                            # Отправляем сообщение
server.quit()                                       # Выходим |  | 
| 30 | Что почитать: |  | 
| 31 | Похожие запросы: 
 |  |