Що нам варто сервіс email-маркетингу побудувати? Погляд зсередини, частина перша

Наскільки складно побудувати повноцінний сервіс email-маркетингу? Що для цього потрібно передбачити? Які підводні камені можуть зустрітися на шляху допитливих умів розробників?



Давайте спробуємо розібратися разом. В рамках декількох статей я розповім про те, як я за рік зробив свій власний сервіс email розсилок, які уроки для себе витягнув і що планую з усім цим робити далі.

Відразу обмовлюся, що в статті розглянута тільки технічна сторона питання.

Коротко про себе
Я пишу на Python от вже 5 років, в основному використовую Django, PostgreSQL, вмію готувати JavaScript на рівні jQuery + KnockoutJS. У вільний від основної роботи займаюся фрілансом і власними інтернет-проектами, про один з яких зараз і планую розповісти. Займаюся я цим проектом вже близько року.

Мета проекту
На самому початку мною була поставлена досить проста мета — створити працююче рішення для відправки транзакційних листів та email розсилок з функціями відстеження відкриттів, переходів, неможливості доставки листів, скарг на спам. Використовувати я це рішення планував в інших своїх проектах, оскільки Яндекс ПДР (пошта для домену), яку я використав до цього, такими функціями не мала, а вони були потрібні.

Тоді ще не йшлося про те, щоб дати це рішення у вигляді SaaS всім користувачам в Інтернеті.

Завдання
  • Зрозуміти, як працює відстеження подій email розсилках, розібратися з трекінгом.
  • Придумати рішення, яке буде працювати під середніми навантаженнями (2-3 мільйони листів в місяць). Чому саме 2-3 мільйони? Я вважаю, що такий обсяг необхідний, щоб окупати такий проект (витрачений час + матеріальні ресурси типу серверів).
  • Реалізувати зручний інтерфейс для аналітики масових і транзакційних розсилок.
Далі я постараюся більш-менш докладно зупинитися на тому, як я виконав кожну з цих завдань.

Технології
Використовувати я вирішив ті технології, які знаю — Python, Django, PostgreSQL, KnockoutJS, LESS, py.test.

Додатково у процесі роботи над проектом я непогано розібрався в Celery і микросервисной архітектурі.

На цьому я пропоную закінчити вступну частину і перейти до найцікавішого — практиці.

Як працює трекінг email-повідомлень?


Коли ви відправляєте повідомлення, ви напевно хочете знати, дійшли ваші листи адресатам, прочитали вони їх взагалі, цікаві вони їм, перейшли вони посилання в листі, що після цього вони робили на сайті, здійснили цільове дію — придбання, замовлення, дзвінок і так далі.

Отримати відповіді на ці питання ви можете тільки з допомогою трекінгу або систем типу Яндекс.Метрики (ну або запитавши своїх одержувачів особисто).

Відстеження відкриттів листів

Сьогодні стандартним підходом для відстеження відкриттів листів є впровадження спеціального пікселя в лист — ви можете побачити цей піксель в більшій частині рекламних листів у вашій поштовій скриньці, якщо подивитеся вихідні листи. Він може виглядати приблизно так:

<img src="http://api.mailhandler.ru/message/track/<UNIQUE_EMAIL_ID>/OPENED/" width="1px" height="1px" border="0"/>

Зрозуміло, що при запиті на URL, вказаний в атрибуті src зображення, має відбуватися додавання події, що означає, що лист з id рівним UNIQUE_EMAIL_ID було відкрито.

Однак не все так просто. Дуже часто у src зображення вказують URL, ведучий на який-небудь php-скрипт, і не думають про те, що поштовий сервіс дуже хоче отримати у відповідь валідні для зображення заголовки, а так само зображення. Якщо поштовий сервіс з цієї причини розчаровується у вашому пікселі, він просто видалить його з листа і ви не дізнаєтеся про те, відкрив ваш адресат листа чи ні.

Для того, щоб цього не сталося, слід додати коректні заголовки відповіді і віддати валидное зображення клієнту. Реалізація на Django Rest Framework може виглядати приблизно так:

class TrackMessageView(APIView):
renderer_classes = [JPEGRenderer]

@property
def pixel(self):
return open(os.path.join(settings.STATIC_ROOT, 'site/img/pixel.jpg'), 'rb')

def get(self, request, *args, **kwargs):
manager = BaseManager()
message = manager.get_message_by_unique_id(self.kwargs['unique_id'])
if message:
manager.track_message(message)
return Response(self.pixel.read(), status=201)
return Response(status=404)

Відстеження переходів по посиланнях в листі



Думаю, у допитливого читача не повинно виникнути проблем з реалізацією такого типу трекінгу. Загалом — кожна посилання в листі замінюється на посилання через спеціальний сервіс перенаправлення, який створює подія типу «Перехід за посиланням». Додатково можна до кожної посиланням додавати унікальний ідентифікатор — тоді ви зможете реалізувати «теплову карту» листи. Це дуже корисна функція, наприклад, для А/Б тестування.

Реалізація на Python виглядає досить просто:

REDIRECT_URL_TEMPLATE = '%s/message/redirect/%s/'
HREF_REGEXP = r'(?<=href=(\"|\'))(http|https)([^\"\']+)(?=(\"|\'))'
...

def replace_links(message):
redirect_url = REDIRECT_URL_TEMPLATE % (settings.API_URL, message.unique_id)
message.html_body = re.sub(HREF_REGEXP, r'%s?next=\2\3' % redirect_url, message.html_body)
...

Відстеження неможливості доставки листів

А ось з цим все набагато цікавіше.

Кожен раз, коли поштовий сервер не може доставити ваш лист, у відповідь на адресу відправника йде звіт про неможливість доставки з описом причини (іноді докладним, іноді — так собі). Для обробки цих вхідних листів я використовував підхід, який полягає в пробросе вхідного листа на Python скрипт обробника через /etc/aliases.

Приклад шматочка листи для аналізу:

Final-Recipient: rfc822; ****@****.ru
Original-Recipient: rfc822; ****@****.ru
Action: failed
Status: 4.4.1
Diagnostic-Code: X-Postfix; connect to ****.ru[xx.xx.xxx.xxx]:25: Connection refused

Сам скрипт намагається більш-менш інтелектуально зрозуміти причину неможливості доставки листа і створює подія Soft-Bounce (лист в даний момент не може бути доставлена, але ви зможете спробувати ще разок) або Hard-Bounce (лист не буде доставлено ніколи, наприклад тому що ящик не існує).

Тут важливо зробити невелику ремарку про те, як власне потрібно реагувати на такі події згідно правил поштових сервісів типу Mail.ru, Yandex та ін

сервіси, які здійснюють розсилки на основі передплати, повинні безумовно видаляти з бази передплатників або вживати заходів щодо припинення розсилок на адреси, які генерують помилку протоколу SMTP: 550 user not found (відстеження валідності бази одержувачів — необхідна умова для підтримання позитивної репутації рассыльщика);
» Посилання на список правил

Таким чином, мені необхідно було передбачити «вимикання» передплатників, на адреси яких неможливо доставити пошту. В підсумку я прийшов до того, що вимикаю передплатника з усіх списків передплатників на сервісі.

Ну ось, з трекінгом начебто розібралися.

Трохи статистики
В даний момент через мій сервіс відправляється близько 150 000 листів на місяць. Багато це, чи мало? Напевно мало, враховуючи обсяги, які я собі поставив у рамках означених завдань.

З них:

  • 20% — відкриті (це досить великий відсоток, насправді, дякую транзакційний поштою)
  • 13% — переходи по посиланнях
  • 9% — Hard/Soft bounce
p.s.
У наступних статтях я розповім про те, як і чим я обробляю ці дані, розповім про тонкощі використання celery в подібних проектах, а так само зупинюся на тому, що я планую робити з цим сервісом далі.

Спасибі за увагу!
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.