Sending HTML Email From Python

Since I posted about using Python to send HTML email, things have changed in Python’s standard library.  While that method still works, it’s better to use the new email package.


For the purposes of this post, I am assuming the following:

  • Python 2.x (I have Python 2.6 installed on my development server)
  • Linux
  • Lynx is installed
import os
def html2text( html ):
    fname = os.tmpnam()
    fp = open(fname, 'w')
    fp.write(html)
    fp.close()
    fp = os.popen('cat %s | lynx -dump -force_html -stdin' % fname)
    buff = fp.read()
    fp.close()
    return buff

The html2text function takes a given HTML document (either local or a you can use an external URL) and converts it to a plain text document. It uses os.popen to do this, by piping the document through Lynx and using that program as a filter to do the conversion.

import email
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
def createhtmlmail (html, text, subject):
    msg = MIMEMultipart('alternative')
    part1 = MIMEText(text, 'plain')
    part2 = MIMEText(html, 'html')
    msg.attach(part1)
    msg.attach(part2)
    return msg

createhtmlmail does the heavy lifting of making the MIME attachments and adding them to the email message. You may notice that I kept the function signature the same as that used in my earlier post on sending HTML email from Python; this way, I was able to use these new functions as a drop-in replacement to what I had in my production environment.

import email
import os
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
def compose_mail(html, subject, to_mail, from_mail):
     text = html2text(html)
     email_data = createhtmlmail(html, text, subject)
     email_data['Subject'] = subject
     email_data['From'] = reply_mail
     email_data['To'] = to_mail
     email_data["Date"] = email.Utils.formatdate(localtime=True)
     return email_data

Here will build the actual email message, and set the Subject, From, and To headers. While Subject is technically optional, some MTA’s and MUA’s complain about mails with Subject headers; I’ve found it easier to always set it.

import smtplib
def send_mail(from_mail, to_mail, email_data):
     server = smtplib.SMTP("localhost")
     server.sendmail(from_mail, to_mail, email_data)
     server.quit()

I have a local sendmail running on my Linux machines, so I can use localhost as my SMTP server; you may need to change it according to your actual environment.

With those functions in place, we can now send a MIME multipart email, with the following:

email_data = compose_mail(body, subject, to_mail, from_mail)
send_mail(from_mail, to_mail, email_data.as_string())

The only part of this example code that relies on a non-typical piece of software is the html2text function. I use Lynx as the filtering process, but you can replace that with something more appropriate for your platform and environment. You can learn about the open source nosql that will fit your chosen platform.

You can download the full file here.


Posted

in

by

Comments

4 responses to “Sending HTML Email From Python”

  1. anonymous Avatar
    anonymous

    You should also always add the date header. It is required accoring to RFC 822 and messages without it might have an increased spam rating. I suggest the following solution for doing so:

    msg[“Date”] = email.Utils.formatdate(localtime=True)

    1. admin Avatar

      I was relying on sendmail to add that header, but it’s still a good point to add it ourselves. A different MTA may not behave the same way.

      I’ve updated the body of the post accordingly, and also updated the attached .py file.

  2. anonymous2 Avatar
    anonymous2

    Have you tested mailer?

    https://bitbucket.org/ginstrom/mailer

    Whats your opinion of it?

    1. admin Avatar

      It looks like a good option, but seems more heavy-weight than what I was looking to do.

Leave a Reply

Your email address will not be published. Required fields are marked *