Блог AST-SoftPro
Автоматическая рассылка коммерческих предложений: Python + SMTP + шаблоны
Автоматизация рассылки КП
Коммерческие предложения — основной инструмент продаж B2B. Ручная отправка занимает время: нужно открыть письмо, вставить шаблон, подставить имя клиента, приложить файл и отправить. Python автоматизирует весь процесс: генерация персонализированных писем, отправка через SMTP, отслеживание доставленности.
Установка зависимостей
pip install jinja2 python-dotenv yagmail
Генерация HTML-шаблона с Jinja2
from jinja2 import Template
from datetime import datetime
KP_TEMPLATE = '''
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.header { background: #2c3e50; color: white; padding: 20px; text-align: center; }
.content { padding: 20px; }
.price-table { border-collapse: collapse; width: 100%; margin: 20px 0; }
.price-table th, .price-table td { border: 1px solid #ddd; padding: 8px; }
.price-table th { background: #3498db; color: white; }
.footer { background: #ecf0f1; padding: 10px; text-align: center; font-size: 12px; }
</style></head>
<body>
<div class="header">
<h1>Коммерческое предложение</h1>
<p>{{ company_name }}</p>
</div>
<div class="content">
<p>Уважаемый(ая) {{ client_name }}!</p>
<p>Благодарим за интерес к нашим услугам. Направляем вам индивидуальное предложение:</p>
<table class="price-table">
<tr><th>Услуга</th><th>Срок</th><th>Стоимость</th></tr>
{% for service in services %}
<tr><td>{{ service.name }}</td><td>{{ service.term }}</td><td>{{ service.price }} руб.</td></tr>
{% endfor %}
<tr><td colspan="2"><b>Итого</b></td><td><b>{{ total }} руб.</b></td></tr>
</table>
<p>Действительно до: {{ valid_until }}</p>
<p>С уважением,<br>{{ sender_name }}<br>{{ sender_phone }}</p>
</div>
<div class="footer">
<p>{{ company_name }} | {{ company_address }} | {{ date_today }}</p>
</div>
</body>
</html>
'''
def generate_kp_html(client_name, services, company_name='Наша Компания'):
template = Template(KP_TEMPLATE)
total = sum(s['price'] for s in services)
html = template.render(
client_name=client_name,
services=services,
company_name=company_name,
total=total,
valid_until=(datetime.now() + timedelta(days=30)).strftime('%d.%m.%Y'),
sender_name='Иван Петров',
sender_phone='+7 (999) 123-45-67',
company_address='г. Москва, ул. Примерная, д. 1',
date_today=datetime.now().strftime('%d.%m.%Y'),
)
return html
from datetime import timedelta
Отправка через SMTP
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import csv
def send_kp(client_email, client_name, services):
"""Отправка персонализированного КП."""
html_content = generate_kp_html(client_name, services)
msg = MIMEMultipart('alternative')
msg['Subject'] = f'Коммерческое предложение для {client_name}'
msg['From'] = 'sales@company.ru'
msg['To'] = client_email
msg.attach(MIMEText(html_content, 'html'))
try:
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login('your_email@gmail.com', 'your_app_password')
server.send_message(msg)
server.quit()
print(f"✅ КП отправлено: {client_email}")
except Exception as e:
print(f"❌ Ошибка отправки на {client_email}: {e}")
# Рассылка по списку клиентов
def send_to_all_clients(csv_file, services):
with open(csv_file, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
send_kp(row['email'], row['name'], services)
import time
time.sleep(2) # Задержка между письмами
services_list = [
{'name': 'Разработка сайта', 'term': '2 недели', 'price': 150000},
{'name': 'SEO-оптимизация', 'term': '1 месяц', 'price': 50000},
]
send_to_all_clients('clients.csv', services_list)
Отслеживание открытий и кликов
Для аналитики можно использовать трекер-пиксель:
<!-- Вставьте в конец HTML-письма -->
<img src="https://your-server.com/tracker?email={{ client_email }}" width="1" height="1" />
from flask import Flask, request, send_file
import sqlite3
app = Flask(__name__)
@app.route('/tracker')
def track_open():
email = request.args.get('email')
if email:
conn = sqlite3.connect('tracking.db')
conn.execute(
'INSERT INTO opens (email, opened_at) VALUES (?, CURRENT_TIMESTAMP)',
(email,)
)
conn.commit()
conn.close()
return send_file('pixel.gif', mimetype='image/gif')
@app.route('/click/<kp_id>')
def track_click(kp_id):
conn = sqlite3.connect('tracking.db')
conn.execute('UPDATE kps SET clicked_at = CURRENT_TIMESTAMP WHERE id = ?', (kp_id,))
conn.commit()
conn.close()
# Редирект на целевую страницу
from flask import redirect
return redirect('https://company.ru/landing')
Сравнение подходов к рассылке
| Метод | Преимущества | Ограничения |
|---|---|---|
| SMTP (smtplib) | Полный контроль, нет лимитов сервиса | Настройка сервера, риск попадания в спам |
| SendGrid API | Аналитика, высокая доставляемость | Платный от 100 писем/день |
| Mailgun API | Вебхуки, шаблоны | Платный, требует верификации домена |
| Telegram-бот | Быстро, бесплатно | Только для клиентов с Telegram |
Заключение
Автоматизация рассылки КП на Python экономит часы ручной работы. Ключевые компоненты:
-
Jinja2 для генерации персонализированных HTML-шаблонов;
-
SMTP для отправки писем;
-
Flask-эндпоинты для отслеживания открытий и кликов;
-
CSV-файл со списком клиентов для массовой рассылки.
Начните с небольшой рассылки (10-20 писем), проверьте доставляемость, затем масштабируйте.