Comment envoyer un e-mail avec Python?
Ce code fonctionne et m'envoie un email très bien:
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()
Cependant, si j'essaie de l'insérer dans une fonction comme ceci:
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, j'obtiens 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:/Python31sendmail.py", line 13, in sendMail
server.sendmail(FROM, TO, message)
File "C:Python31libsmtplib.py", line 720, in sendmail
self.rset()
File "C:Python31libsmtplib.py", line 444, in rset
return self.docmd("rset")
File "C:Python31libsmtplib.py", line 368, in docmd
return self.getreply()
File "C:Python31libsmtplib.py", line 345, in getreply
raise SMTPServerDisconnected("Connection unexpectedly closed")
smtplib.SMTPServerDisconnected: Connection unexpectedly closed
Quelqu'un Peut-il m'aider à comprendre pourquoi?
10 réponses
Je vous recommande d'utiliser les packages standard email
et smtplib
ensemble pour envoyer l'email. 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 plain/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 e-mails vers plusieurs destinations, vous pouvez également suivez l'exemple dans 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 voir, l'en-tête To
dans le MIMEText
objet doit être une chaîne composée d'adresses e-mail 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 e-mail).
Donc, si vous avez trois adresses e-mail: person1@example.com
, person2@example.com
, et person3@example.com
, vous pouvez le faire comme suit (évident sections omis):
to = ["person1@example.com", "person2@example.com", "person3@example.com"]
msg['To'] = ",".join(to)
s.sendmail(me, to, msg.as_string())
La partie "","".join(to)
fait un seule chaîne de la liste, séparée par des virgules.
D'après vos questions, je comprends que vous n'avez pas passé par le tutoriel Python - C'est un MUST si vous voulez aller n'importe où en Python - la documentation est principalement excellente pour la bibliothèque standard.
Eh bien, vous voulez avoir une réponse à jour et moderne.
Voici ma réponse:
Quand j'ai besoin de poster en python, j'utilise l'API mailgun qui contient beaucoup de maux de tête avec l'envoi de mails triés. Ils ont une application/api merveilleuse qui vous permet d'envoyer des e-mails 10,000 par mois gratuitement.
Envoyer un e-mail 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": ["bar@example.com", "YOU@YOUR_DOMAIN_NAME"],
"subject": "Hello",
"text": "Testing some Mailgun awesomness!"})
Vous pouvez également suivre les événements et bien plus encore, voir le guide de démarrage rapide.
J'espère que vous trouver cela utile!
J'aimerais vous aider à envoyer des e-mails en conseillant le paquet yagmail (je suis le mainteneur, 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 voulez vous envoyer, vous pouvez omettre TO
, Si vous ne voulez pas de sujet, vous pouvez également l'omettre.
En outre, le but est aussi de rendre vraiment facile de joindre du code html ou des images (et d'autres fichier).
Où vous mettez du 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 n'aurez 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)
Qui est beaucoup plus concis!
Je vous invite à jeter un oeil à la github, ou l'installer directement avec pip install yagmail
.
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()
Essayez ceci:
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
En indentant votre code dans la fonction (ce qui est ok), vous avez également indenté les lignes de la chaîne de message brute. Mais l'espace blanc principal implique le pliage (concaténation) des lignes d'en-tête, comme décrit dans les sections 2.2.3 et 3.2.3 de RFC 2822 - Format de Message Internet :
Chaque champ d'en-tête est logiquement une seule ligne de caractères comprenant le nom du champ, les deux-points et le corps du champ. Pour plus de commodité cependant, et pour traiter le caractère 998/78 limitations par ligne, le domaine partie du corps d'un champ d'en-tête peut être divisé en plusieurs représentation de ligne; Ceci est appelé "pliage".
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: monty@python.com To: jon@mycompany.com Subject: Hello! This message was sent with Python's smtplib.
Autre que notre esprit suggère, {[5] } ne comprendra plus les en-têtes To:
et Subject:
, car ces noms ne sont reconnus qu'au début d'une ligne. Au lieu de cela, smtplib
supposera une adresse e-mail d'expéditeur très longue:
monty@python.com To: jon@mycompany.com Subject: Hello! This message was sent with Python's smtplib.
Cela ne fonctionnera pas et ainsi vient votre Exception.
La solution est simple: il suffit de conserver la chaîne message
comme avant. Cela peut être fait par une fonction (comme Zeeshan l'a suggéré) 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: monty@python.com
To: jon@mycompany.com
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 aussi le vide ligne entre les en-têtes et le corps pour accueillir la section 3.5 de la RFC (qui est requise) et mettre l'inclusion en dehors de la fonction selon le guide de style Python PEP-0008 (qui est facultatif).
Il met probablement des onglets dans votre message. Imprimez le message avant de le transmettre à sendMail.
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='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.')
Appelez cette fonction:
send_mail(to_email=['12345@qq.com', '12345@126.com'],
subject='hello', message='Your analysis has done!')
Ci-dessous peut seulement pour L'utilisateur Chinois:
Si vous utilisez 126/163, 网易邮箱, vous devez définir "客户端授权密码", comme ci-dessous:
Réf: https://stackoverflow.com/a/41470149/2803344 https://docs.python.org/3/library/email.examples.html#email-examples
En ce qui concerne votre code, il ne semble y avoir rien de fondamentalement faux sauf que, on ne sait pas comment vous appelez réellement cette fonction. Tout ce que je peux penser 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.
"""
Vérifiez un exemple à https://github.com/rreddy80/sendEmails/blob/master/sendEmailAttachments.py cela utilise également un appel de fonction pour envoyer un e-mail, si c'est ce que vous essayez de faire (approche sèche).
J'ai pensé 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é sur les paramètres de connexion de votre serveur, 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 le smtplib.Docs SMTP, votre demande/réponse ehlo ou helo devrait automatiquement être prise en charge, donc vous ne devriez pas avoir à vous inquiéter à ce sujet (mais pourrait être quelque chose à confirmer si tout le reste échoue).
Une autre chose à vous demander Est avez-vous autorisé les connexions SMTP sur votre serveur SMTP lui-même? Pour certains sites comme GMAIL et ZOHO, vous devez réellement entrer et activer les connexions IMAP dans le compte de messagerie. Votre serveur de messagerie peut ne pas autoriser les connexions SMTP qui ne proviennent pas de' localhost ' peut-être? Quelque chose à considérer.
La dernière chose est que vous pourriez vouloir essayer et initier la connexion sur TLS. La plupart des serveurs nécessitent maintenant ce type de Authentication.
Vous verrez que j'ai bloqué deux champs dans mon e-mail. Les éléments du dictionnaire msg['TO'] et msg['FROM'] MSG permettent aux informations correctes d'apparaître dans les en-têtes de l'e-mail lui-même, que l'on voit à la réception de l'e-mail dans les champs To/From (vous pourriez même être en mesure d'ajouter une réponse au champ ici. Les champs vers et depuis sont eux-mêmes ce dont le serveur a besoin. Je sais que j'ai entendu parler de certains serveurs de messagerie rejetant les e mails s'ils n'ont pas le bon e mail en-têtes en place.
C'est le code que j'ai utilisé, dans une fonction, qui fonctionne pour moi d'envoyer le contenu d'un *.fichier txt utilisant mon ordinateur local et 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 = '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()