Как отправить электронное письмо с Python?

Этот код работает и отправляет мне письмо просто отлично:

import smtplib
#SERVER = "localhost"

FROM = '[email protected]'

TO = ["[email protected]"] # must be a list

SUBJECT = "Hello!"

TEXT = "This message was sent with Python smtplib."

# Prepare actual message

message = """\
From: %s
To: %s
Subject: %s

%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT)

# Send the mail

server = smtplib.SMTP('myserver')
server.sendmail(FROM, TO, message)
server.quit()

Однако, если я попытаюсь обернуть его в такую ​​функцию:

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
    import smtplib
    """this is some test documentation in the function"""
    message = """\
        From: %s
        To: %s
        Subject: %s
        %s
        """ % (FROM, ", ".join(TO), SUBJECT, TEXT)
    # Send the mail
    server = smtplib.SMTP(SERVER)
    server.sendmail(FROM, TO, message)
    server.quit()

и назовите его, я получаю следующие ошибки:

 Traceback (most recent call last):
  File "C:/Python31/mailtest1.py", line 8, in <module>
    sendmail.sendMail(sender,recipients,subject,body,server)
  File "C:/Python31\sendmail.py", line 13, in sendMail
    server.sendmail(FROM, TO, message)
  File "C:\Python31\lib\smtplib.py", line 720, in sendmail
    self.rset()
  File "C:\Python31\lib\smtplib.py", line 444, in rset
    return self.docmd("rset")
  File "C:\Python31\lib\smtplib.py", line 368, in docmd
    return self.getreply()
  File "C:\Python31\lib\smtplib.py", line 345, in getreply
    raise SMTPServerDisconnected("Connection unexpectedly closed")
smtplib.SMTPServerDisconnected: Connection unexpectedly closed

Может ли кто-нибудь помочь мне понять, почему?

Ответы

Ответ 1

Я рекомендую вам использовать стандартные пакеты email и smtplib вместе для отправки электронной почты. Пожалуйста, посмотрите на следующий пример (воспроизведенный из документации по Python). Обратите внимание, что если следовать этому подходу, "простая" задача действительно проста, а более сложные задачи (например, присоединение двоичных объектов или отправка простых/HTML-сообщений из нескольких частей) выполняются очень быстро.

# Import smtplib for the actual sending function
import smtplib

# Import the email modules we'll need
from email.mime.text import MIMEText

# Open a plain text file for reading.  For this example, assume that
# the text file contains only ASCII characters.
with open(textfile, 'rb') as fp:
    # Create a text/plain message
    msg = MIMEText(fp.read())

# me == the sender email address
# you == the recipient email address
msg['Subject'] = 'The contents of %s' % textfile
msg['From'] = me
msg['To'] = you

# Send the message via our own SMTP server, but don't include the
# envelope header.
s = smtplib.SMTP('localhost')
s.sendmail(me, [you], msg.as_string())
s.quit()

Для отправки электронной почты нескольким получателям вы также можете следовать примеру в документации по Python:

# Import smtplib for the actual sending function
import smtplib

# Here are the email package modules we'll need
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart

# Create the container (outer) email message.
msg = MIMEMultipart()
msg['Subject'] = 'Our family reunion'
# me == the sender email address
# family = the list of all recipients' email addresses
msg['From'] = me
msg['To'] = ', '.join(family)
msg.preamble = 'Our family reunion'

# Assume we know that the image files are all in PNG format
for file in pngfiles:
    # Open the files in binary mode.  Let the MIMEImage class automatically
    # guess the specific image type.
    with open(file, 'rb') as fp:
        img = MIMEImage(fp.read())
    msg.attach(img)

# Send the email via our own SMTP server.
s = smtplib.SMTP('localhost')
s.sendmail(me, family, msg.as_string())
s.quit()

Как видите, заголовок To в объекте MIMEText должен быть строкой, состоящей из адресов электронной почты, разделенных запятыми. С другой стороны, вторым аргументом функции sendmail должен быть список строк (каждая строка является адресом электронной почты).

Итак, если у вас есть три адреса электронной почты: [email protected], [email protected] и [email protected], вы можете сделать следующее (очевидные разделы опущены):

to = ["[email protected]", "[email protected]", "[email protected]"]
msg['To'] = ",".join(to)
s.sendmail(me, to, msg.as_string())

часть ",".join(to) создает из списка одну строку, разделенную запятыми.

Из ваших вопросов я понимаю, что вы не прошли учебник по Python - это ОБЯЗАТЕЛЬНО, если вы хотите получить что-нибудь в Python - документация в основном превосходна для стандартной библиотеки.

Ответ 2

Ну, вы хотите получить ответ, который является современным и современным.

Вот мой ответ:

Когда мне нужно отправить почту на python, я использую mailgun API, который получает много головных болей при рассылке рассылок. У них есть замечательное приложение /api, которое позволяет отправлять 10000 писем в месяц бесплатно.

Отправка сообщения электронной почты будет таким:

def send_simple_message():
    return requests.post(
        "https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages",
        auth=("api", "YOUR_API_KEY"),
        data={"from": "Excited User <[email protected]_DOMAIN_NAME>",
              "to": ["[email protected]", "[email protected]_DOMAIN_NAME"],
              "subject": "Hello",
              "text": "Testing some Mailgun awesomness!"})

Вы также можете отслеживать события и многое другое, см. руководство по быстрому запуску.

Надеюсь, вы найдете это полезным!

Ответ 3

Я хочу помочь вам с отправкой электронных писем, сообщив о пакете yagmail (я сторонник, извините за рекламу, но я чувствую, что это может действительно помочь!).

Весь код для вас:

import yagmail
yag = yagmail.SMTP(FROM, 'pass')
yag.send(TO, SUBJECT, TEXT)

Обратите внимание, что я предоставляю значения по умолчанию для всех аргументов, например, если вы хотите отправить себя, вы можете опустить TO, если вы не хотите тему, вы также можете ее опустить.

Кроме того, цель состоит также в том, чтобы сделать очень легко прикрепить html-код или изображения (и другие файлы).

Когда вы помещаете содержимое, вы можете сделать что-то вроде:

contents = ['Body text, and here is an embedded image:', 'http://somedomain/image.png',
            'You can also find an audio file attached.', '/local/path/song.mp3']

Ничего себе, как легко отправлять вложения! Это займет 20 строк без yagmail;)

Кроме того, если вы установите его один раз, вам больше не придется вводить пароль (и безопасно хранить его). В вашем случае вы можете сделать что-то вроде:

import yagmail
yagmail.SMTP().send(contents = contents)

который намного более краток!

Я бы пригласил вас посмотреть github или установить его непосредственно с помощью pip install yagmail.

Ответ 4

Есть проблема с отступом. Код ниже будет работать:

import textwrap

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
    import smtplib
    """this is some test documentation in the function"""
    message = textwrap.dedent("""\
        From: %s
        To: %s
        Subject: %s
        %s
        """ % (FROM, ", ".join(TO), SUBJECT, TEXT))
    # Send the mail
    server = smtplib.SMTP(SERVER)
    server.sendmail(FROM, TO, message)
    server.quit()

Ответ 5

Попробуйте следующее:

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
    import smtplib
    """this is some test documentation in the function"""
    message = """\
        From: %s
        To: %s
        Subject: %s
        %s
        """ % (FROM, ", ".join(TO), SUBJECT, TEXT)
    # Send the mail
    server = smtplib.SMTP(SERVER)
    "New part"
    server.starttls()
    server.login('username', 'password')
    server.sendmail(FROM, TO, message)
    server.quit()

Он работает для smtp.gmail.com

Ответ 6

Отклоняя код в функции (это нормально), вы также отступали строки строки необработанного сообщения. Но ведущее белое пространство подразумевает складывание (конкатенацию) строк заголовка, как описано в разделах 2.2.3 и 3.2.3 RFC 2822 - Формат интернет-сообщений:

Каждое поле заголовка логически представляет собой одну строку символов, содержащую имя поля, двоеточие и тело тела. Для удобства однако, и для устранения ограничений на 998/78 символов в строке, часть тела поля поля заголовка может быть разделена на несколько линейное представление; это называется "складыванием".

В форме функции вашего вызова sendmail все строки начинаются с пробела и поэтому "разворачиваются" (конкатенированы), и вы пытаетесь отправить

From: [email protected]    To: [email protected]    Subject: Hello!    This message was sent with Python smtplib.

Кроме нашего разума, smtplib больше не будет понимать заголовки To: и Subject:, потому что эти имена распознаются только в начале строки. Вместо этого smtplib будет принимать очень длинный адрес электронной почты отправителя:

[email protected]    To: [email protected]    Subject: Hello!    This message was sent with Python smtplib.

Это не сработает, и наступает ваше исключение.

Решение прост: просто сохраните строку message, как было раньше. Это можно сделать с помощью функции (как предложил Зиесан) или сразу в исходном коде:

import smtplib

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
    """this is some test documentation in the function"""
    message = """\
From: %s
To: %s
Subject: %s

%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT)
    # Send the mail
    server = smtplib.SMTP(SERVER)
    server.sendmail(FROM, TO, message)
    server.quit()

Теперь разворачивание не происходит, и вы отправляете

From: [email protected]
To: [email protected]
Subject: Hello!

This message was sent with Python smtplib.

что и работает, и что было сделано с помощью старого кода.

Обратите внимание, что я также сохранял пустую строку между заголовками и телом, чтобы разместить раздел 3.5 RFC (который требуется) и поставить включить вне функции в соответствии с руководством по стилю Python PEP-0008 (что необязательно).

Ответ 7

Вероятно, он помещает вкладки в ваше сообщение. Распечатайте сообщение, прежде чем передавать его в sendMail.

Ответ 8

Вот пример на Python 3.x, гораздо проще, чем 2.x:

import smtplib
from email.message import EmailMessage
def send_mail(to_email, subject, message, server='smtp.example.cn',
              from_email='[email protected]'):
    # import smtplib
    msg = EmailMessage()
    msg['Subject'] = subject
    msg['From'] = from_email
    msg['To'] = ', '.join(to_email)
    msg.set_content(message)
    print(msg)
    server = smtplib.SMTP(server)
    server.set_debuglevel(1)
    server.login(from_email, 'password')  # user & password
    server.send_message(msg)
    server.quit()
    print('successfully sent the mail.')

вызов этой функции:

send_mail(to_email=['[email protected]', '[email protected]'],
          subject='hello', message='Your analysis has done!')

ниже может быть только для китайского пользователя:

Если вы используете 126/163, 网易 邮箱, вам нужно установить "客户 端 授权 密码", как показано ниже:

введите описание изображения здесь

ref: fooobar.com/questions/32644/... https://docs.python.org/3/library/email.examples.html#email-examples

Ответ 9

Убедитесь, что вы предоставили разрешение отправителю и получателю отправлять электронную почту и получать электронную почту из неизвестных источников (внешних источников) в учетной записи электронной почты.

import smtplib

#Ports 465 and 587 are intended for email client to email server communication - sending email
server = smtplib.SMTP('smtp.gmail.com', 587)

#starttls() is a way to take an existing insecure connection and upgrade it to a secure connection using SSL/TLS.
server.starttls()

#Next, log in to the server
server.login("#email", "#password")

msg = "Hello! This Message was sent by the help of Python"

#Send the mail
server.sendmail("#Sender", "#Reciever", msg)

enter image description here

Ответ 10

Как и ваш код, в нем нет ничего принципиально неправильного, кроме того, неясно, как вы на самом деле называете эту функцию. Все, о чем я могу думать, это то, что когда ваш сервер не отвечает, вы получите эту ошибку SMTPServerDisconnected. Если вы посмотрите на функцию getreply() в smtplib (выдержка ниже), вы получите представление.

def getreply(self):
    """Get a reply from the server.

    Returns a tuple consisting of:

      - server response code (e.g. '250', or such, if all goes well)
        Note: returns -1 if it can't read response code.

      - server response string corresponding to response code (multiline
        responses are converted to a single, multiline string).

    Raises SMTPServerDisconnected if end-of-file is reached.
    """

проверьте пример https://github.com/rreddy80/sendEmails/blob/master/sendEmailAttachments.py, который также использует вызов функции для отправки электронной почты, если это то, что вы пытаетесь сделать ( DRY).

Ответ 11

Думал, что я поставил бы свои два бита, так как я только что понял, как это работает.

Похоже, что у вас нет порта, указанного в настройках подключения SERVER, это немного меня немного улучшило, когда я пытался подключиться к моему SMTP-серверу, который не использует порт по умолчанию: 25.

В соответствии с документами smtplib.SMTP, ваш запрос или ответ ehlo или helo автоматически следует позаботиться, поэтому вам не стоит беспокоиться об этом (но может быть что-то подтвердить, если все остальное не работает).

Еще одна вещь, которую вы задаете себе, - разрешить ли вам SMTP-соединения на вашем SMTP-сервере? Для некоторых сайтов, таких как GMAIL и ZOHO, вы должны фактически подключиться и активировать IMAP-соединения в учетной записи электронной почты. Возможно, ваш почтовый сервер не разрешает соединения SMTP, которые не происходят из "localhost"? Что-то посмотреть.

Последнее, что вы можете попробовать и инициировать подключение к TLS. Большинство серверов теперь требуют такого типа аутентификации.

Вы увидите, что я забил два поля TO в свой адрес электронной почты. Значения слов msg ['TO'] и msg ['FROM'] msg позволяют отображать правильную информацию в заголовках самого электронного письма, которое видно на получающем конце письма в полях To/From (вы может даже быть в состоянии добавить ответ на поле здесь. Поля TO и FROM сами являются тем, что требуется серверу. Я знаю, что слышал о том, что некоторые серверы электронной почты отклоняют электронные письма, если у них нет правильных заголовков электронной почты.

Это код, который я использовал в функции, которая работает для отправки по электронной почте содержимого файла *.txt с помощью моего локального компьютера и удаленного SMTP-сервера (ZOHO, как показано):

def emailResults(folder, filename):

    # body of the message
    doc = folder + filename + '.txt'
    with open(doc, 'r') as readText:
        msg = MIMEText(readText.read())

    # headers
    TO = '[email protected]'
    msg['To'] = TO
    FROM = '[email protected]'
    msg['From'] = FROM
    msg['Subject'] = 'email subject |' + filename

    # SMTP
    send = smtplib.SMTP('smtp.zoho.com', 587)
    send.starttls()
    send.login('[email protected]', 'password')
    send.sendmail(FROM, TO, msg.as_string())
    send.quit()