Подписывайтесь:

Блог AST-SoftPro

Автоматическая рассылка коммерческих предложений: Python + SMTP + шаблоны

18.06.2026 7 мин чтения
Автоматическая рассылка коммерческих предложений: 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 писем), проверьте доставляемость, затем масштабируйте.


AI-Помощник