14 мая 2018
Кравченко Виктор

Python: Как отправить письмо на электронную почту с вложением

Программирование Python
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

Результат:

20
21

Но здесь нужно учитывать, что для разных почтовых провайдеров необходимо модифицировать строки отправки сообщения в соответствии с предоставляемыми настройками. О них далее.

22

Gmail

Компания Google очень беспокоится о безопасности своих пользователей, поэтому перед тем как начинать отправку сообщений, необходимо разрешить доступ к аккаунту для ненадежных приложений:

23
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() # Выходим
31

Похожие запросы:

  • Как отправить электронное письмо с помощью Python: руководство для «чайников»
  • Как отправлять email на python
  • Отправка писем из python
  • Python отправка email на gmail
  • Работа с почтой — модули email / smtplib в Python
  • Python. Простая отправка email уведомлений.
  • Отправка почты в Python через SMTP — Python
  • Python SMTP для отправки почты
  • Отправка почты с помощью smtplib
  • Отправка email при помощи python
comments powered by HyperComments