Gmail displays both HTML and text and HTML parts of email - python

I am sending an email to a gmail account using Python. This is the code I'm using
msg = email.mime.multipart.MIMEMultipart()
msg['From'] = 'myemail#gmail.com'
msg['To'] = 'toemail#gmail.com'
msg['Subject'] = 'HTML test'
msg_html = email.mime.text.MIMEText('<html><head></head><body><b>This is HTML</b></body></html>', 'html')
msg_txt = email.mime.text.MIMEText('This is text','plain')
msg.attach(msg_html)
msg.attach(msg_txt)
#SNIP SMTP connection code
smtpConn.sendmail('myemail#gmail.com', 'toemail#gmail.com', msg.as_string())
When I view this email in gmail both the HTML and text version are shown like this:
This is HTML
This is text
It should be either displaying text or html, what causes this behavior.

The message is being sent as multipart/mixed (as this is the default) when it needs to be sent as multipart/alternative. mixed means that each part contains different content and all should be displayed, while alternative means that all parts have the same content in different formats and only one should be displayed.
msg = email.mime.multipart.MIMEMultipart("alternative")
Additionally, you should put the parts in increasing order of preference, i.e., text before HTML. The MUA (GMail in this case) will render the last part it knows how to display.
See the Wikipedia article on MIME for a good introduction to formatting MIME messages.

Related

imaplib: what factors decide maintype is "text" or "multipart"

I have been writing python code for small tool, wherein i am trying to fetch mails using python libraries imaplib and email.
code statement is something like below.
import imaplib
import email
mail = imaplib.IMAP4_SSL('imap.server')
mail.login('userid#mail.com', 'password')
result, data = mail.uid('search', None, "ALL")
latest_email_uid = data[0].split()[-1]
result, data = mail.uid('fetch', latest_email_uid, '(RFC822)')
raw_email = data[0][1]
email_message = email.message_from_string(raw_email)
maintype = email_message_instance.get_content_maintype()
I am executing the script from different host machines simultaneously.
Problem that I am facing is, while fetching the mail body, for same incoming email, on first host mac maintype is evaluated as "text" whereas for other host machine, its evaluated as "multipart" during script execution.
Would like to know how these values are determined at runtime and if I always want maintype to be "multipart", what standard layout should I follow while writing email in email body.
From comments:
raw_email for both cases has raw html code with multiple values. all most all html code is same except few differences. For maintype=multipart, Content-Type="multipart/alternative", boundary tag is present. For maintype=text, Content-Type="text/html", boundary field is not present
Well, that answers the question. get_content_maintype returns first part of the Content-Type, which is multipart for multipart/alternative and text for text/html.
multiplart/alternative means there are multiple alternative versions of the email. Usually, that is html + text. Emails are often sent that way, because then they can be read by any client (the text part), but will still contain HTML formatting which will be used in clients which support it.
Apparently one of the emails was sent with both html and text, whereas the other contains only html.

HTML not rendering correctly when sending email via python email

I am using my company's smtp server to send email using python's email module. When I do that my email html is not rendering correctly. This is what I got:
sender = 'abc#abc.com'
# Create message container - the correct MIME type is multipart/alternative.
msg = MIMEMultipart('multipart')
msg['Subject'] = "My Report"
msg['From'] = sender
msg['To'] = ", ".join(recipients)
# Create the body of the message using html in msg_body
h = unicode(msg_body).encode("utf-8", "ignore")
# Record the MIME type
part = MIMEText(h, 'html')
# Attach parts into message container.
msg.attach(part)
pngfiles = ['image.png']
for file in pngfiles:
# Open the files in binary mode.
fp = open(file, 'rb')
img = MIMEImage(fp.read())
fp.close()
msg.attach(img)
# Send the message via local SMTP server.
s = smtplib.SMTP('send.abc.com')
s.sendmail(msg.get("From"), recipients, msg.as_string())
s.quit()
What should I do differently ?
Just wanted to throw out there how easy it is to send emails with attachments using yagmail (full disclose: I'm the developer). My philosophy was to have to only once figure out how to deal with the MIME stuff, and then never look back.
You can use the following 3 lines to send your email:
import yagmail
yag = yagmail.SMTP(sender, your_password)
# assuming your undefined "msg_body" is just some message (string)
yag.send(recipients, 'My report', contents = [msg_body, '/local/path/to/image.png'])
You can do all kind of things with contents: if you have a list of stuff it will nicely combine it. For example, a list of filenames will make it so that it will attach all. Mix it with some message, and it will have a message.
Any string that is a valid file will be attached, other strings are just text.
It will nicely handle HTML code, images (inline), text and any other files. And yes, I'm quite proud of it.
I encourage you to read the github documentation to see other nice features, for example you do not have to have your password / username in the script (extra safety) by using the keyring. Set it up once, and you'll be happy....
Oh yea, to install, you can use:
pip install yagmail # python 2
pip3 install yagmail # python 3

Can't properly send HTML email with attached file via smtplib in python

I try to send email which contains text, html version of body and attached file.
I use standart python example with additional code:
img = MIMEImage(some_image_file)
img.add_header('Content-Disposition','attachment; filename="file.jpg"')
msg.attach(text_body)
msg.attach(html_body)
msg.attach(img)
Gmail show my email well, however yandex.com' email client shows only attached picture without html or text body of the letter.
If I change order like that:
msg.attach(img)
msg.attach(html_body)
Yandex shows only html body of my letter, and do not show attachment!
Is there any additional headers I need to add in order to show my email correctly (html/txt body AND attached file) in any email clients?
After some research of email headers sent from normal email client, I found solution:
body = MIMEMultipart('alternative')
msg = MIMEMultipart('mixed')
....
body.attach(text_body)
body.attach(html_body)
msg.attach(body)
msg.attach(img)

Adding multiple inline parts to an email

I am using Python 2.7 and trying to send an email using smtplib/MIMEMultipart. I want to send an email that contains multiple pieces e.g., a text message and an html message. I do NOT want them to be alternatives. I want to it to display the text message (inline) follow by the html (inline)
In the future, I would also like to include images. So, the message will contain text, html, and images all inline.
Here is what I have currently, which produces a text message and then the html as an attachment
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import smtplib
s = smtplib.SMTP('localhost')
#this part works
msg = MIMEMultipart('mixed')
msg['Subject'] = subject
msg['From'] = from
msg['To'] = to
html_str = """<html><head></head><body><p>Test</p></body></html>"""
#this shows up with "This is my text" inline and the html as an attachment
text = MIMEText("This is my text", 'plain')
html = MIMEText(html_str, 'html')
msg.attach(text)
msg.attach(html)
s.sendmail(fromEmail, toEmail, msg.as_string())
s.quit()
How do I add multiple inline pieces to an email?
Thank you for your help
I wasn't able to do this exactly as I wanted, but I found a work around.
I ended up just making the entire email an html email. For every piece that I needed to add, I would just add html code. For example, here is how I ended up adding an image
html_list.append('<img src="cid:image.png">')
img = MIMEImage(my_data)
img.add_header('Content-ID', '<image.png>')
msg.attach(img)
And if I wanted to add text, I could add it as a header (e.g. <h3>My Text</h3>) or as any other html element.

HTML email in Python

I'm trying to send an HTML email using smtplib. But I need the HTML content to have a table that is populated using values from a dictionary. I did look at the examples on the Python website. But it doesn't explain how to embed Python code within HTML. Any solutions/suggestions?
I also looked at this question. Can I just format it this way?
.format(dict_name)
From your link:
Here’s an example of how to create an HTML message with an alternative
plain text version: 2
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 link you wanted.
</p>
</body>
</html>
"""
and the sending section of it:
# 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()
Edit 2022: for new comers, please advise with python’s latest stable release docs.
What you need is a template engine. That is, you need a python library that reads a file written in both HTML and code, interprets the code written within the HTML file (e.g. code that retrieves values from a dictionary), and then produces an HTML file for you.
The python wiki seems to have some suggestions

Categories