I am newbie to Python. I wanted to send html based email with company logo embedded on top left to the email body.
With the following code the email is absolutely working but not attaching the embedded image anymore. Not sure where i did mistake. Can anyone please help me out here.
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.MIMEImage import MIMEImage
msg = MIMEMultipart('alternative')
msg['Subject'] = "My text dated %s" % (today)
msg['From'] = sender
msg['To'] = receiver
html = """\
<html>
<head></head>
<body>
<img src="cid:image1" alt="Logo" style="width:250px;height:50px;"><br>
<p><h4 style="font-size:15px;">Some Text.</h4></p>
</body>
</html>
"""
# The image file is in the same directory as the script
fp = open('logo.png', 'rb')
msgImage = MIMEImage(fp.read())
fp.close()
msgImage.add_header('Content-ID', '<image1>')
msg.attach(msgImage)
part2 = MIMEText(html, 'html')
msg.attach(part2)
mailsrv = smtplib.SMTP('localhost')
mailsrv.sendmail(sender, receiver, msg.as_string())
mailsrv.quit()
I figured out the issue. Here is the updated code for your referance.
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.MIMEImage import MIMEImage
msg = MIMEMultipart('related')
msg['Subject'] = "My text dated %s" % (today)
msg['From'] = sender
msg['To'] = receiver
html = """\
<html>
<head></head>
<body>
<img src="cid:image1" alt="Logo" style="width:250px;height:50px;"><br>
<p><h4 style="font-size:15px;">Some Text.</h4></p>
</body>
</html>
"""
# Record the MIME types of text/html.
part2 = MIMEText(html, 'html')
# Attach parts into message container.
msg.attach(part2)
# This example assumes the image is in the current directory
fp = open('logo.png', 'rb')
msgImage = MIMEImage(fp.read())
fp.close()
# Define the image's ID as referenced above
msgImage.add_header('Content-ID', '<image1>')
msg.attach(msgImage)
# Send the message via local SMTP server.
mailsrv = smtplib.SMTP('localhost')
mailsrv.sendmail(sender, receiver, msg.as_string())
mailsrv.quit()
In case you want something simple:
from redmail import EmailSender
email = EmailSender(host="smtp.myhost.com", port=1)
email.send(
sender="me#example.com",
subject="Example email",
receivers=["you#example.com"],
html="""
<h1>Hi, take a look at this image:</h1>
{{ my_image }}
""",
body_images={"my_image": "path/to/image.png"}
)
The image is embedded to the HTML using Jinja and it creates the img tag automatically. You may also directly pass bytes, matplotlib.Figure or PIL.Image.Image to body_images as well if you prefer those formats.
In case you want more control on the image's properties, like change the width and height:
email.send(
sender="me#example.com",
subject="Example email",
receivers=["you#example.com"],
html="""
<h1>Hi, take a look at this image:</h1>
<img src="{{ my_image.src }}" width=500 height=350>
""",
body_images={"my_image": "path/to/image.png"}
)
Pip install it from PyPI:
pip install redmail
There is a lot the library can do: include attachments from various forms, prettify and embed tables to HTML body, load templates from disk, parametrize etc. It is also well tested (100 % test coverage) and documented. I'm the author of the library and sorry for the self-promotion. I think it's awesome and worth sharing.
Documentation: https://red-mail.readthedocs.io/en/latest/index.html
Source code: https://github.com/Miksus/red-mail
Related
Here is my code
import sys
import smtplib
import imghdr
from email.message import EmailMessage
from tkfilebrowser import askopenfilename
from email.mime.base import MIMEBase
from email.encoders import encode_base64
from email import encoders
def send():
msg = EmailMessage()
msg['Subject'] = body
msg['From'] = 'sender#gmail.com'
msg['To'] = 'receiver#gmail.com'
it worked well until i added this below to attach an image
with open('DSC_0020.jpg', 'rb') as f:
mime = MIMEBase('image', 'jpg', filename="DSC_0020.jpg")
mime.add_header('Content-Dispotion', 'attachment', filename="DSC_0020.jpg")
mime.add_header('X-Attachment-Id', '0')
mime.add_header('Content-ID', '<0>')
mime.set_payload(f.read())
encoders.encode_base64(mime)
mime.attach(mime)
msg.set_content('This is a plain text email')
msg.add_alternative("""\
<DOCTYPE html>
<html>
<body>
<h1 style="color:gray;"> This is an HTML Email! </h1>
<img src="body">
</body>
</html>
""", subtype='html')
with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp:
smtp.login('sender51#gmail.com', 'password')
smtp.send_message(msg)
here is the error i get can someone tell me what im doing wrong
File "C:\Users\my name\AppData\Local\Programs\Python\Python38\lib\email\message.py", line 210, in attach
raise TypeError("Attach is not valid on a message with a"
TypeError: Attach is not valid on a message with a non-multipart payload
The main part of the email message should be of type MIMEMUltipart and then text content should be of type MIMEText as follows:
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# and, of course, other imports
msg = MIMEMultipart('alternative') # to support alternatives
msg['Subject'] = body
msg['From'] = 'sender#gmail.com'
msg['To'] = 'receiver#gmail.com'
with open('DSC_0020.jpg', 'rb') as f:
mime = MIMEBase('image', 'jpg', filename="DSC_0020.jpg")
mime.add_header('Content-Disposition', 'attachment', filename="DSC_0020.jpg") # corrected header
mime.add_header('X-Attachment-Id', '0')
mime.add_header('Content-ID', '<0>')
mime.set_payload(f.read())
encode_base64(mime) # improved statement (you can now get rid of following import: from email import encoders)
msg.attach(mime) # corrected statement
# Attach the plain text as first alternative
msg.attach(MIMEText('This is a plain text email', 'plain'))
# Attach html text as second alternative
msg.attach(MIMEText("""\
<DOCTYPE html>
<html>
<body>
<h1 style="color:gray;"> This is an HTML Email! </h1>
<img src="body">
</body>
</html>
""", 'html'))
You had some errors in your file processing block, which I tried to correct. There may have been others I did not catch.
I'm currently using the following code to send an email (which contains a report) to users 3 times per day. I'd like to add in a graph to this email but can't seem to figure out how.
def HTML_Email(subject, to, html, files, filename):
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from os.path import basename
import email
import email.mime.application
# Create message container - the correct MIME type is
multipart/alternative.
msg = MIMEMultipart('mixed')
msg['Subject'] = subject
msg['From'] = "ChicagoGISScripts#mobilitie.com"
msg['To'] = ", ".join(to)
# Record the MIME types of both parts - text/plain and text/html
part2 = MIMEText(html, 'html')
# create PDF attachment
fp=open(files,'rb')
att = email.mime.application.MIMEApplication(fp.read(),_subtype="xlsx")
fp.close()
att.add_header('Content-Disposition','attachment',filename=filename)
# Attach parts into message container.
msg.attach(att)
msg.attach(part2)
# Send the message via local SMTP server.
user = 'ausername'
pwd = 'apassword'
s = smtplib.SMTP('smtp.office365.com',587)
s.ehlo()
s.starttls()
s.ehlo()
s.login(user,pwd)
s.sendmail(msg['From'], to, msg.as_string())
s.quit()
Typically I'll use something like this to go along with it, but I'm trying to include a .png that's stored locally on my computer. is not working to embed an image into the body of the email, what am I missing here?
html = """\
<html>
<head></head>
<body>
<p><font face ="Gotham, monospace">Some HTML,<br><br>
<img src="C:\\Users\\Me\\Desktop\\graphs.png"></img></font>
</p>
</body>
</html>
"""
Because you aren't hosting the image on a server, you won't be able to embed it in an email using a normal link. Try encoding the .png file into a data uri and setting that as the src
EDIT
Look at this other answer to see how to do this in python
EDIT 2
The resulting html should look like this
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />
Exactly what RemedialBear said. You have to host the email on a server and have an absolute src in your email body.
Instead of:
<img src="C:\\Users\\Me\\Desktop\\graphs.png">
You need:
<img src="http://www.somedomain.com/images/graphs.png" alt="Name' />
I've got a script that sends emails with html content in them.. works as expected...
I'm having trouble sending an attachment with the email.
The attachment is an html file stored in the active directory of the script... "test.html"
How do I attach the html file to the email? I've tried snippets from various other posts I've found relating to this, but each returned the same output of "no such file or directory".
code as follows:
import smtplib
import os
import email.encoders
import email.mime.text
import email.mime.base
import mimetools
import base64
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# me == Outgoing email address
# you == Recipient's email address
me = "secret"
you = "secret"
# Create message container - the correct MIME type is multipart/alternative.
msg = MIMEMultipart('alternative')
msg['Subject'] = "TEST"
msg['From'] = me
msg['To'] = you
emailMsg = email.MIMEMultipart.MIMEMultipart('alternative')
# Create the body of the message (a plain-text and an HTML version).
html = """\
<html>
<head></head>
<body>test</p>
</body>
</html>"""
# Record the MIME types of both parts - text/plain and text/html.
part1 = MIMEText(text, 'plain')
part2 = MIMEText(html, 'html')
filename = "C:/CHRIS/ServerStatus/Oceaneering_Server_Status.html"
f = file(filename)
attachment = MIMEText(f.read(), _subtype='html')
attachment.add_header('Content-Disposition', 'attachment', filename=filename)
# 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)
msg.attach(attachment)
# Send the message via local SMTP server.
mail = smtplib.SMTP('smtp.gmail.com', 587)
mail.ehlo()
# mail.login(username, password) to Outgoing email account
mail.login('secret', 'secret')
mail.sendmail(me, you, msg.as_string())
mail.quit()
i've updated my code in hopes to get this question back on topic... i've made a little progress with the help of Dirk and this link:
Attach a txt file in Python smtplib...
I've been able to physically send an attachment now, but the attachment is still coming through as a text type of file of sort and does not open as the original html file does.
So to reword my question... What is the corrective action for changing the MIME type of this code to correctly attach an .html file to an html based email?
The relative path and directory of my py script and the html file needed to be sent is as follows:
C:\CHRIS\ServerStatus\
This is the output i'm receiving with the code I have:
This is the way the html doc looks outside of the email script (The way it's supposed to look):
I would encourage you to use a library rather than deal with the -rather unpythonic- built-in mail modules, such as the highly recommended envelopes:
https://tomekwojcik.github.io/envelopes/index.html
install with:
pip install envelopes
Python code:
import os
from envelopes import Envelope
filename = "C:/CHRIS/ServerStatus/Oceaneering_Server_Status.html"
envelope = Envelope(
from_addr=(me),
to_addr=(you),
subject=u'Test',
text_body=u'Plain text version',
html_body=html
)
envelope.add_attachment(filename)
envelope.send('smtp.gmail.com', login='secret', password='secret', tls=True)
I've been playing around with the email module in python but I want to be able to know how to embed images which are included in the html.
So for example if the body is something like
<img src="../path/image.png"></img>
I would like to embed image.png into the email, and the src attribute should be replaced with content-id. Does anybody know how to do this?
Here is an example I found.
Recipe 473810: Send an HTML email with embedded image and plain text alternate:
HTML is the method of choice for those
wishing to send emails with rich text,
layout and graphics. Often it is
desirable to embed the graphics within
the message so recipients can display
the message directly, without further
downloads.
Some mail agents don't support HTML or
their users prefer to receive plain
text messages. Senders of HTML
messages should include a plain text
message as an alternate for these
users.
This recipe sends a short HTML message
with a single embedded image and an
alternate plain text message.
# Send an HTML email with an embedded image and a plain text message for
# email clients that don't want to display the HTML.
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.MIMEImage import MIMEImage
# Define these once; use them twice!
strFrom = 'from#example.com'
strTo = 'to#example.com'
# Create the root message and fill in the from, to, and subject headers
msgRoot = MIMEMultipart('related')
msgRoot['Subject'] = 'test message'
msgRoot['From'] = strFrom
msgRoot['To'] = strTo
msgRoot.preamble = 'This is a multi-part message in MIME format.'
# Encapsulate the plain and HTML versions of the message body in an
# 'alternative' part, so message agents can decide which they want to display.
msgAlternative = MIMEMultipart('alternative')
msgRoot.attach(msgAlternative)
msgText = MIMEText('This is the alternative plain text message.')
msgAlternative.attach(msgText)
# We reference the image in the IMG SRC attribute by the ID we give it below
msgText = MIMEText('<b>Some <i>HTML</i> text</b> and an image.<br><img src="cid:image1"><br>Nifty!', 'html')
msgAlternative.attach(msgText)
# This example assumes the image is in the current directory
fp = open('test.jpg', 'rb')
msgImage = MIMEImage(fp.read())
fp.close()
# Define the image's ID as referenced above
msgImage.add_header('Content-ID', '<image1>')
msgRoot.attach(msgImage)
# Send the email (this example assumes SMTP authentication is required)
import smtplib
smtp = smtplib.SMTP()
smtp.connect('smtp.example.com')
smtp.login('exampleuser', 'examplepass')
smtp.sendmail(strFrom, strTo, msgRoot.as_string())
smtp.quit()
For Python versions 3.4 and above.
The accepted answer is excellent, but only suitable for older Python versions (2.x and 3.3). I think it needs an update.
Here's how you can do it in newer Python versions (3.4 and above):
from email.message import EmailMessage
from email.utils import make_msgid
import mimetypes
msg = EmailMessage()
# generic email headers
msg['Subject'] = 'Hello there'
msg['From'] = 'ABCD <abcd#xyz.com>'
msg['To'] = 'PQRS <pqrs#xyz.com>'
# set the plain text body
msg.set_content('This is a plain text body.')
# now create a Content-ID for the image
image_cid = make_msgid(domain='xyz.com')
# if `domain` argument isn't provided, it will
# use your computer's name
# set an alternative html body
msg.add_alternative("""\
<html>
<body>
<p>This is an HTML body.<br>
It also has an image.
</p>
<img src="cid:{image_cid}">
</body>
</html>
""".format(image_cid=image_cid[1:-1]), subtype='html')
# image_cid looks like <long.random.number#xyz.com>
# to use it as the img src, we don't need `<` or `>`
# so we use [1:-1] to strip them off
# now open the image and attach it to the email
with open('path/to/image.jpg', 'rb') as img:
# know the Content-Type of the image
maintype, subtype = mimetypes.guess_type(img.name)[0].split('/')
# attach it
msg.get_payload()[1].add_related(img.read(),
maintype=maintype,
subtype=subtype,
cid=image_cid)
# the message is ready now
# you can write it to a file
# or send it using smtplib
I realized how painful some of the things are with SMTP and email libraries and I thought I have to do something with it. I made a library that makes embedding images to HTML way easier:
from redmail import EmailSender
email = EmailSender(host="<SMTP HOST>", port=0)
email.send(
sender="me#example.com",
receivers=["you#example.com"]
subject="An email with image",
html="""
<h1>Look at this:</h1>
{{ my_image }}
""",
body_images={
"my_image": "path/to/image.png"
}
)
Sorry for promotion but I think it's pretty awesome. You can supply the image as Matplotlib Figure, Pillow Image or just as bytes if your image is in those formats. It uses Jinja for templating.
If you need to control the size of the image, you can also do this:
email.send(
sender="me#example.com",
receivers=["you#example.com"]
subject="An email with image",
html="""
<h1>Look at this:</h1>
<img src="{{ my_image.src }}" width=200 height=300>
""",
body_images={
"my_image": "path/to/image.png"
}
)
You can just pip install it:
pip install redmail
It's (hopefully) all you need for email sending (has a lot more) and it is well tested. I also wrote extensive documentation: https://red-mail.readthedocs.io/en/latest/ and source code is found here.
There's actually a really good post with multiple solutions to this problem here. I was able to send an email with HTML, an Excel attachment, and an embedded images because of it.
Code working
att = MIMEImage(imgData)
att.add_header('Content-ID', f'<image{i}.{imgType}>')
att.add_header('X-Attachment-Id', f'image{i}.{imgType}')
att['Content-Disposition'] = f'inline; filename=image{i}.{imgType}'
msg.attach(att)
I am sending a mail by reading an excel file using pandas as dataframe. Then I am converting that dataframe using df.to_html with CSS style wrapped. Below is my code.
from email.mime.text import MIMEText
import email.message
import numpy as np
import pandas as pd
import email
import smtplib
import xlrd
filename = 'exl_sheet.xls'
df = pd.read_excel(filename)
html_string = '''
<html>
<head><title>HTML Pandas Dataframe with CSS</title></head>
<link rel="stylesheet" type="text/css" href="df_style.css"/>
<body>
{table}
</body>
</html>.
'''
df_html = html_string.format(table=df.to_html(classes='mystyle'))
sender = "sender#gmail.com"
receiver = "receiver#gmail.com"
password = 'xxxxxxxxx'
server = 'smtp.gmail.com:587'
msg = email.message.EmailMessage()
msg['Subject'] = 'Train Data'
msg['From'] = sender
msg['To'] = receiver
msg = MIMEText(df_html, 'html')
print(msg)
server = smtplib.SMTP(server)
server.ehlo()
server.starttls()
server.login(sender, password)
server.sendmail(sender, receiver, msg)
server.quit()
The output of print(msg) is :
enter image description here
However, the message I am receiving is as below :
enter image description here
That is I am not receiving the table with style. Please suggest.
First, I believe that only inline styles are supported on email -- at least that's all you can depend on across all email clients. For example, Gamil strips out all <head> and <body> tags. Try adding style parameters to your HTML tags, for example:
<span style="color: red;">I am red</span>
Even if you could have external style sheets, you specified: href="df_style.css", which is a relative URL. If you sent that email to me, would I have that stylesheet on my PC? You would need to make it an absolute URL, such as href="http://demo.com/df_syle.css". Keep this in mind when you reference other items in your email, such as images.