Пригодится для решения задач по организации рассылок, а также как средство предварительной проверки эл. почты при регистрации.
Это пересказ довольно старой статьи (2015 г) некоего Скотта Бради. Автор предупреждает, что попытка реализовать предложенный подход в промышленных масштабах приведёт вас в списки спамеров, и вообще всё это крайне ненадежно и сомнительно. А реализовано больше с целью познакомиться с процессом, что собственно предлагаю и сделать.
Процесс верификации состоит из 4 частей:
Проверка синтаксиса
Проверка домена и MX-записи
Проверка ответа helo
И, наконец, server.rcpt (готовность принять на конкретный адрес)
Проверка синтаксиса
По проверке синтаксиса материалов довольно много, можно ознакомиться самостоятельно (1, 2, 3). Скотт предлагает своё выражение, которое тоже вполне рабочее.
import re
match = re.match('^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$', addressToVerify)
Проверка домена и MX-записи
Пожалуй самое ценное во всём этом материале, это проверка домена и существования на нём маршрутизации электронных писем. Делается при помощи библиотеки dnspython.
import dns.resolver
try:
records = dns.resolver.resolve("mail.ru", 'MX')
mxRecord = records[0].exchange
mxRecord = str(mxRecord)
except Exception as ex:
print(ex)
По опыту использования именно здесь и было большинство ошибок, либо несуществующие домены, либо отсутствие MX-записи. Что автоматически означает бессмысленность попытки отправить почту.
Проверка ответа helo
В то же время существование домена и наличие MX-записи не говорят, о том, что письмо может быть отправлено, с сервером надо вступить в разговор.
import smtplib
server = smtplib.SMTP()
server.set_debuglevel(0)
try:
server.connect(mxRecord)
code, message = server.helo(host)
except Exception as ex:
print(ex)
На этом этапе ошибки ловить не приходилось. Если всё хорошо, code будет 250, и ответное сообщение сервера разной степени учтивости. У mail.ru это 'mxs.mail.ru', Гугл более приветливый и отвечает:
mx.google.com at your service
Тут стоит обратить внимание, что проверка осуществляется без подключения к реальному почтовому серверу (крупные сервисы к этому спокойно относятся, а вот более мелкие корпоративные могут на этом этапе уже резать запрос).
А ещё есть проверка ehlo.
Готовность принять на конкретный адрес
В библиотеке smtplib есть специальный метод SMTP.verify(address), однако прямо в документации обозначено, что многие сервисы его поотключали из-за деятельности спамеров, поэтому предлагается другой способ проверки, существования конкретного ящика, а именно SMTP.rcpt(address).
server.mail('any@mail.box')
code, message = server.rcpt(str(addressToVerify))
Это низкоуровневый метод, который идентифицирует конверт получателя. Если всё хорошо, получаем 250 код и сообщение типа:
(250, b'Go ahead')
В этом месте тоже ловится много проблем. Например, mail.ru не стесняется говорить: "Go ahead" при явно несуществующих адресах. Гугл, даёт более содержательные ответы по содержанию конверта.
(550, b"5.1.1 The email account that you tried to reach does not exist. Please try double-checking the recipient's email address for typos or unnecessary spaces. Learn more at https://support.google.com/mail/?p=NoSuchUser ")
Ещё раз отмечу, что все проверки выполнялись без подключения к реальному почтовому серверу, и в ряде случаев к ящику не пускает спам-асассин, есть ещё множество подводных камней.
Если коротко, то по большому счёту это ручной проход всех этапов отправки письма кроме собственно отправки.
Закон о рекламе
На всякий случай напомню, что рассылки попадают под закон о рекламе и требуется предварительное согласие на её получение. Почтовые сервисы требуют указывать в подобных письмах где было получено согласие на получение и большими буквами надпись "ОТПИСАТЬСЯ".