Пайтон содержит несколько действительно полезных модулей, которые можно использовать для создания электронной рассылки. Это модули email и smtplib. Вместо того чтобы рассматривать различные методы в этих двух модулях, мы уделим больше времени на то, как их, собственно, использовать. В частности, мы рассмотрим следующие этапы работы:
- Основы работы с почтой;
- Как выполнять рассылку нескольким адресатам за раз;
- Как отправлять письма, используя строки TO, CC и BCC
- Как создавать содержимое и тело письма при помощи модуля email.
Начнем!
Основы работы с почтой – как отправлять письма при помощи smtplib
Модуль smtplib весьма интуитивен в пользовании. Давайте проведем небольшой пример, который покажет, как при помощи данного модуля отправлять почту. Сохраните нижеизложенный код в файле на вашем жестком диске:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import smtplib HOST = "mySMTP.server.com" SUBJECT = "Test email from Python" TO = "mike@someAddress.org" FROM = "python@mydomain.com" text = "Python 3.4 rules them all!" BODY = "\r\n".join(( "From: %s" % FROM, "To: %s" % TO, "Subject: %s" % SUBJECT , "", text )) server = smtplib.SMTP(HOST) server.sendmail(FROM, [TO], BODY) server.quit() |
Мы выполнили импорт двух модулей, smtplib и string. Две трети этого кода используются для настройки почты. Большая часть переменных прекрасно объясняют сами себя, так что мы сфокусируемся на BODY. Мы воспользуемся модулем string, чтобы скомбинировать все переменные в единую строку, в которой каждая строчка заканчивается“/r”, а новая начинается с “/n”. После вывода BODY вы получите следующую картину:
1 2 |
'From: python@mydomain.com\r\nTo: mike@mydomain.com\r\nSubject: Test email from \ Python\r\n\r\nblah blah blah' |
После этого, нам нужно установить соединение нашего хоста с сервером, после чего мы вызываем метод sendmail в модуле smtplib и отправляем письмо. После этого мы отключаемся от сервера. Обратите внимание на то, что в этом коде нет пароля и логина. Если ваш сервер требует залогиниться, вам нужно будет добавить следующий код:
1 |
server.login(username, password) |
Эта часть должна быть добавлена сразу после создания объекта сервер. Как правило, вам, скорее всего, захочется добавить этот код в функцию и вызвать его с теми или иными параметрами. Или же вам может потребоваться использовать часть этой информации в файл config. Попробуем добавить этот код в функцию.
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 |
import smtplib def send_email(host, subject, to_addr, from_addr, body_text): """ Send an email """ BODY = "\r\n".join(( "From: %s" % from_addr, "To: %s" % to_addr, "Subject: %s" % subject , "", body_text )) server = smtplib.SMTP(host) server.sendmail(from_addr, [to_addr], BODY) server.quit() if __name__ == "__main__": host = "mySMTP.server.com" subject = "Test email from Python" to_addr = "mike@someAddress.org" from_addr = "python@mydomain.com" body_text = "Python rules them all!" send_email(host, subject, to_addr, from_addr, body_text) |
Теперь вы видите, насколько маленький код на самом деле, если взглянуть на саму функцию. Всего 13 строчек. Мы можем сделать его еще короче, если не будем добавлять каждый элемент в BODY в его собственной строке, но это будет не так читаемо. Сейчас мы создадим файл config, чтобы сберечь информацию сервера и адреса.
Есть вопросы по Python?
На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!
Паблик VK
Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!
Зачем? К примеру, в работе вам может понадобиться несколько почтовых серверов для рассылки писем, или если почтовый сервер обновился, и название теперь новое. В таком случае нам нужно только изменить файл config, а не менять сам код. То же самое может понадобиться, если ваша компания была куплена, или слилась с другой. Давайте взглянем на файл config (сохраните его под названием email.ini):
1 2 3 |
[smtp] server = some.server.com from_addr = python@mydomain.com |
Это очень простой файл config. В нем есть секция, под названием smtp, в которой у нас есть два объекта: server и from_addr. Мы воспользуемся configObj для чтения этого файла и вернемся к словарю Пайтон. Вот обновленная версия кода (сохраните её под именем smtp_config.py):
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 |
import os import smtplib import sys from configparser import ConfigParser def send_email(subject, to_addr, body_text): """ Send an email """ base_path = os.path.dirname(os.path.abspath(__file__)) config_path = os.path.join(base_path, "email.ini") if os.path.exists(config_path): cfg = ConfigParser() cfg.read(config_path) else: print("Config not found! Exiting!") sys.exit(1) host = cfg.get("smtp", "server") from_addr = cfg.get("smtp", "from_addr") BODY = "\r\n".join(( "From: %s" % from_addr, "To: %s" % to_addr, "Subject: %s" % subject , "", body_text )) server = smtplib.SMTP(host) server.sendmail(from_addr, [to_addr], BODY) server.quit() if __name__ == "__main__": subject = "Test email from Python" to_addr = "mike@someAddress.org" body_text = "Python rules them all!" send_email(subject, to_addr, body_text) |
Мы добавили в этот код небольшую проверку. Нам нужно взять путь, в котором находится сам скрипт, который представляет base_path. Далее мы комбинируем данный путь с названием файла для получения окончательного пути к файлу config. После этого мы проверим, существует ли данный файл. Если да, мы создаем ConfigParser, если нет, мы выводим сообщение и выходим из скрипта. Мы можем добавить обработчик исключений в рамках вызова ConfigParser.read() чтобы проверить, что файл существует, но поврежден, или у нас закрыт к нему доступ, что и будет причиной исключения. Это будет небольшой проект, который вы можете выполнить самостоятельно. В любом случае, представим, что все хорошо и объект ConfigParser был успешно создан. Теперь мы можем извлечь хост и информацию from_addr при помощи обычного синтаксиса ConfigParser.
Сайт doctorsmm.com предлагает заказать ботов в Инстаграм по очень недорогим ценам. При этом Вы получаете не просто массовку на страницу, а аудиторию среднего качества с естественным для Вашего аккаунта приростом. А это значит, что Вы можете не беспокоиться за безопасность своего профиля! Торопитесь совершить заказ, пока на сайте действуют большие оптовые скидки!
Теперь мы готовы к тому, чтобы научиться отправлять несколько писем сразу.
Одновременная отправка нескольких писем
Давайте немного модифицируем предыдущий пример так, чтобы отправить несколько писем!
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 |
import os import smtplib import sys from configparser import ConfigParser def send_email(subject, body_text, emails): """ Send an email """ base_path = os.path.dirname(os.path.abspath(__file__)) config_path = os.path.join(base_path, "email.ini") if os.path.exists(config_path): cfg = ConfigParser() cfg.read(config_path) else: print("Config not found! Exiting!") sys.exit(1) host = cfg.get("smtp", "server") from_addr = cfg.get("smtp", "from_addr") BODY = "\r\n".join(( "From: %s" % from_addr, "To: %s" % ', '.join(emails), "Subject: %s" % subject , "", body_text )) server = smtplib.SMTP(host) server.sendmail(from_addr, emails, BODY) server.quit() if __name__ == "__main__": emails = ["mike@someAddress.org", "someone@gmail.com"] subject = "Test email from Python" body_text = "Python rules them all!" send_email(subject, body_text, emails) |
Обратите внимание на то, что в этом примере мы убрали параметр to_addr и добавили параметр emails, который является списком электронных почтовых адресов. Чтобы это заработало, нам нужно создать разделенную запятыми строку в части BODY под названием «To:» ,а также указать список адресов в методе sendmail. Таким образом, мы получаем рабочую, разделенную запятыми строку: ’, ‘.join(emails). Ничего сложного, не правда ли?
Рассылка писем при помощи строчек TO, CC и BCC
Сейчас нам нужно будет понять, как отправлять письма при помощи полей CC и BCC. Для этого мы создадим новую версию этого кода, которая поддерживает данный функционал.
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 |
import os import smtplib import sys from configparser import ConfigParser def send_email(subject, body_text, to_emails, cc_emails, bcc_emails): """ Send an email """ base_path = os.path.dirname(os.path.abspath(__file__)) config_path = os.path.join(base_path, "email.ini") if os.path.exists(config_path): cfg = ConfigParser() cfg.read(config_path) else: print("Config not found! Exiting!") sys.exit(1) host = cfg.get("smtp", "server") from_addr = cfg.get("smtp", "from_addr") BODY = "\r\n".join(( "From: %s" % from_addr, "To: %s" % ', '.join(to_emails), "CC: %s" % ', '.join(cc_emails), "BCC: %s" % ', '.join(bcc_emails), "Subject: %s" % subject , "", body_text )) emails = to_emails + cc_emails + bcc_emails server = smtplib.SMTP(host) server.sendmail(from_addr, emails, BODY) server.quit() if __name__ == "__main__": emails = ["mike@somewhere.org"] cc_emails = ["someone@gmail.com"] bcc_emails = ["schmuck@newtel.net"] subject = "Test email from Python" body_text = "Python rules them all!" send_email(subject, body_text, emails, cc_emails, bcc_emails) |
В этом коде мы указали три списка, каждый из которых содержит по одному электронному адресу. Мы создали поля CC и BCC таким же образом, как делали это ранее, с той разницей, что теперь нам нужно соединить три списка в один, чтобы мы могли указать полученный список методу sendmail(). На разных форумах пишут, что некоторые почтовые клиенты обрабатывают поле BCC нечётно, из-за чего получатель может видеть содержимое списка ВСС через заголовки писем. Однако мы точно знаем, что Gmail успешно удаляет ВСС данные из заголовка письма.
Теперь мы готовы перейти к модулю email!
Добавление вложения \ тела письма при помощи модуля email
Теперь мы возьмем все то, чему мы научились в предыдущих разделах и смешаем их вместе с модулем email, чтобы отправлять письма с прикрепленными файлами. Модуль email позволяет прикреплять вложения к письму очень просто. Вот код:
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 |
import os import smtplib import sys from configparser import ConfigParser from email import encoders from email.mime.text import MIMEText from email.mime.base import MIMEBase from email.mime.multipart import MIMEMultipart from email.utils import formatdate #---------------------------------------------------------------------- def send_email_with_attachment(subject, body_text, to_emails, cc_emails, bcc_emails, file_to_attach): """ Send an email with an attachment """ base_path = os.path.dirname(os.path.abspath(__file__)) config_path = os.path.join(base_path, "email.ini") header = 'Content-Disposition', 'attachment; filename="%s"' % file_to_attach # get the config if os.path.exists(config_path): cfg = ConfigParser() cfg.read(config_path) else: print("Config not found! Exiting!") sys.exit(1) # extract server and from_addr from config host = cfg.get("smtp", "server") from_addr = cfg.get("smtp", "from_addr") # create the message msg = MIMEMultipart() msg["From"] = from_addr msg["Subject"] = subject msg["Date"] = formatdate(localtime=True) if body_text: msg.attach( MIMEText(body_text) ) msg["To"] = ', '.join(to_emails) msg["cc"] = ', '.join(cc_emails) attachment = MIMEBase('application', "octet-stream") try: with open(file_to_attach, "rb") as fh: data = fh.read() attachment.set_payload( data ) encoders.encode_base64(attachment) attachment.add_header(*header) msg.attach(attachment) except IOError: msg = "Error opening attachment file %s" % file_to_attach print(msg) sys.exit(1) emails = to_emails + cc_emails server = smtplib.SMTP(host) server.sendmail(from_addr, emails, msg.as_string()) server.quit() if __name__ == "__main__": emails = ["mike@someAddress.org", "nedry@jp.net"] cc_emails = ["someone@gmail.com"] bcc_emails = ["anonymous@circe.org"] subject = "Test email with attachment from Python" body_text = "This email contains an attachment!" path = "/path/to/some/file" send_email_with_attachment(subject, body_text, emails,cc_emails, bcc_emails, path) |
В данном коде мы переименовали нашу функцию и добавили новый аргумент file_to_attach. Нам также нужно добавить заголовок и создать объект MIMEMultipart. Заголовок может быть создан в любое время, перед тем как мы добавим вложение. Мы добавим элементы в объект MIMEMultipart (msg) также, как и ключевые слова в словарь. Обратите внимание на то, что нам нужно использовать метод formatdate модуля email, чтобы ввести данные правильного формата. Чтобы добавить тело письма, нам нужно создать экземпляр MIMEText. Как вы могли заметить, мы не добавляем информацию BCC, но вы можете легко это сделать, следуя условиям написанного выше кода. Далее мы добавляем вложение. Мы размещаем его в обработчике исключений и используем оператор with для извлечения файла и помещения его в наш объект MIMEBase. Наконец мы добавляем его в переменную msg и отправляем. Обратите внимание на то, что нам нужно конвертировать msg в методе sendmail в строку.
Подведем итоги
Теперь вы знаете, как отправлять почту при помощи Python. Для тех из вас, кто любит мини проекты, вы можете вернуться назад и добавить обработку дополнительную ошибку в кодовой части server.sendmail, если что-то не так пойдет во время процесса работы, типа ошибки SMTPAuthenticationError или SMTPConnectError. Мы можем также увеличить обработку ошибок во время прикрепления файла к телу письма, чтобы уловить другие ошибки. Наконец, мы можем взять списки различных писем, чтобы создать один нормальный, в котором отсутствуют дубликаты адресов. Это очень важно, если мы читаем список электронных адресов из файла. Также обратите внимание на то, что адрес from – не настоящий. Мы можем подделывать письма при помощи Пайтона и других языков программирование, однако это крайне неэтично, а в некоторых странах еще и нелегально. Имейте ввиду! Используйте свои знания правильно и пользуйтесь Пайтоном для удовольствия и с пользой.
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: vasile.buldumac@ati.utm.md
Образование
Universitatea Tehnică a Moldovei (utm.md)
- 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»