Отправка писем с помощью Python

Отправка писем с помощью Python

Вы, вероятно, нашли этот учебник, потому что вы хотите отправлять электронные письма, используя Python. Возможно, вы хотите получать по электронной почте напоминания из своего кода, отправлять подтверждение по электронной почте пользователям, когда они создают учетную запись, или отправлять электронные письма членам вашей организации, чтобы напомнить им об оплате их взносов. Отправка электронной почты вручную - это трудоемкая и подверженная ошибкам задача, но ее легко автоматизировать с помощью Python.

In this tutorial you’ll learn how to:

  • Настройтеsecure connection, используяSMTP_SSL() и.starttls()

  • Используйте встроенную библиотеку Pythonsmtplib для отправкиbasic emails

  • Отправляйте электронные письма сHTML content иattachments, используя пакетemail

  • Отправить несколькоpersonalized emails с помощью CSV-файла с контактными данными

  • Используйте пакетYagmail для отправки электронной почты через свою учетную запись Gmail, используя всего несколько строк кода.

В конце этого учебника вы найдете несколько транзакционных почтовых сервисов, которые пригодятся, если вы захотите отправить большое количество писем.

Free Bonus:Click here to get access to a chapter from Python Tricks: The Book, который демонстрирует вам лучшие практики Python на простых примерах, которые вы можете мгновенно применить для написания более красивого кода Pythonic.

Начиная

Python поставляется со встроенным модулемsmtplib для отправки электронных писем с использованием простого протокола передачи почты (SMTP). smtplib использует протоколRFC 821 для SMTP. В примерах этого руководства будет использоваться SMTP-сервер Gmail для отправки электронных писем, но те же принципы применимы и к другим почтовым службам. Хотя большинство провайдеров электронной почты используют те же порты подключения, что и в этом руководстве, вы можете быстро запуститьGoogle search, чтобы подтвердить свой.

Чтобы начать работу с этим руководством, используйтеset up a Gmail account for development илиset up an SMTP debugging server, которые отбрасывают отправленные вами электронные письма и вместо этого печатают их в командной строке. Оба варианта выложены ниже. Локальный сервер отладки SMTP может быть полезен для устранения любых проблем с функциональностью электронной почты и обеспечения исправности функций электронной почты перед отправкой любых электронных писем.

Вариант 1. Настройка учетной записи Gmail для разработки.

Если вы решили использовать учетную запись Gmail для отправки электронных писем, я настоятельно рекомендую создать одноразовую учетную запись для разработки вашего кода. Это связано с тем, что вам придется настроить параметры безопасности своей учетной записи Gmail, чтобы разрешить доступ из кода Python, и потому что есть вероятность, что вы можете случайно раскрыть данные для входа в систему. Кроме того, я обнаружил, что почтовый ящик моей тестовой учетной записи быстро заполняется тестовыми электронными письмами, что является достаточной причиной для создания новой учетной записи Gmail для разработки.

Приятной особенностью Gmail является то, что вы можете использовать знак+ для добавления любых модификаторов к своему адресу электронной почты, прямо перед знаком@. Например, письмо, отправленное на[email protected] и[email protected], будет доставлено на[email protected]. При тестировании функций электронной почты вы можете использовать это для эмуляции нескольких адресов, которые все указывают на один и тот же почтовый ящик.

Чтобы настроить адрес Gmail для тестирования вашего кода, выполните следующие действия:

Если вы не хотите снижать настройки безопасности своей учетной записи Gmail, ознакомьтесь сdocumentation Google о том, как получить учетные данные для вашего скрипта Python с помощью инфраструктуры авторизации OAuth2.

Вариант 2. Настройка локального SMTP-сервера

Функциональность электронной почты можно проверить, запустив локальный сервер отладки SMTP, используя модульsmtpd, который предустановлен вместе с Python. Вместо отправки электронных писем на указанный адрес он отбрасывает их и печатает их содержимое на консоли. Запуск локального сервера отладки означает, что нет необходимости иметь дело с шифрованием сообщений или использовать учетные данные для входа на сервер электронной почты.

Вы можете запустить локальный сервер отладки SMTP, введя в командной строке следующее:

$ python -m smtpd -c DebuggingServer -n localhost:1025

В Linux используйте ту же команду, перед которой стоитsudo.

Любые электронные письма, отправленные через этот сервер, будут отброшены и показаны в окне терминала как объектbytes для каждой строки:

---------- MESSAGE FOLLOWS ----------
b'X-Peer: ::1'
b''
b'From: [email protected]'
b'To: [email protected]'
b'Subject: a local test mail'
b''
b'Hello there, here is a test email'
------------ END MESSAGE ------------

В оставшейся части руководства я предполагаю, что вы используете учетную запись Gmail, но если вы используете локальный сервер отладки, просто обязательно используйтеlocalhost в качестве SMTP-сервера и используйте порт 1025, а не порт 465 или 587. Кроме того, вам не нужно использоватьlogin() или шифровать обмен данными с помощью SSL / TLS.

Отправка простого текстового электронного письма

Прежде чем мы углубимся в отправку электронных писем с HTML-содержимым и вложениями, вы научитесь отправлять обычные текстовые сообщения с использованием Python. Это электронные письма, которые вы можете написать в простом текстовом редакторе. Там нет таких причудливых вещей, как форматирование текста или гиперссылки. Вы узнаете это чуть позже.

Запуск безопасного SMTP-соединения

Когда вы отправляете электронные письма через Python, вы должны убедиться, что ваше SMTP-соединение зашифровано, чтобы другие лица не могли легко получить доступ к вашим данным и учетным данным. SSL (Secure Sockets Layer) и TLS (Transport Layer Security) - это два протокола, которые можно использовать для шифрования SMTP-соединения. Нет необходимости использовать ни один из них при использовании локального сервера отладки.

Существует два способа установить безопасное соединение с вашим почтовым сервером:

  • Запустите SMTP-соединение, которое с самого начала защищено с помощьюSMTP_SSL().

  • Запустите незащищенное соединение SMTP, которое затем можно зашифровать с помощью.starttls().

В обоих случаях Gmail будет шифровать электронную почту с использованием TLS, поскольку это более безопасный преемник SSL. Согласно PythonSecurity considerations, настоятельно рекомендуется использоватьcreate_default_context() из модуляssl. Это загрузит доверенные сертификаты CA системы, включит проверку имени хоста и проверку сертификата и попытается выбрать разумно безопасный протокол и настройки шифра.

Если вы хотите проверить шифрование электронной почты в своем почтовом ящике Gmail, перейдите кMoreShow original, чтобы увидеть тип шифрования, указанный под заголовкомReceived.

smtplib - это встроенный модуль Python для отправки электронной почты на любой компьютер в Интернете с помощью демона прослушивателя SMTP или ESMTP.

Сначала я покажу вам, как использоватьSMTP_SSL(), поскольку он создает экземпляр соединения, которое с самого начала является безопасным и немного более кратким, чем альтернатива.starttls(). Помните, что Gmail требует подключения к порту 465 при использованииSMTP_SSL() и к порту 587 при использовании.starttls().

Вариант 1. ИспользованиеSMTP_SSL()

В приведенном ниже примере кода создается безопасное соединение с SMTP-сервером Gmail с использованиемSMTP_SSL() изsmtplib для инициирования соединения с шифрованием TLS. Контекст по умолчаниюssl проверяет имя хоста и его сертификаты и оптимизирует безопасность соединения. Обязательно укажите свой адрес электронной почты вместо[email protected]:

import smtplib, ssl

port = 465  # For SSL
password = input("Type your password and press enter: ")

# Create a secure SSL context
context = ssl.create_default_context()

with smtplib.SMTP_SSL("smtp.gmail.com", port, context=context) as server:
    server.login("[email protected]", password)
    # TODO: Send email here

Использованиеwith smtplib.SMTP_SSL() as server: гарантирует, что соединение автоматически закрывается в конце блока кода с отступом. Еслиport равен нулю или не указан,.SMTP_SSL() будет использовать стандартный порт для SMTP через SSL (порт 465).

Хранить пароль электронной почты в своем коде небезопасно, особенно если вы собираетесь поделиться им с другими. Вместо этого используйтеinput(), чтобы пользователь мог ввести свой пароль при запуске скрипта, как в примере выше. Если вы не хотите, чтобы ваш пароль отображался на экране при его вводе, вы можете импортировать модульgetpass и использовать вместо него.getpass() для ввода пароля вслепую.

Вариант 2: Использование.starttls()

Вместо использования.SMTP_SSL() для создания безопасного с самого начала соединения, мы можем создать незащищенное SMTP-соединение и зашифровать его с помощью.starttls().

Для этого создайте экземплярsmtplib.SMTP, который инкапсулирует SMTP-соединение и позволяет вам получить доступ к его методам. Я рекомендую определить ваш SMTP-сервер и порт в начале вашего скрипта, чтобы легко их настроить.

В приведенном ниже фрагменте кода используется конструкцияserver = SMTP(), а не форматwith SMTP() as server:, который мы использовали в предыдущем примере. Чтобы гарантировать, что ваш код не выйдет из строя, когда что-то пойдет не так, поместите ваш основной код в блокtry и позвольте блокуexcept выводить любые сообщения об ошибках вstdout:

import smtplib, ssl

smtp_server = "smtp.gmail.com"
port = 587  # For starttls
sender_email = "[email protected]"
password = input("Type your password and press enter: ")

# Create a secure SSL context
context = ssl.create_default_context()

# Try to log in to server and send email
try:
    server = smtplib.SMTP(smtp_server,port)
    server.ehlo() # Can be omitted
    server.starttls(context=context) # Secure the connection
    server.ehlo() # Can be omitted
    server.login(sender_email, password)
    # TODO: Send email here
except Exception as e:
    # Print any error messages to stdout
    print(e)
finally:
    server.quit()

Чтобы идентифицировать себя для сервера, необходимо вызвать.helo() (SMTP) или.ehlo() (ESMTP) после создания объекта.SMTP(), а затем после.starttls(). Эта функция неявно вызывается.starttls() и.sendmail(), если это необходимо, поэтому, если вы не хотите проверять расширения службы SMTP на сервере, нет необходимости использовать.helo() или.ehlo() явно.

Отправка вашего обычного электронного письма

После того, как вы инициировали безопасное SMTP-соединение с помощью любого из вышеперечисленных методов, вы можете отправить электронное письмо с помощью.sendmail(), что в значительной степени делает то, что написано на банке:

server.sendmail(sender_email, receiver_email, message)

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

sender_email = "[email protected]"
receiver_email = "[email protected]"
message = """\
Subject: Hi there

This message is sent from Python."""

# Send email here

messagestring начинается с"Subject: Hi there", за которым следуют два символа новой строки ( ). Это гарантирует, чтоHi there будет отображаться в качестве темы электронного письма, а текст, следующий за символами новой строки, будет рассматриваться как тело сообщения.

В приведенном ниже примере кода отправляется электронное письмо в виде обычного текста с использованиемSMTP_SSL():

import smtplib, ssl

port = 465  # For SSL
smtp_server = "smtp.gmail.com"
sender_email = "[email protected]"  # Enter your address
receiver_email = "[email protected]"  # Enter receiver address
password = input("Type your password and press enter: ")
message = """\
Subject: Hi there

This message is sent from Python."""

context = ssl.create_default_context()
with smtplib.SMTP_SSL(smtp_server, port, context=context) as server:
    server.login(sender_email, password)
    server.sendmail(sender_email, receiver_email, message)

Для сравнения, вот пример кода, который отправляет обычное текстовое электронное письмо через SMTP-соединение, защищенное.starttls(). Строкиserver.ehlo() могут быть опущены, так как они неявно вызываются.starttls() и.sendmail(), если требуется:

import smtplib, ssl

port = 587  # For starttls
smtp_server = "smtp.gmail.com"
sender_email = "[email protected]"
receiver_email = "[email protected]"
password = input("Type your password and press enter:")
message = """\
Subject: Hi there

This message is sent from Python."""

context = ssl.create_default_context()
with smtplib.SMTP(smtp_server, port) as server:
    server.ehlo()  # Can be omitted
    server.starttls(context=context)
    server.ehlo()  # Can be omitted
    server.login(sender_email, password)
    server.sendmail(sender_email, receiver_email, message)

Отправка модных писем

Встроенный в Python пакетemail позволяет структурировать более модные электронные письма, которые затем можно передавать с помощьюsmtplib, как вы это уже сделали. Ниже вы узнаете, как использовать пакетemail для отправки электронных писем с HTML-содержимым и вложениями.

Включая контент HTML

Если вы хотите отформатировать текст в своем электронном письме (bold,italics и т. Д.) Или если вы хотите добавить какие-либо изображения, гиперссылки или адаптивный контент, тогда HTML вам очень пригодится. На сегодняшний день наиболее распространенным типом электронной почты является многокомпонентная электронная почта MIME (Multipurpose Internet Mail Extensions), сочетающая HTML и обычный текст. Сообщения MIME обрабатываются модулем Pythonemail.mime. Для подробного описания проверьтеthe documentation.

Поскольку не все почтовые клиенты отображают HTML-контент по умолчанию, а некоторые люди предпочитают получать только текстовые электронные письма по соображениям безопасности, важно включить текстовую альтернативу для HTML-сообщений. Так как почтовый клиент будет отображать последнее многочастное вложение первым, обязательно добавьте HTML-сообщение после простой текстовой версии.

В приведенном ниже примере наши объектыMIMEText() будут содержать HTML-версии и текстовые версии нашего сообщения, а экземплярMIMEMultipart("alternative") объединяет их в одно сообщение с двумя альтернативными вариантами отображения:

import smtplib, ssl
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

sender_email = "[email protected]"
receiver_email = "[email protected]"
password = input("Type your password and press enter:")

message = MIMEMultipart("alternative")
message["Subject"] = "multipart test"
message["From"] = sender_email
message["To"] = receiver_email

# Create the plain-text and HTML version of your message
text = """\
Hi,
How are you?
Real Python has many great tutorials:
www.realpython.com"""
html = """\

  
    

Hi,
How are you?
Real Python has many great tutorials.

""" # Turn these into plain/html MIMEText objects part1 = MIMEText(text, "plain") part2 = MIMEText(html, "html") # Add HTML/plain-text parts to MIMEMultipart message # The email client will try to render the last part first message.attach(part1) message.attach(part2) # Create secure connection with server and send email context = ssl.create_default_context() with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server: server.login(sender_email, password) server.sendmail( sender_email, receiver_email, message.as_string() )

В этом примере вы сначала определяете обычный текст и сообщение HTML как строковые литералы, а затем сохраняете их как объектыplain /htmlMIMEText. Затем они могут быть добавлены в этом порядке к сообщениюMIMEMultipart("alternative") и отправлены через защищенное соединение с почтовым сервером. Не забудьте добавить сообщение HTML после простого текста, так как почтовые клиенты будут пытаться отобразить последнюю часть сначала.

Добавление вложений с помощью пакетаemail

Чтобы отправлять двоичные файлы на сервер электронной почты, предназначенный для работы с текстовыми данными, их необходимо кодировать перед передачей. Чаще всего это делается с помощьюbase64, который кодирует двоичные данные в печатаемые символы ASCII.

Пример кода ниже показывает, как отправить электронное письмо с PDF-файлом в качестве вложения:

import email, smtplib, ssl

from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

subject = "An email with attachment from Python"
body = "This is an email with attachment sent from Python"
sender_email = "[email protected]"
receiver_email = "[email protected]"
password = input("Type your password and press enter:")

# Create a multipart message and set headers
message = MIMEMultipart()
message["From"] = sender_email
message["To"] = receiver_email
message["Subject"] = subject
message["Bcc"] = receiver_email  # Recommended for mass emails

# Add body to email
message.attach(MIMEText(body, "plain"))

filename = "document.pdf"  # In same directory as script

# Open PDF file in binary mode
with open(filename, "rb") as attachment:
    # Add file as application/octet-stream
    # Email client can usually download this automatically as attachment
    part = MIMEBase("application", "octet-stream")
    part.set_payload(attachment.read())

# Encode file in ASCII characters to send by email
encoders.encode_base64(part)

# Add header as key/value pair to attachment part
part.add_header(
    "Content-Disposition",
    f"attachment; filename= {filename}",
)

# Add attachment to message and convert message to string
message.attach(part)
text = message.as_string()

# Log in to server using secure context and send email
context = ssl.create_default_context()
with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server:
    server.login(sender_email, password)
    server.sendmail(sender_email, receiver_email, text)

СообщениеMIMEultipart() принимает параметры в виде пар ключ / значение в стилеRFC5233, которые хранятся в словаре и передаются в.add_header method базового классаMessage.

Ознакомьтесь сdocumentation для модуля Pythonemail.mime, чтобы узнать больше об использовании классов MIME.

Отправка нескольких персонализированных писем

Представьте, что вы хотите отправлять электронные письма членам вашей организации, чтобы напомнить им о необходимости уплаты взносов. Или, может быть, вы хотите отправить учащимся в своем классе персонализированные электронные письма с оценками для их недавнего задания. Эти задачи очень просты в Python.

Создайте файл CSV с соответствующей личной информацией

Легкая отправная точка для отправки нескольких персонализированных писем - этоcreate a CSV (comma-separated values) file, который содержит всю необходимую личную информацию. (Убедитесь, что не передаете личную информацию других людей без их согласия.) Файл CSV можно рассматривать как простую таблицу, где первая строка часто содержит заголовки столбцов.

Ниже представлено содержимое файлаcontacts_file.csv, который я сохранил в той же папке, что и мой код Python. Он содержит имена, адреса и оценки для вымышленных людей. Я использовал конструкции[email protected], чтобы убедиться, что все электронные письма попадают в мой собственный почтовый ящик, которым в этом примере является[email protected]:

name,email,grade
Ron Obvious,[email protected],B+
Killer Rabbit of Caerbannog,[email protected],A
Brian Cohen,[email protected],C

При создании файла CSV обязательно разделяйте значения через запятую, без каких-либо окружающих пробелов.

Цикл по строкам для отправки нескольких писем

В приведенном ниже примере кода показано, как открыть файл CSV и перебрать его строки содержимого (пропуская строку заголовка). Чтобы убедиться, что код работает правильно, прежде чем отправлять электронные письма всем своим контактам, я напечаталSending email to ... для каждого контакта, который мы позже можем заменить функциональностью, которая фактически отправляет электронные письма:

import csv

with open("contacts_file.csv") as file:
    reader = csv.reader(file)
    next(reader)  # Skip header row
    for name, email, grade in reader:
        print(f"Sending email to {name}")
        # Send email here

В приведенном выше примере использованиеwith open(filename) as file:+`makes sure that your file closes at the end of the code block. `+csv.reader() упрощает построчное чтение файла CSV и извлечение его значений. Строкаnext(reader) пропускает строку заголовка, так что следующая строкаfor name, email, grade in reader: разделяет последующие строки на каждую запятую и сохраняет полученные значения в строкахname,email иgrade для текущего контакта.

Если значения в вашем CSV-файле содержат пробелы с одной или обеих сторон, вы можете удалить их с помощью метода.strip().

Персонализированный контент

Вы можете поместить в сообщение персонализированный контент, используяstr.format() для заполнения фигурных скобок. Например,"hi {name}, you {result} your assignment".format(name="John", result="passed") даст вам"hi John, you passed your assignment".

Начиная с Python 3.6, форматирование строк может быть выполнено более элегантно с использованиемf-strings, но для этого требуется, чтобы заполнители были определены перед самой f-строкой. Чтобы определить сообщение электронной почты в начале сценария и заполнить заполнители для каждого контакта при просмотре файла CSV, используется более старый метод.format().

Имея это в виду, вы можете создать общее тело сообщения с заполнителями, которые могут быть адаптированы для отдельных лиц.

Пример кода

Следующий пример кода позволяет отправлять персонализированные электронные письма нескольким контактам. Он перебирает CSV-файл сname,email,grade для каждого контакта, как вexample above.

Общее сообщение определяется в начале сценария, и для каждого контакта в CSV-файле заполняются его заполнители{name} и{grade}, а персонализированное электронное письмо отправляется через безопасное соединение с Сервер Gmail, как вы видели ранее:

import csv, smtplib, ssl

message = """Subject: Your grade

Hi {name}, your grade is {grade}"""
from_address = "[email protected]"
password = input("Type your password and press enter: ")

context = ssl.create_default_context()
with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server:
    server.login(from_address, password)
    with open("contacts_file.csv") as file:
        reader = csv.reader(file)
        next(reader)  # Skip header row
        for name, email, grade in reader:
            server.sendmail(
                from_address,
                email,
                message.format(name=name,grade=grade),
            )

Yagmail

Существует несколько библиотек, предназначенных для упрощения отправки электронных писем, напримерEnvelopes,Flanker иYagmail. Yagmail разработан специально для работы с Gmail и значительно упрощает процесс отправки электронной почты через дружественный API, как вы можете видеть в примере кода ниже:

import yagmail

receiver = "[email protected]"
body = "Hello there from Yagmail"
filename = "document.pdf"

yag = yagmail.SMTP("[email protected]")
yag.send(
    to=receiver,
    subject="Yagmail test with attachment",
    contents=body,
    attachments=filename,
)

Этот пример кода отправляет электронное письмо с вложением PDF в части строк, необходимых для нашегоexample using email and smtplib.

При настройке Yagmail вы можете добавить свои проверки Gmail в связку ключей вашей ОС, как описано вthe documentation. Если вы этого не сделаете, Yagmail предложит вам ввести пароль в случае необходимости и автоматически сохранить его в связке ключей.

Транзакционные почтовые сервисы

Если вы планируете отправлять большое количество сообщений электронной почты, хотите просматривать статистику электронной почты и хотите обеспечить надежную доставку, возможно, стоит обратиться к транзакционным службам электронной почты. Хотя все следующие сервисы имеют платные планы для отправки больших объемов сообщений электронной почты, они также поставляются с бесплатным планом, чтобы вы могли их опробовать. Некоторые из этих бесплатных тарифных планов действительны на неопределенный срок и могут быть достаточными для вашей электронной почты.

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

поставщик Бесплатный план

Sendgrid

40 000 писем в течение первых 30 дней, затем 100 писем в день

Sendinblue

300 писем в день

Mailgun

Первые 10 000 писем бесплатно

Mailjet

200 писем в день

Amazon SES

62000 писем в месяц

Вы можете запуститьGoogle search, чтобы узнать, какой провайдер лучше всего соответствует вашим потребностям, или просто попробуйте несколько бесплатных планов, чтобы узнать, с каким API вам больше всего нравится работать.

Пример кода Sendgrid

Вот пример кода для отправки электронных писем сSendgrid, чтобы дать вам представление о том, как использовать сервис транзакционной электронной почты с Python:

import os
import sendgrid
from sendgrid.helpers.mail import Content, Email, Mail

sg = sendgrid.SendGridAPIClient(
    apikey=os.environ.get("SENDGRID_API_KEY")
)
from_email = Email("[email protected]")
to_email = Email("[email protected]")
subject = "A test email from Sendgrid"
content = Content(
    "text/plain", "Here's a test email sent through Python"
)
mail = Mail(from_email, subject, to_email, content)
response = sg.client.mail.send.post(request_body=mail.get())

# The statements below can be included for debugging purposes
print(response.status_code)
print(response.body)
print(response.headers)

Чтобы запустить этот код, вы должны сначала:

Более подробную информацию о том, как настроить Sendgrid для Mac и Windows, можно найти в README репозитория наGithub.

Заключение

Теперь вы можете установить безопасное SMTP-соединение и отправить несколько персонализированных электронных писем людям из вашего списка контактов!

Вы узнали, как отправлять электронную почту в формате HTML с альтернативой в виде простого текста и прикреплять файлы к своей электронной почте. ПакетYagmail упрощает все эти задачи при использовании учетной записи Gmail. Если вы планируете отправлять большие объемы электронной почты, стоит обратить внимание на транзакционные почтовые сервисы.

Наслаждайтесь отправкой писем с помощью Python и помните:no spam please!

Related