Как правильно отправлять почту из кода (Python)? - PullRequest
0 голосов
/ 05 июня 2018

Отказ от ответственности : я колебался в названии из-за широкого характера этого вопроса (см. Ниже ;-), другие варианты включены:

  • Как отправить письмос локального хоста, используя только код Python?
  • Как отправить электронное письмо из кода Python, без использования внешнего SMTP-сервера?
  • Возможно ли отправить сообщение ПРЯМО в адрес назначения, используя localhost иТолько на Python?

Во-первых, немного контекста :Ради обучения я создаю сайт с функцией регистрации пользователей.Идея заключается в том, что после регистрации пользователь получит письмо со ссылкой для активации.Я хотел бы составить и отправить электронное письмо из кода Python, и это та часть, где я хотел бы попросить некоторые разъяснения.


Мое понимание, прежде чем я начал (очевидно,Наивный =) можно проиллюстрировать следующим образом (учитывая, что существует допустимый адрес электронной почты user@example.com):

My naive understanding

После поиска примеров я наткнулся на некоторые вопросы и ответы по stackoverflow ( 1 , 2 , 3 , 4 ).Из этого я извлек следующий фрагмент кода, чтобы составить и отправить электронное письмо из кода Python:

import smtplib

from email.message import EmailMessage

message = EmailMessage()
message.set_content('Message content here')
message['Subject'] = 'Your subject here'
message['From'] = 'me@example.com'
message['To'] = 'user@example.com'

smtp_server = smtplib.SMTP('smtp.server.address:587')
smtp_server.send_message(message)
smtp_server.quit()

Следующий (очевидный) вопрос заключался в том, что передать smtplib.SMTP() вместо 'smtp.server.address:587'.Из комментариев к к этому ответу я обнаружил, что локальный SMTP-сервер (только для целей тестирования) можно запустить через python3 -m smtpd -c DebuggingServer -n localhost:1025, затем smtp_server = smtplib.SMTP('smtp.server.address:587') можно изменить на smtp_server = smtplib.SMTP('localhost:1025') и все отправленные электронные письмабудет отображаться в консоли (откуда была выполнена команда python3 -m smtpd -c DebuggingServer -n localhost:1025), и этого будет достаточно для тестирования - это было не то, что я хотел (моей целью было - возможность отправлять почту на реальный адрес электронной почты с локального компьютера), используя только код Python).

Итак, следующим шагом будет настройка локального SMTP-сервера, способного отправлять электронную почту на внешний «реальный» адрес электронной почты (как я хотел сделать все этоиз кода Python, поэтому сам сервер лучше будет реализовывать и в Python).Я вспомнил, как читал в каком-то журнале (в начале 2000 года), что спамеры используют локальные серверы для отправки почты (в этой конкретной статье говорилось о Sambar , разработка которого закончилась в 2007 году и которая не была написана на Python:-) Я думал, что должно быть какое-то современное решение с подобной функциональностью.Поэтому я начал искать, я надеялся найти (в стеке или в другом месте) достаточно короткий фрагмент кода, который будет делать то, что я хотел.Я не нашел такого фрагмента кода, но натолкнулся на фрагмент под названием (Python) Отправить электронную почту без почтового сервера (который использует chilkat API), хотя все, что мне было нужно (предположительно) прямо там, в комментариях к коду, в первой строке было четко указано:

Действительно ли возможно отправлять электронную почту без подключения к почтовому серверу?Не совсем.

и несколько строк ниже:

Вот что происходит внутри тех других компонентов, которые утверждают, что им не нужен почтовый сервер: Компонент выполняет поиск DNS MXиспользуя адрес электронной почты получателя, чтобы найти почтовый сервер (т.е. SMTP-сервер) для этого домена.Затем он подключается к этому серверу и доставляет электронную почту.Вы по-прежнему подключаетесь к SMTP-серверу, но не к ВАШЕМУ серверу.

Прочитав это, я понял - мне явно не хватало некоторых деталей в моем понимании (как показано на рисунке выше)процесс.Чтобы исправить это, я прочитал весь RFC по SMTP .


После прочтения RFC мое улучшенное понимание процесса может быть изображено так: My improved understanding


Из этого понимания пришли актуальные вопросы Я хотел бы уточнить:

  1. Можетмое " улучшенное понимание " будет считаться правильным, как общая картина?
  2. Какие именно адреса возвращаются поиском MX?

    • с помощью команды host -t mx gmail.com (предложено этим ответом ), я смог получить следующее:

      gmail.com mail is handled by 10 alt1.gmail-smtp-in.l.google.com.
      gmail.com mail is handled by 20 alt2.gmail-smtp-in.l.google.com.
      gmail.com mail is handled by 40 alt4.gmail-smtp-in.l.google.com.
      gmail.com mail is handled by 30 alt3.gmail-smtp-in.l.google.com.
      gmail.com mail is handled by 5 gmail-smtp-in.l.google.com.
      
    • , но ни один из них не упомянут в официальных документах (которые там есть: smtp-relay.gmail.com, smtp.gmail.com, aspmx.l.google.com)
  3. Всегда ли требуется проверка подлинности для передачи электронной почты на SMTP-сервер установленной почтовой службы (скажем, Gmail)?

    • Я понимаю, что дляиспользуйте, скажем, smtp.gmail.com для отправки почты, вам понадобится, независимо от того, есть ли у получателя адрес @gmail или нет (как указано в документах ):

      Ваш полный адрес электронной почты Gmail или G Suite требуется для аутентификации.

    • Но, если письмо на user@gmail.com отправлено на SMTP-сервер, не принадлежащий gmail, то оноВы будете перенаправлены на один из серверов Gmail (напрямую или через шлюз / ретранслятор).В этом случае (я полагаю) отправителю электронной почты нужно будет пройти аутентификацию только при отправке почты, поэтому после этого сервер gmail будет принимать почту без аутентификации?

      • Если да, что мешает мне «притворяться» таким шлюзом / ретранслятором и передавать сообщения электронной почты непосредственно на назначенные им SMTP?Тогда также должно быть довольно легко написать «прокси-SMTP», который будет просто искать соответствующий сервер через поиск MX и перенаправлять ему электронные письма, вроде как напрямую.
  4. Документация по SMTP gmail , также упоминает aspmx.l.google.com сервер, который не требует аутентификации, хотя:

    Почту можно отправлять только пользователям Gmail или G Suite.

    Учитывая вышесказанное, я предполагаю, что следующий фрагмент кода должен работать для отправки почты в почтовый ящик ExistingUser@gmail.com:

    import smtplib
    
    from email.message import EmailMessage
    
    message = EmailMessage()
    message.set_content('Message test content')
    message['Subject'] = 'Test mail!'
    message['From'] = 'me@whatever.com'
    message['To'] = 'ExistingUser@gmail.com'
    
    smtp_server = smtplib.SMTP('aspmx.l.google.com:25')
    smtp_server.send_message(message)
    smtp_server.quit()
    

    При запуске приведенный выше код (с заменой ExistingUser@gmail.com действительной почтой) выдает OSError: [Errno 65] No route to host.Все, что я хочу подтвердить, это то, что сообщение на aspmx.l.google.com правильно обрабатывается в коде.

Ответы [ 2 ]

0 голосов
/ 06 мая 2019

Благодаря этим ответам, на мои дополнительные вопросы: 1 , 2 , 3 , а также эти два вопроса (и ответы) других людей: один , два - Я думаю, что теперь я готов самостоятельно ответить на вопросы, которые я отправил.


Я отвечу на вопросы одинпо одному:

  1. Да, как общая картина, отправка электронного письма может быть изображена так: My improved understanding

  2. MX lookup возвращает адрес (а) серверов, которые получают электронную почту, предназначенную для указанного домена.

    • Относительно "Почему smtp-relay.gmail.com, smtp.gmail.com, aspmx.l.google.com не являютсявозвращено командой host -t mx gmail.com?Этот момент, в значительной степени, охвачен другим ответом на этот вопрос.Основные моменты, на которые следует обратить внимание:
      • серверов, возвращаемых поиском MX, отвечают за получение писем электронной почты для домена (в данном случае gmail)
      • серверов в спискев gmail документы предназначены для почты отправки (т. е. письма, которые пользователь gmail хочет отправить другому пользователю gmail или иным образом, отправляются на эти серверы)
  3. Проверка подлинности не требуется для серверов, получающих электронную почту (т. Е. Те, которые возвращены поиском MX).

    • Есть пара вещей, которые мешаютзлоупотребление такими серверами:
      • многие интернет-провайдеры блокируют исходящие соединения с портом 25 (который является портом по умолчанию для серверов приема почты), чтобы предотвратить такую ​​" прямую " отправку почты
      • существует множество мер, принятых на стороне принимающих серверов, которые, в основном, предназначены для предотвращения рассылки спама, но в результате, вероятно, также предотвратят такую ​​" прямую " отправку почты (некоторые электронныеВот примеры: DNSBL - список заблокированных IP-адресов, DKIM - это метод проверки подлинности электронной почты, предназначенный для обнаружения поддельных адресов отправителей в электронных письмах (если у вас нет собственного, легитимного почтового сервера)., вы будете использовать чужой домен для поля From, в котором DKIM может ударить вас)
  4. Фрагмент кода в порядке.Ошибка возникает, по всей вероятности, из-за блокировки на стороне провайдера.


С учетом всего сказанного, фрагмент кода:

import smtplib

from email.message import EmailMessage

message = EmailMessage()
message.set_content('Message content here')
message['Subject'] = 'Your subject here'
message['From'] = 'me@example.com'
message['To'] = 'user@example.com'

smtp_server = smtplib.SMTP('smtp.server.address:25')
smtp_server.send_message(message)
smtp_server.quit()

фактически отправит электронное письмо (см. этот вопрос , для реального рабочего примера), учитывая, что smtp.server.address:25 является законным сервером и нет блокировок на стороне провайдера и / или smtp.server.address.

0 голосов
/ 12 июля 2018

Ваше понимание того, как работает почта, является приблизительно правильным.Некоторые дополнительные примечания, которые могут прояснить ситуацию:

  • SMTP используется для двух разных целей.Вы, кажется, путаете эти два.:

    • Первое использование, обычно называемое "отправкой", - это отправка почты из MUA (Mail User Agent, вашей почтовой программы, Outlook,Thunderbird, ...) для MTA (Mail Transfer Agent, обычно называемый «почтовый сервер»).MTA управляются вашим ISP или почтовыми провайдерами, такими как GMail.Как правило, их использование ограничено либо IP-адресом (его могут использовать только клиенты указанного интернет-провайдера), либо именем пользователя / паролем.

    • Второе использование - отправка почты от одного MTA другомуMTA.Эта часть, как правило, широко открыта, так как вы, вероятно, готовы принимать входящую почту от кого-либо.Это также место, где принимаются антиспамовые меры.

Чтобы отправить письмо, вам нужна, как минимум, вторая часть SMTP:возможность общаться с другим MTA для доставки почты.

Типичным способом отправки почты является составление письма в вашем приложении, а затем отправка его на почтовый сервер MTA для доставки.В зависимости от настроек этот MTA может быть установлен на той же машине, на которой выполняется код Python (localhost), или может быть более «центральным» почтовым сервером (возможно, требующим аутентификацию).

"Ваш«MTA позаботится обо всех неприятных деталях доставки почты, таких как:

  • Выполнение поиска DNS, чтобы выяснить контакты MTA для пересылки почты.Это включает в себя поиск MX, но также и другие резервные механизмы, такие как A-записи .

  • Повторная попытка доставки, если первая попытка временно не удалась

  • Генерирование сообщения об отказе, если сообщение не удается окончательно

  • Сделать несколько копий сообщения, в случае нескольких получателей в разных доменах

  • Подписание сообщения с помощью DKIM для уменьшения вероятности того, что оно будет помечено как СПАМ.

  • ...

Вы, конечно, могли бы повторно реализовать все эти функции в своем собственном коде Python и эффективно объединить MTA с вашим приложением, но я настоятельно рекомендую против этого.Почту на удивление трудно получить правильно ...

Итог: попробуйте отправить почту через SMTP на почтовый сервер вашего провайдера или другую почтовую службу.Если это невозможно: подумайте очень серьезно, если вы хотите запустить свой собственный почтовый сервер.Быть отмеченным как спамер происходит легко;удалить из спам-списков гораздо сложнее.Не переустанавливайте SMTP-код в вашем приложении.

...