Python: отправка почты через smtplib с вложениями (Яндекс и Gmail)

ИИ и нейросети для почты · Обновлено: · 4 мин чтения
Python: отправка почты через smtplib с вложениями (Яндекс и Gmail)
Содержание
  1. Что нужно перед стартом
  2. Параметры серверов
  3. Простое письмо через Яндекс
  4. Gmail через порт 587
  5. Вложения и HTML
  6. Типичные ошибки
  7. Частые вопросы

Для отправки писем из Python хватает стандартных модулей: smtplib подключается к серверу, email.message.EmailMessage собирает письмо. Установка сторонних библиотек не нужна. Важно сделать две вещи правильно: использовать пароль приложения вместо основного пароля и хранить его не в коде, а в переменных окружения. Ниже готовые примеры для Яндекса и Gmail, а также отправка вложений и ответы на типичные ошибки.

Что нужно перед стартом

  1. Python 3 (с python.org).
  2. Ящик с включенным доступом по SMTP. Для Яндекса, Mail.ru и Gmail нужен пароль приложения (Gmail - при включенной двухэтапной аутентификации). См. пароль приложения для почты.
  3. Переменные окружения с логином и паролем. В Windows PowerShell: $env:MAIL_USER = "you@yandex.ru" и $env:MAIL_PASS = "пароль-приложения". В Linux и macOS: export MAIL_USER=....
Пароль приложения для Python и smtplib в Gmail
Пароль приложения для Python и smtplib в Gmail

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

Параметры серверов

Сервис Сервер Порт Шифрование
Яндекс smtp.yandex.ru 465 SSL
Mail.ru smtp.mail.ru 465 SSL
Gmail smtp.gmail.com 465 SSL
Gmail smtp.gmail.com 587 STARTTLS

Разницу портов объясняет статья порты почтовых серверов.

Простое письмо через Яндекс

Порт 465 означает, что соединение сразу шифруется, поэтому используется SMTP_SSL.

import os
import smtplib
from email.message import EmailMessage

user = os.environ["MAIL_USER"]
password = os.environ["MAIL_PASS"]

msg = EmailMessage()
msg["From"] = user
msg["To"] = "friend@example.com"
msg["Subject"] = "Тест из Python"
msg.set_content("Привет! Это письмо отправлено скриптом.")

with smtplib.SMTP_SSL("smtp.yandex.ru", 465) as server:
    server.login(user, password)
    server.send_message(msg)

Адрес в поле From должен совпадать с логином ящика: Яндекс отклонит письмо с чужим отправителем.

Gmail через порт 587

Для STARTTLS соединение сначала открывается без шифрования, а затем переводится в защищенный режим командой starttls().

import os
import smtplib
from email.message import EmailMessage

user = os.environ["MAIL_USER"]
password = os.environ["MAIL_PASS"]

msg = EmailMessage()
msg["From"] = user
msg["To"] = "friend@example.com"
msg["Subject"] = "Письмо через Gmail"
msg.set_content("Текст письма")

with smtplib.SMTP("smtp.gmail.com", 587) as server:
    server.starttls()
    server.login(user, password)
    server.send_message(msg)

Вложения и HTML

Файл добавляется методом add_attachment. Тип вложения нужно указать вручную. Для PDF это application/pdf.

from pathlib import Path

path = Path("schet.pdf")
msg.add_attachment(
    path.read_bytes(),
    maintype="application",
    subtype="pdf",
    filename=path.name,
)

Чтобы письмо было с оформлением, добавьте HTML-версию вторым вариантом: msg.add_alternative("<h1>Счет</h1>", subtype="html"). Текстовую версию оставьте: так письмо корректно откроется в любых клиентах. Если файл большой, помните об ограничениях на размер вложения, см. как отправить большой файл.

Python отправка почты: запуск скрипта с переменными окружения
Python отправка почты: запуск скрипта с переменными окружения

Типичные ошибки

Ошибка Причина Что делать
SMTPAuthenticationError 535 Неверный пароль или использован основной пароль Создайте пароль приложения
SMTPSenderRefused Адрес "From" не совпадает с логином Укажите адрес ящика
Connection timed out Заблокирован порт или неверный сервер Проверьте сервер и порт, попробуйте 587
Письмо ушло в спам Нет подписи домена, много ссылок См. почему письма попадают в спам

Расшифровка кодов - в статье Ошибки SMTP.

Частые вопросы

Можно ли хранить пароль прямо в скрипте?

Не стоит: файл легко отправить или выложить случайно. Используйте переменные окружения или отдельный файл настроек вне репозитория.

Чем отличаются SMTP_SSL и starttls?

SMTP_SSL шифрует соединение с первой секунды (порт 465). starttls() переключает обычное соединение на шифрованное (порт 587). Оба варианта безопасны.

Сколько писем можно отправить за день?

У каждого сервиса есть свои дневные лимиты, они могут меняться. Для больших списков лучше использовать сервис рассылок, см. рассылка писем.

Можно ли попросить ИИ написать такой скрипт?

Да, это быстрый способ получить заготовку. Подробнее в статье про Claude Code и почту. Код все равно проверяйте и запускайте сначала на себе.