Como enviar um e-mail com Python?

Este código funciona e envia-me um e-mail muito bem.
import smtplib
#SERVER = "localhost"

FROM = '[email protected]'

TO = ["[email protected]"] # 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()

No entanto, se eu tentar envolvê-lo numa função como esta:

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

e chama - lhe Eu recebo os seguintes erros:

 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
Alguém me pode ajudar a perceber porquê?

10 answers

Eu recomendo que você use os pacotes padrãoemail e smtplib juntos para enviar e-mail. Por favor, veja o seguinte exemplo (reproduzido na documentação em Python). Observe que se você seguir esta abordagem, a tarefa "simples" é realmente simples, e as tarefas mais complexas (como anexar objetos binários ou enviar mensagens simples/HTML multipart) são realizadas muito rapidamente.

# 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()
Para enviar e-mail para vários destinos, pode siga também o exemplo na documentação 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()

Como pode ver, o cabeçalho To no objecto MIMEText deve ser um texto que consiste em endereços de E-mail separados por vírgulas. Por outro lado, o segundo argumento para a função sendmail deve ser uma lista de strings (cada string é um endereço de E-mail).

Então, se você tem três endereços de E-mail: [email protected], [email protected], e [email protected], pode fazer o seguinte (secções óbvias omitidas):
to = ["[email protected]", "[email protected]", "[email protected]"]
msg['To'] = ",".join(to)
s.sendmail(me, to, msg.as_string())

A parte "","".join(to) faz uma única corda fora da lista, separada por vírgulas.

De suas perguntas, eu percebi que você não passou por o tutorial em Python - é uma obrigação se você quiser chegar a algum lugar em Python - a documentação é principalmente excelente para a biblioteca padrão.

 157
Author: Escualo, 2017-11-21 19:20:32
Bem, queres ter uma resposta actualizada e moderna. Eis a minha resposta: Quando preciso de enviar correio em python, uso a arma de correio que dá muitas dores de cabeça ao enviar e-mails. Eles têm um aplicativo/api wonderfull que lhe permite enviar 10.000 e-mails por mês gratuitamente. Enviar um e-mail seria assim:
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": ["[email protected]", "YOU@YOUR_DOMAIN_NAME"],
              "subject": "Hello",
              "text": "Testing some Mailgun awesomness!"})

Você também pode acompanhar eventos e muito mais, veja O Guia de arranque rápido .

Espero que sim. você acha isso útil!
 51
Author: Dennis Decoene, 2016-02-09 16:16:14
Eu gostaria de ajudá-lo com o envio de E-mails aconselhando o pacote yagmail (eu sou o mantenedor, desculpe a publicidade, mas eu sinto que ele pode realmente ajudar!).

O código completo para ti seria:

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

Note que eu forneço valores por omissão para todos os argumentos, por exemplo, se quiser enviar para si mesmo, pode omitir {[[3]}, Se não quiser um assunto, pode omiti-lo também.

Além disso, o objectivo é também tornar muito fácil anexar código html ou imagens (e outros arquivo).

Onde se coloca o conteúdo pode-se fazer algo como:

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']
Como é fácil enviar anexos! Isto levaria umas 20 linhas sem o yagmail;)

Também, se o configurar uma vez, nunca mais terá de introduzir a senha (e guardá-la em segurança). No seu caso, pode fazer algo como:

import yagmail
yagmail.SMTP().send(contents = contents)
O que é muito mais conciso!

Convidava-te a dar uma vista de olhos ao github ou instalá-lo directamente com pip install yagmail.

 32
Author: PascalVKooten, 2015-12-07 17:44:36

Há um problema de indentação. O código abaixo funcionará:

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

 11
Author: Zeeshan, 2015-08-12 07:05:28

Tenta isto:

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

Funciona para smtp.gmail.com

 7
Author: An Epic Person, 2015-05-10 10:32:48

Ao indentar o seu código na função (o que é ok), você também indentou as linhas da cadeia de mensagens raw. Mas o espaço branco principal implica a dobragem (concatenação) das linhas de cabeçalho, tal como descrito nos pontos 2.2.3 e 3.2.3 de RFC 2822-formato de mensagem Internet:

Cada campo de cabeçalho é logicamente uma única linha de caracteres compreendendo o nome do campo, o cólon e o corpo do campo. Por conveniência no entanto, e para tratar do 998/78 limitações de caracteres por linha, a porção de corpo de campo de um campo de cabeçalho pode ser dividida em um múltiplo representação em linha; isto é chamado de "dobrar".

Na forma de função da sua chamada sendmail, todas as linhas estão a começar com o espaço branco e por isso estão "desdobradas" ( concatenadas) e você está a tentar enviar

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

Além do que a nossa mente sugere, smtplib não vai entender os cabeçalhos To: e Subject: por mais tempo, porque estes nomes só são reconhecidos no início de um linha. Em vez disso smtplib irá assumir um endereço de E-mail muito longo do remetente:

[email protected]    To: [email protected]    Subject: Hello!    This message was sent with Python's smtplib.
Isto não vai funcionar e assim será a tua excepção.

A solução é simples: basta preservar a sequência message como era antes. Isto pode ser feito por uma função (como Zeeshan sugeriu) ou imediatamente no código fonte:

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()
Agora o desdobramento não ocorre e você envia
From: [email protected]
To: [email protected]
Subject: Hello!

This message was sent with Python's smtplib.
Que é o que funciona e o que foi feito pelo teu antigo código. Note que eu também estava a preservar o ... uma linha vazia entre cabeçalhos e corpos para acomodar a secção 3.5 de o RFC (que é necessário) e colocar a inclusão fora da função de acordo com o Guia de estilo Python PEP-0008 (que é opcional).
 4
Author: flaschbier, 2016-02-08 07:50:51
Deve estar a pôr abas na tua mensagem. Imprime a mensagem antes de a passar para o sendMail.
 3
Author: Nick ODell, 2011-06-07 19:52:47

Aqui está um exemplo em Python 3.x, muito mais simples do que 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.')

Chama esta função:

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

Em baixo só pode ser utilizado para o utilizador Chinês:

Se utilizar 126 / 163, 网易邮箱, deve definir "客户端授权密码", como em baixo:

enter image description here

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

 1
Author: Belter, 2017-11-30 11:29:53
No que diz respeito ao seu código, não parece haver nada de errado com ele, excepto que não está claro como está a chamar essa função. Tudo o que posso pensar é que quando o seu servidor não está respondendo então você vai obter este erro SMTPServerDisconnected. Se você procurar a função getreply() no smtplib (excerpt abaixo), você terá uma idéia.
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.
    """

Verifique um exemplo em https://github.com/rreddy80/sendEmails/blob/master/sendEmailAttachments.py que também usa uma chamada de função para enviar um e-mail, se é isso que está a tentar fazer (abordagem seca).

 0
Author: rreddy, 2016-02-12 11:48:49
Pensei em pôr os meus dois pedaços aqui, já que acabei de descobrir como isto funciona.

Parece que não tem o porto indicado na configuração da ligação do seu servidor, isto afectou-me um pouco quando estava a tentar ligar-me ao meu servidor de SMTP que não está a usar o porto predefinido: 25.

De acordo com o smtplib.SMTP docs, o seu EHLO ou helo pedido / resposta deve ser automaticamente tratado, para que você não deve ter que se preocupar com isso (mas pode ser algo para confirme se tudo o resto falhar).

Outra coisa a perguntar a si mesmo é se você permitiu conexões SMTP no seu próprio servidor SMTP? Para alguns sites como GMAIL e ZOHO você tem que realmente entrar e ativar as conexões IMAP dentro da conta de E-mail. O seu servidor de E-mail poderá não permitir ligações SMTP que não venham de 'localhost', talvez? Algo para investigar.

A última coisa é que você pode querer tentar iniciar a conexão em TLS. A maioria dos servidores necessita agora deste tipo de autenticacao.

Vais ver que coloquei dois campos no meu e-mail. Os itens do dicionário msg ['para'] e msg['de'] msg permitem que a informação correta apareça nos cabeçalhos do próprio E-mail, que se vê no final de recepção do E-mail nos campos de Para/Para (você pode até ser capaz de adicionar uma resposta ao campo aqui. Os campos de ida e volta são o que o servidor necessita. Eu sei que ouvi falar de alguns servidores de E-mail rejeitando e-mails se eles não têm o e-mail adequado cabeçalhos no lugar.

Este é o código que eu usei, numa função, que funciona para mim enviar e-mail para o conteúdo de um *.ficheiro txt usando o meu computador local e um servidor de SMTP remoto (ZOHO como mostrado):

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()
 0
Author: ntk4, 2016-06-10 23:10:28