Вложения и массовая рассылка писем
Отчёт ценен с вложением. Модуль email умеет прикреплять файлы любого типа, а шаблонизация позволяет разослать персональные письма всему списку получателей.
Суть: msg.add_attachment(данные, тип, имя) прикрепляет файл. Массовая рассылка = цикл по получателям с персональным телом из шаблона.В прошлом уроке мы отправили текстовое письмо. Но обычно цель — доставить файл: Excel-отчёт, PDF-счёт, договор. EmailMessage добавляет вложение методом add_attachment, которому нужны три вещи: байты файла, его MIME-тип (например application/pdf) и имя, под которым получатель его увидит.
Массовая рассылка соединяет это с шаблонами из урока про Word: для каждого получателя подставляем его данные в текст письма. Логику персонализации и подготовки рассылки соберём в браузере на stdlib.
Попробуй сам ▶
from email.message import EmailMessage
# Список рассылки
recipients = [
{'email': '[email protected]', 'name': 'Анна', 'debt': 0},
{'email': '[email protected]', 'name': 'Борис', 'debt': 4200},
{'email': '[email protected]', 'name': 'Вика', 'debt': 1500},
]
body_tpl = 'Здравствуйте, {name}! {tail}'
queue = []
for r in recipients:
if r['debt'] > 0:
tail = f"Напоминаем о задолженности {r['debt']} руб."
else:
tail = 'Спасибо, у вас нет задолженностей.'
msg = EmailMessage()
msg['To'] = r['email']
msg['Subject'] = 'Состояние счёта'
msg.set_content(body_tpl.format(name=r['name'], tail=tail))
queue.append(msg)
print(f'Подготовлено писем: {len(queue)}')
for m in queue:
print('->', m['To'], '|', m.get_content().strip())Каждое письмо получает персональный текст в зависимости от данных получателя — это и есть суть умной рассылки. Прикрепление реального файла и отправка трогают диск и сеть, поэтому показаны как нерабочая в браузере врезка.
from pathlib import Path
from email.message import EmailMessage
import smtplib, os
msg = EmailMessage()
msg['From'] = '[email protected]'
msg['To'] = '[email protected]'
msg['Subject'] = 'Ваш счёт'
msg.set_content('Счёт во вложении.')
data = Path('invoice.pdf').read_bytes()
msg.add_attachment(data, maintype='application',
subtype='pdf', filename='invoice.pdf')
with smtplib.SMTP_SSL('smtp.company.ru', 465) as s:
s.login('[email protected]', os.environ['EMAIL_PASSWORD'])
s.send_message(msg)МАССОВАЯ РАССЫЛКА
список получателей
| для каждого:
v
шаблон + данные -> персональное письмо
| + вложение (add_attachment)
v
send_message --пауза--> следующий
пауза между письмами спасает от спам-лимитаДля серьёзных рассылок одного цикла по адресам мало — нужна идемпотентность. Представьте: скрипт разослал половину писем и упал на сбое сети. При повторном запуске наивный цикл отправит первую половину заново, и часть людей получит письмо дважды. Решение — вести журнал уже отправленных адресов (хоть в простом файле) и перед отправкой проверять, не ушло ли письмо этому получателю. Тогда повторный запуск спокойно «доберёт» оставшихся, не беспокоя тех, кто уже получил. Это тот самый принцип идемпотентности из начала курса, применённый к рассылке: повторный запуск не должен ломать уже сделанную работу.
Как работает под капотом
Письмо с вложением — это multipart-сообщение: оно состоит из частей (тело + каждый файл), разделённых служебными границами. add_attachment сам создаёт нужную структуру и кодирует бинарный файл в base64, потому что по протоколу почты можно передавать только текст. Получатель декодирует обратно. Вам всё это знать необязательно — метод прячет сложность.
MIME-тип (maintype/subtype) подсказывает почтовому клиенту, чем открыть вложение: application/pdf — PDF, image/png — картинка. Неверный тип не сломает доставку, но клиент может не предложить правильное приложение для открытия.
Частые ошибки
- Слать всё одним залпом. Сотни писем за секунду — прямой путь в спам-фильтр. Делайте паузы.
- Один сбой роняет всю рассылку. Оборачивайте каждое письмо в try, чтобы плохой адрес не остановил остальные.
- Видимые адреса всех получателей. Не кладите весь список в To; для рассылки шлите по одному письму на адрес.
Best practices
- Читайте файл как байты (
read_bytes()) и указывайте корректный MIME-тип. - Делайте небольшую паузу между письмами и логируйте каждый результат.
- Ведите учёт отправленных, чтобы при сбое не разослать дубли при повторном запуске.
Итоги. add_attachment прикрепляет файлы, а шаблон по списку получателей даёт персональную рассылку. Паузы и обработка ошибок отличают надёжную рассылку от спама. Дальше научимся забирать данные из веба.