Как отправить электронное письмо с Python? - PullRequest
146 голосов
/ 07 июня 2011

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

import smtplib
#SERVER = "localhost"

FROM = 'monty@python.com'

TO = ["jon@mycompany.com"] # must be a list

SUBJECT = "Hello!"

TEXT = "This message was sent with Python's 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

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

Ответы [ 11 ]

169 голосов
/ 08 июня 2011

Я рекомендую использовать стандартные пакеты 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's email address
# you == the recipient's 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's 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 должен быть список строк (каждая строка является адресом электронной почты).

Итак, если у вас есть три адреса электронной почты: person1@example.com, person2@example.com и person3@example.com, вы можете сделать следующее (очевидные разделы опущены):

to = ["person1@example.com", "person2@example.com", "person3@example.com"]
msg['To'] = ",".join(to)
s.sendmail(me, to, msg.as_string())

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

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

60 голосов
/ 09 февраля 2016

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

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

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

Отправка электронной почты будет выглядеть так:

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 <mailgun@YOUR_DOMAIN_NAME>",
              "to": ["bar@example.com", "YOU@YOUR_DOMAIN_NAME"],
              "subject": "Hello",
              "text": "Testing some Mailgun awesomness!"})

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

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

35 голосов
/ 07 декабря 2015

Я бы хотел помочь вам с отправкой электронных писем, посоветовав пакет 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.

14 голосов
/ 12 августа 2015

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

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()

8 голосов
/ 10 мая 2015

Попробуйте это:

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

4 голосов
/ 08 февраля 2016

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

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

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

From: monty@python.com    To: jon@mycompany.com    Subject: Hello!    This message was sent with Python's smtplib.

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

monty@python.com    To: jon@mycompany.com    Subject: Hello!    This message was sent with Python's smtplib.

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

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

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: monty@python.com
To: jon@mycompany.com
Subject: Hello!

This message was sent with Python's smtplib.

, вот чтоработает и что было сделано вашим старым кодом.

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

3 голосов
/ 07 июня 2011

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

2 голосов
/ 29 декабря 2018

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

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

2 голосов
/ 30 ноября 2017

Вот пример на 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='xx@example.com'):
    # 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=['12345@qq.com', '12345@126.com'],
          subject='hello', message='Your analysis has done!')

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

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

enter image description here

ref: https://stackoverflow.com/a/41470149/2803344 https://docs.python.org/3/library/email.examples.html#email-examples

0 голосов
/ 11 июня 2016

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

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

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

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

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

Вы увидите, что я забил два поля TO в своем письме. Элементы словаря msg ['TO'] и msg ['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 = 'to_user@domain.com'
    msg['To'] = TO
    FROM = 'from_user@domain.com'
    msg['From'] = FROM
    msg['Subject'] = 'email subject |' + filename

    # SMTP
    send = smtplib.SMTP('smtp.zoho.com', 587)
    send.starttls()
    send.login('from_user@domain.com', 'password')
    send.sendmail(FROM, TO, msg.as_string())
    send.quit()
...