web-dev-qa-db-fra.com

Comment envoyer un email avec Python?

Ce code fonctionne et m'envoie un email très bien: 

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

Cependant, si j'essaye de l'envelopper dans une fonction comme celle-ci:

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

et appelez-le je reçois les erreurs suivantes: 

 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

Quelqu'un peut-il m'aider à comprendre pourquoi? 

135
cloud311

Je vous recommande d'utiliser les packages standard email et smtplib together pour envoyer un courrier électronique. Veuillez regarder l'exemple suivant (reproduit à partir de la documentation Python ). Notez que si vous suivez cette approche, la tâche "simple" est en effet simple et les tâches plus complexes (comme attacher des objets binaires ou envoyer des messages multipart en clair/HTML) sont accomplies très rapidement.

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

Pour envoyer des courriers électroniques à plusieurs destinataires, vous pouvez également suivre l'exemple de la documentation 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()

Comme vous pouvez le constater, l'en-tête To de l'objet MIMEText doit être une chaîne composée d'adresses électroniques séparées par des virgules. D'autre part, le deuxième argument de la fonction sendmail doit être une liste de chaînes (chaque chaîne est une adresse électronique).

Ainsi, si vous avez trois adresses électroniques: [email protected], [email protected] et [email protected], vous pouvez procéder comme suit (sections évidentes omises):

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

la partie "","".join(to) crée une seule chaîne de la liste, séparée par des virgules.

D'après vos questions, je suppose que vous n'avez pas parcouru le tutoriel sur Python - c'est un MUST si vous voulez aller n'importe où dans Python - la documentation est généralement excellente pour la bibliothèque standard.

164
Escualo

Eh bien, vous voulez une réponse moderne et à jour.

Voici ma réponse:

Quand j'ai besoin d'envoyer un mail en python, j'utilise l'API mailgun qui soulève de nombreux problèmes avec l'envoi de mails triés. Ils ont une application/api merveilleuse qui vous permet d'envoyer 10 000 emails par mois gratuitement.

Envoyer un email serait comme ceci:

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!"})

Vous pouvez également suivre des événements et bien d’autres encore, voir le guide de démarrage rapide .

J'espère que vous trouvez ça utile!

58
Dennis Decoene

Je voudrais vous aider à envoyer des courriels en conseillant le paquet yagmail (je suis le responsable, désolé pour la publicité, mais je pense que cela peut vraiment aider!). 

Le code entier pour vous serait:

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

Notez que je fournis des valeurs par défaut pour tous les arguments. Par exemple, si vous souhaitez vous envoyer à vous-même, vous pouvez omettre TO, si vous ne voulez pas de sujet, vous pouvez également le faire.

En outre, l'objectif est également de rendre très facile l'attachement de code html ou d'images (et d'autres fichiers).

Où vous mettez le contenu, vous pouvez faire quelque chose comme:

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']

Wow, comme il est facile d'envoyer des pièces jointes! Cela prendrait comme 20 lignes sans yagmail;)

De plus, si vous le configurez une fois, vous ne devrez plus jamais entrer le mot de passe (et le stocker en toute sécurité). Dans votre cas, vous pouvez faire quelque chose comme:

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

ce qui est beaucoup plus concis!

Je vous invite à jeter un œil à github ou à l’installer directement avec pip install yagmail.

34
PascalVKooten

Il y a un problème d'indentation. Le code ci-dessous fonctionnera:

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

14
Zeeshan

Essaye ça:

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

Cela fonctionne pour smtp.gmail.com

8
Theoremiser

Tout en mettant votre code dans la fonction (ce qui est ok), vous avez également indenté les lignes de la chaîne de message brute. Toutefois, les espaces en début de ligne impliquent un repliement (concaténation) des lignes d’en-tête, comme décrit aux sections 2.2.3 et 3.2.3 de RFC 2822 - Format du message Internet :

Chaque champ d'en-tête est logiquement une seule ligne de caractères comprenant le nom du champ, le côlon et le corps du champ. Pour plus de commodité cependant, et pour traiter des limites de 998/78 caractères par ligne, la partie corps d'un champ d'en-tête peut être divisée en un multiple représentation en ligne; c'est ce qu'on appelle "plier".

Dans la forme de fonction de votre appel sendmail, toutes les lignes commencent par un espace blanc et sont donc "dépliées" (concaténées) et vous essayez d'envoyer

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

À part ce que notre esprit suggère, smtplib ne comprendra plus les en-têtes To: et Subject:, car ces noms ne sont reconnus qu'au début d'une ligne. À la place, smtplib assumera une adresse électronique d'expéditeur très longue: 

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

Cela ne fonctionnera pas et ainsi votre exception se présentera.

La solution est simple: conservez la chaîne message telle qu'elle était auparavant. Cela peut être fait par une fonction (comme suggéré par Zeeshan) ou tout de suite dans le code source:

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

Maintenant, le déroulement ne se produit pas et vous envoyez

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

This message was sent with Python's smtplib.

qui est ce qui fonctionne et ce qui a été fait par votre ancien code.

Notez que je conservais également la ligne vide entre les en-têtes et le corps pour prendre en charge la section 3.5 de le RFC (qui est requis) et mettre l’option include en dehors de la fonction conformément au guide de style Python PEP-0008 ( qui est facultatif).

4
flaschbier

C'est probablement mettre des onglets dans votre message. Imprimez le message avant de le transmettre à sendMail.

3
Nick ODell

Assurez-vous que vous avez accordé la permission à l'expéditeur et au destinataire d'envoyer un courrier électronique et de recevoir un courrier électronique de sources inconnues (sources externes) dans le compte de messagerie.

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
Devanand Sharma

Voici un exemple sur Python 3.x, beaucoup plus simple 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.')

appelez cette fonction:

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

ci-dessous peut uniquement pour l'utilisateur chinois:

Si vous utilisez 126/163, vous devez définir "授权", comme ci-dessous:

 enter image description here

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

1
Belter

En ce qui concerne votre code, il ne semble pas y avoir de problème fondamental dans ce code, si ce n’est que, la manière dont vous appelez cette fonction n’est pas claire. Tout ce que je peux penser, c'est que lorsque votre serveur ne répond pas, vous obtiendrez cette erreur SMTPServerDisconnected. Si vous recherchez la fonction getreply () dans smtplib (extrait ci-dessous), vous aurez une idée.

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.
    """

consultez un exemple sur https://github.com/rreddy80/sendEmails/blob/master/sendEmailAttachments.py qui utilise également un appel de fonction pour envoyer un courrier électronique, si c'est ce que vous essayez de faire (approche DRY). ).

0
rreddy

Je pensais que je mettrais mes deux bits ici puisque je viens de comprendre comment cela fonctionne. 

Il semble que vous n’ayez pas le port spécifié dans vos paramètres de connexion SERVER, cela m’a un peu affecté lorsque j’essayais de me connecter à mon serveur SMTP qui n’utilise pas le port par défaut: 25. 

Selon les documents smtplib.SMTP, votre demande/réponse ehlo ou helo devrait automatiquement être prise en charge, vous ne devriez donc pas avoir à vous en préoccuper (mais cela pourrait être quelque chose à confirmer si tout le reste échoue).

Vous pouvez également vous demander si vous avez autorisé les connexions SMTP sur votre serveur SMTP lui-même. Pour certains sites comme GMAIL et ZOHO, vous devez réellement activer les connexions IMAP dans le compte de messagerie. Votre serveur de messagerie n'autorisera peut-être pas les connexions SMTP qui ne proviennent pas de «localhost»? Quelque chose à examiner. 

La dernière chose à faire est d’essayer d’initier la connexion sur TLS. La plupart des serveurs nécessitent maintenant ce type d'authentification.

Vous verrez que j'ai coincé deux champs TO dans mon courrier électronique. Les éléments du dictionnaire msg ['TO'] et msg ['FROM'] du dictionnaire permettent d'afficher les informations correctes dans les en-têtes de l'e-mail lui-même, que l'on voit à la réception des e-mails dans les champs À/De Il se peut même que vous puissiez ajouter un champ Répondre à ici Les champs À et De sont eux-mêmes ce que le serveur requiert. Je sais que certains serveurs de messagerie refusent des courriers électroniques s'ils ne disposent pas des en-têtes appropriés. 

C'est le code que j'ai utilisé, dans une fonction, qui fonctionne pour moi afin d'envoyer par courrier électronique le contenu d'un fichier * .txt à l'aide de mon ordinateur local et d'un serveur SMTP distant (ZOHO, comme indiqué): 

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
ntk4