Envoi d'e-mails HTML à l'aide de Python


260

Comment envoyer le contenu HTML dans un e-mail en utilisant Python? Je peux envoyer du texte simple.


Juste un gros gros avertissement. Si vous envoyez des e-mails non ASCII à l'aide de Python <3.0, envisagez d'utiliser l'e-mail dans Django . Il encapsule correctement les chaînes UTF-8 et est également beaucoup plus simple à utiliser. Vous avez été prévenu :-)
Anders Rune Jensen

1
Si vous souhaitez envoyer un HTML avec unicode, voir ici: stackoverflow.com/questions/36397827/…
guettli

Réponses:


419

Depuis la documentation de Python v2.7.14 - 18.1.11. email: Exemples :

Voici un exemple de création d'un message HTML avec une autre version en texte brut:

#! /usr/bin/python

import smtplib

from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

# me == my email address
# you == recipient's email address
me = "my@email.com"
you = "your@email.com"

# Create message container - the correct MIME type is multipart/alternative.
msg = MIMEMultipart('alternative')
msg['Subject'] = "Link"
msg['From'] = me
msg['To'] = you

# Create the body of the message (a plain-text and an HTML version).
text = "Hi!\nHow are you?\nHere is the link you wanted:\nhttp://www.python.org"
html = """\
<html>
  <head></head>
  <body>
    <p>Hi!<br>
       How are you?<br>
       Here is the <a href="http://www.python.org">link</a> you wanted.
    </p>
  </body>
</html>
"""

# Record the MIME types of both parts - text/plain and text/html.
part1 = MIMEText(text, 'plain')
part2 = MIMEText(html, 'html')

# Attach parts into message container.
# According to RFC 2046, the last part of a multipart message, in this case
# the HTML message, is best and preferred.
msg.attach(part1)
msg.attach(part2)

# Send the message via local SMTP server.
s = smtplib.SMTP('localhost')
# sendmail function takes 3 arguments: sender's address, recipient's address
# and message to send - here it is sent as one string.
s.sendmail(me, you, msg.as_string())
s.quit()

1
Est-il possible de joindre une troisième et une quatrième partie, toutes deux des pièces jointes (une ASCII, une binaire)? Comment ferait-on cela? Merci.
Hamish Grubijan

1
Salut, j'ai remarqué qu'à la fin tu es quitl' sobjet. Et si je veux envoyer plusieurs messages? Dois-je quitter chaque fois que j'envoie le message ou les envoyer tous (dans une boucle for) puis quitter une fois pour toutes?
xpanta

Assurez-vous de joindre le html en dernier, car la partie préférée (affichée) sera celle jointe en dernier. # According to RFC 2046, the last part of a multipart message, in this case # the HTML message, is best and preferred. J'aimerais avoir lu ceci il y a 2
heures

1
Attention: cela échoue si vous avez des caractères non ascii dans le texte.
guettli

2
Hmm, j'obtiens l'erreur pour msg.as_string (): l'objet de liste n'a pas d'attribut
encodé

61

Vous pourriez essayer d'utiliser mon module de messagerie .

from mailer import Mailer
from mailer import Message

message = Message(From="me@example.com",
                  To="you@example.com")
message.Subject = "An HTML Email"
message.Html = """<p>Hi!<br>
   How are you?<br>
   Here is the <a href="http://www.python.org">link</a> you wanted.</p>"""

sender = Mailer('smtp.example.com')
sender.send(message)

Le module Mailer est génial, mais il prétend fonctionner avec Gmail, mais ce n'est pas le cas et il n'y a pas de documentation.
MFB

1
@MFB - Avez-vous essayé le repo Bitbucket? bitbucket.org/ginstrom/mailer
Ryan Ginstrom

2
Pour gmail, il faut fournir use_tls=True, usr='email'et pwd='password'lors de l'initialisation Maileret cela fonctionnera.
ToonAlfrink

Je recommanderais d'ajouter à votre code la ligne suivante juste après le message. Ligne message.Body = """Some text to show when the client cannot show HTML emails"""
HTML

génial, mais comment ajouter les valeurs des variables au lien, je veux dire créer un lien comme celui-ci <a href=" python.org/somevalues"> lien </ a > Pour que je puisse accéder à ces valeurs à partir des routes vers lesquelles il se dirige. Merci
TaraGurung

49

Voici une implémentation Gmail de la réponse acceptée:

import smtplib

from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

# me == my email address
# you == recipient's email address
me = "my@email.com"
you = "your@email.com"

# Create message container - the correct MIME type is multipart/alternative.
msg = MIMEMultipart('alternative')
msg['Subject'] = "Link"
msg['From'] = me
msg['To'] = you

# Create the body of the message (a plain-text and an HTML version).
text = "Hi!\nHow are you?\nHere is the link you wanted:\nhttp://www.python.org"
html = """\
<html>
  <head></head>
  <body>
    <p>Hi!<br>
       How are you?<br>
       Here is the <a href="http://www.python.org">link</a> you wanted.
    </p>
  </body>
</html>
"""

# Record the MIME types of both parts - text/plain and text/html.
part1 = MIMEText(text, 'plain')
part2 = MIMEText(html, 'html')

# Attach parts into message container.
# According to RFC 2046, the last part of a multipart message, in this case
# the HTML message, is best and preferred.
msg.attach(part1)
msg.attach(part2)
# Send the message via local SMTP server.
mail = smtplib.SMTP('smtp.gmail.com', 587)

mail.ehlo()

mail.starttls()

mail.login('userName', 'password')
mail.sendmail(me, you, msg.as_string())
mail.quit()

2
Excellent code, cela fonctionne pour moi, si
j'active la

15
J'utilise un mot de passe spécifique à l'application Google avec python smtplib, j'ai fait l'affaire sans avoir à passer par une sécurité faible.
yoyo

2
pour tous ceux qui lisent les commentaires ci-dessus: Vous n'avez besoin d'un "mot de passe d'application" que si vous avez précédemment activé la vérification en 2 étapes dans votre compte Gmail.
Mugen

Existe-t-il un moyen d'ajouter quelque chose de manière dynamique dans la partie HTML du message?
magma

40

Voici un moyen simple d'envoyer un e-mail HTML, simplement en spécifiant l'en-tête Content-Type comme 'text / html':

import email.message
import smtplib

msg = email.message.Message()
msg['Subject'] = 'foo'
msg['From'] = 'sender@test.com'
msg['To'] = 'recipient@test.com'
msg.add_header('Content-Type','text/html')
msg.set_payload('Body of <b>message</b>')

# Send the message via local SMTP server.
s = smtplib.SMTP('localhost')
s.starttls()
s.login(email_login,
        email_passwd)
s.sendmail(msg['From'], [msg['To']], msg.as_string())
s.quit()

2
Ceci est une belle réponse simple, pratique pour les scripts rapides et sales, merci. BTW on peut se référer à la réponse acceptée pour un smtplib.SMTP()exemple simple , qui n'utilise pas tls. Je l'ai utilisé pour un script interne au travail où nous utilisons ssmtp et un mailhub local. En outre, cet exemple est manquant s.quit().
Mike S

1
"mailmerge_conf.smtp_server" n'est pas défini ... c'est du moins ce que dit Python 3.6 ...
ZEE

j'ai une erreur lors de l'utilisation de récepteurs basés sur une liste AttributeError: l'objet 'list' n'a pas d'attribut 'lstrip' une solution?
navotera

10

Voici un exemple de code. Ceci est inspiré du code trouvé sur le site Python Cookbook (impossible de trouver le lien exact)

def createhtmlmail (html, text, subject, fromEmail):
    """Create a mime-message that will render HTML in popular
    MUAs, text in better ones"""
    import MimeWriter
    import mimetools
    import cStringIO

    out = cStringIO.StringIO() # output buffer for our message 
    htmlin = cStringIO.StringIO(html)
    txtin = cStringIO.StringIO(text)

    writer = MimeWriter.MimeWriter(out)
    #
    # set up some basic headers... we put subject here
    # because smtplib.sendmail expects it to be in the
    # message body
    #
    writer.addheader("From", fromEmail)
    writer.addheader("Subject", subject)
    writer.addheader("MIME-Version", "1.0")
    #
    # start the multipart section of the message
    # multipart/alternative seems to work better
    # on some MUAs than multipart/mixed
    #
    writer.startmultipartbody("alternative")
    writer.flushheaders()
    #
    # the plain text section
    #
    subpart = writer.nextpart()
    subpart.addheader("Content-Transfer-Encoding", "quoted-printable")
    pout = subpart.startbody("text/plain", [("charset", 'us-ascii')])
    mimetools.encode(txtin, pout, 'quoted-printable')
    txtin.close()
    #
    # start the html subpart of the message
    #
    subpart = writer.nextpart()
    subpart.addheader("Content-Transfer-Encoding", "quoted-printable")
    #
    # returns us a file-ish object we can write to
    #
    pout = subpart.startbody("text/html", [("charset", 'us-ascii')])
    mimetools.encode(htmlin, pout, 'quoted-printable')
    htmlin.close()
    #
    # Now that we're done, close our writer and
    # return the message body
    #
    writer.lastpart()
    msg = out.getvalue()
    out.close()
    print msg
    return msg

if __name__=="__main__":
    import smtplib
    html = 'html version'
    text = 'TEST VERSION'
    subject = "BACKUP REPORT"
    message = createhtmlmail(html, text, subject, 'From Host <sender@host.com>')
    server = smtplib.SMTP("smtp_server_address","smtp_port")
    server.login('username', 'password')
    server.sendmail('sender@host.com', 'target@otherhost.com', message)
    server.quit()


5

pour python3, améliorez la réponse de @taltman :

  • utiliser email.message.EmailMessageau lieu de email.message.Messagepour créer un e-mail.
  • utiliser email.set_contentfunc, assigner un subtype='html'argument. au lieu de fonctions de bas niveau set_payloadet ajoutez un en-tête manuellement.
  • utilisez SMTP.send_messagefunc au lieu de SMTP.sendmailfunc pour envoyer des e-mails.
  • utilisez le withbloc pour fermer automatiquement la connexion.
from email.message import EmailMessage
from smtplib import SMTP

# construct email
email = EmailMessage()
email['Subject'] = 'foo'
email['From'] = 'sender@test.com'
email['To'] = 'recipient@test.com'
email.set_content('<font color="red">red color text</font>', subtype='html')

# Send the message via local SMTP server.
with smtplib.SMTP('localhost') as s:
    s.login('foo_user', 'bar_password')
    s.send_message(email)

4

Réellement, yagmail a adopté une approche un peu différente.

Ce sera par défaut du HTML, avec un repli automatique pour les lecteurs de courrier électronique incapables. Ce n'est plus le XVIIe siècle.

Bien sûr, il peut être annulé, mais voici:

import yagmail
yag = yagmail.SMTP("me@example.com", "mypassword")

html_msg = """<p>Hi!<br>
              How are you?<br>
              Here is the <a href="http://www.python.org">link</a> you wanted.</p>"""

yag.send("to@example.com", "the subject", html_msg)

Pour les instructions d'installation et de nombreuses autres fonctionnalités intéressantes, consultez le github .


3

Voici un exemple de travail pour envoyer du texte brut et des e-mails HTML à partir de Python en utilisant smtplibles options CC et BCC.

https://varunver.wordpress.com/2017/01/26/python-smtplib-send-plaintext-and-html-emails/

#!/usr/bin/env python
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

def send_mail(params, type_):
      email_subject = params['email_subject']
      email_from = "from_email@domain.com"
      email_to = params['email_to']
      email_cc = params.get('email_cc')
      email_bcc = params.get('email_bcc')
      email_body = params['email_body']

      msg = MIMEMultipart('alternative')
      msg['To'] = email_to
      msg['CC'] = email_cc
      msg['Subject'] = email_subject
      mt_html = MIMEText(email_body, type_)
      msg.attach(mt_html)

      server = smtplib.SMTP('YOUR_MAIL_SERVER.DOMAIN.COM')
      server.set_debuglevel(1)
      toaddrs = [email_to] + [email_cc] + [email_bcc]
      server.sendmail(email_from, toaddrs, msg.as_string())
      server.quit()

# Calling the mailer functions
params = {
    'email_to': 'to_email@domain.com',
    'email_cc': 'cc_email@domain.com',
    'email_bcc': 'bcc_email@domain.com',
    'email_subject': 'Test message from python library',
    'email_body': '<h1>Hello World</h1>'
}
for t in ['plain', 'html']:
    send_mail(params, t)

Pensez que cette réponse couvre tout. Great link
stingMantis

1

Voici ma réponse pour AWS utilisant boto3

    subject = "Hello"
    html = "<b>Hello Consumer</b>"

    client = boto3.client('ses', region_name='us-east-1', aws_access_key_id="your_key",
                      aws_secret_access_key="your_secret")

client.send_email(
    Source='ACME <do-not-reply@acme.com>',
    Destination={'ToAddresses': [email]},
    Message={
        'Subject': {'Data': subject},
        'Body': {
            'Html': {'Data': html}
        }
    }

0

Solution la plus simple pour envoyer des e-mails depuis un compte d'organisation dans Office 365:

from O365 import Message

html_template =     """ 
            <html>
            <head>
                <title></title>
            </head>
            <body>
                    {}
            </body>
            </html>
        """

final_html_data = html_template.format(df.to_html(index=False))

o365_auth = ('sender_username@company_email.com','Password')
m = Message(auth=o365_auth)
m.setRecipients('receiver_username@company_email.com')
m.setSubject('Weekly report')
m.setBodyHTML(final_html_data)
m.sendMessage()

ici df est une trame de données convertie en table html, qui est injectée dans html_template


La question ne mentionne rien sur l'utilisation d'Office ou d'un compte d'organisation. Bonne contribution mais pas très utile au demandeur
Mwikala Kangwa
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.