send email with a pandas dataframe as attachment - python

I have a pandas dataframe that I am looking to convert into an xlsx and attach to an email. I can send emails based on outlook(this is the only way I can do it). I am able to convert the dataframe into an xlsx and save it on my drive and afterwards send it as attachment but I am looking to attach it directly without having to save it on my drive. see bellow my function to send emails:
def email():
olMailItem = 0x0
obj = win32com.client.Dispatch("Outlook.Application")
newMail = obj.CreateItem(olMailItem)
newMail.Subject ="FRANCE SO"
newMail.Body =' '
newMail.To = "email adress"
newMail.Attachments.Add(attachment)
newMail.Send()
return
attachment is the dataframe that has been transformed into xlsx

Have you tried to play with the io module in Python 3? It allows you to use streams as file-like objects, so that APIs that expect a file can read from or save their content to the stream instead.
That works nicely, using a StringIO along with pandas.DataFrame.to_csv:
import io
def export_csv(df):
with io.StringIO() as buffer:
df.to_csv(buffer)
return buffer.getvalue()
That works, because to_csv expects a string (interpreted as a path) or a file handle, and StringIO can be used like a file handle. Unfortunately, pandas.DataFrame.to_excel works with either a string (interpreted as a path) or an ExcelWriter. In that case, we need to create the ExcelWriter ourselves, and wrap a BytesIO with it.
import io
import pandas as pd
def export_excel(df):
with io.BytesIO() as buffer:
with pd.ExcelWriter(buffer) as writer:
df.to_excel(writer)
return buffer.getvalue()
I am not familiar with the Outlook Python tools for sending emails, I use SMTP:
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import smtplib
SEND_FROM = 'noreply#example.com'
EXPORTERS = {'dataframe.csv': export_csv, 'dataframe.xlsx': export_excel}
def send_dataframe(send_to, subject, body, df):
multipart = MIMEMultipart()
multipart['From'] = SEND_FROM
multipart['To'] = send_to
multipart['Subject'] = subject
for filename in EXPORTERS:
attachment = MIMEApplication(EXPORTERS[filename](df))
attachment['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
multipart.attach(attachment)
multipart.attach(MIMEText(body, 'html'))
s = smtplib.SMTP('localhost')
s.sendmail(SEND_FROM, send_to, multipart.as_string())
s.quit()
I hope this helps, good luck!

Related

How do I store Python dataframe in-memory and send it as excel attachment to specific email addresses?

I read my data from mongo dB and create a report. I used to save the report as a .xlsx file on my local machine and manually send it using email. I would like to automate this process.
I found a way in Python 3 to save the file in-memory using Python's io.BytesIO() functionality. I tried a few things that I found on stack overflow but it didn't work for me. I would like to get some suggestions on how I can automate my work.
Following is what I have tried,
df = pd.dataframe(df) # has my reports dataframe
def export_excel(df):
with io.BytesIO() as buffer:
writer = pd.ExcelWriter(buffer)
df.to_excel(writer)
writer.save()
return buffer.getvalue()
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import smtplib
SEND_FROM = 'myemail#company.com'
EXPORTERS = {'dataframe.csv': export_csv, 'dataframe.xlsx': export_excel}
def send_dataframe(send_to, subject, body, df):
multipart = MIMEMultipart()
multipart['From'] = SEND_FROM
multipart['To'] = 'myemail#company.com'
multipart['Subject'] = subject
for filename in EXPORTERS:
attachment = MIMEApplication(EXPORTERS[filename](df))
attachment['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
multipart.attach(attachment)
multipart.attach(MIMEText(body, 'html'))
s = smtplib.SMTP('localhost')
s.sendmail(SEND_FROM, send_to, multipart.as_string())
s.quit()
I do not get an error but don't get an email with an excel attachment too.
I need to solve the same topic, so here is my code:
import io
import pandas as pd
from pandas import util
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import smtplib
df = util.testing.makeDataFrame()
# df.head()
output = io.BytesIO()
writer = pd.ExcelWriter(output, engine='xlsxwriter')
df.to_excel(writer, sheet_name='Sheet1')
writer.save()
output.seek(0)
send_from = 'my_gmail#gmail.com'
gmail_pwd = 'my_gmail_password'
send_to = 'my_gmail#gmail.com'
subject = 'Daily report'
body = "<p>Please find attached report,<br/>by me</p>"
report_name = "report.xlsx"
msg = MIMEMultipart()
msg['Subject'] = subject # add in the subject
msg['From'] = send_from
msg['To'] = send_to
msg.attach(MIMEText(body, 'html')) # add text contents
file = MIMEApplication(output.read(), name=report_name)
file['Content-Disposition'] = f'attachment; filename="{report_name}"'
msg.attach(file)
smtp = smtplib.SMTP('smtp.gmail.com:587')
smtp.ehlo()
smtp.starttls()
smtp.login(send_from, gmail_pwd)
smtp.sendmail(send_from, send_to, msg.as_string())
smtp.quit()
Here is a snippet of my code where I was taking a dataframe then convert it to StringIO() to send it as a spreadsheet format. Only difference is I am sending a csv file instead of xlsx. Hope it helps.
textStream = StringIO()
dataframe.to_csv(textStream,index=False)
message = MIMEMultipart()
message['Subject'] = "Given Subject"
message.attach(MIMEApplication(textStream.getvalue(), Name="Name of the
file"))
context = ssl.create_default_context()
with smtplib.SMTP(SMTP_host, SMTP_port) as server:
server.starttls(context=context)
server.login("mail user", "mail password")
server.sendmail("mail user", "receiver email", message.as_string().encode('utf-8'))
server.close()

How attach a dataframe as excel in email and send that attachment from python?

I have a file which is generated and stored inside a S3 bucket in AWS.
I want a sent this S3 file as excel as attachment in email send using python how can do it.
I was successfully was able sent email without the attachment.
My code
import os
import boto3
import pandas as pd
from sagemaker import get_execution_role
role = get_execution_role()
bucket='cotydata'
data_key = 'modeloutput'+'.csv'
data_location = 's3://{}/{}'.format(bucket, data_key)
output_key='UI_'+input_date
output_bucket='model-output-ui'
# Insert weather api key here
api_key= 'key'
#read modeloutput and prepare dataframe
modeloutput = pd.read_csv(data_location)
df = modeloutput.to_excel("Outpout.xls")
# import necessary packages
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import smtplib
# create message object instance
msg = MIMEMultipart()
password = "password"
msg['From'] = "riskradar#gmail.com"
msg['To'] = "abc#gmail.com"
msg['Subject'] = "Messgae"
filename = df
f = file(filename)
attachment = MIMEText(f.read(),'xls')
msg.attach(attachment)
# attach image to message body
server = smtplib.SMTP('smtp.gmail.com:587')
server.starttls()
# Login Credentials for sending the mail
server.login(msg['From'], password)
server.sendmail(msg['From'], msg['To'], msg.as_string())
Any help is appreciated
There is no need to generate a pandas dataframe out of the xls file.
msg = MIMEMultipart('mixed')
fp = open(data_location, 'rb')
record = MIMEBase('application', 'octet-stream')
record.set_payload(fp.read())
encode_base64(record)
record.add_header('Content-Disposition', 'attachment',
filename="what_ever_header_you_want.xls")
msg.attach(record)
And the rest is as you wrote it.. That should do the trick.
If you'd like to send a dataframe as an excel file, then you can use the to_excel() method and pass the file's absolute path to the block of code above.

Create a text file, and email it without saving it locally

I would like to create a text file in Python, write something to it, and then e-mail it out (put test.txt as an email attachment).
However, I cannot save this file locally. Does anyone know how to go about doing this?
As soon as I open the text file to write in, it is saved locally on my computer.
f = open("test.txt","w+")
I am using smtplib and MIMEMultipart to send the mail.
StringIO is the way to go...
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from io import StringIO
email = MIMEMultipart()
email['Subject'] = 'subject'
email['To'] = 'recipient#example.com'
email['From'] = 'sender#example.com'
# Add the attachment to the message
f = StringIO()
# write some content to 'f'
f.write("content for 'test.txt'")
f.seek(0)
msg = MIMEBase('application', "octet-stream")
msg.set_payload(f.read())
encoders.encode_base64(msg)
msg.add_header('Content-Disposition',
'attachment',
filename='test.txt')
email.attach(msg)
I found this post while figuring out how to do this with the newer EmailMessage, which was introduced in Python 3.6 (see https://docs.python.org/3/library/email.message.html). It's slightly less code:
from email.message import EmailMessage
from io import StringIO
from smtplib import SMTP
message = EmailMessage()
message['Subject'] = 'Subject'
message['From'] = 'from#example.com'
message['To'] = 'to#example.com'
f = StringIO()
f.name = 'attachment.txt'
f.write('contents of file')
f.seek(0)
message.add_attachment(f.read(), filename=f.name)
with SMTP('yourmailserver.com', 25) as server:
server.send_message(message)

MIME- sending an email with an HTML file as an attachment?

I am writing a script that creates multiple plotly interactive charts that are saved in HTML format. I want to write code that sends these HTML files out in an email as an attachment. I can't seem to find any documentation on this, only instructions on how to embed the HTML in an email which is not what I want. I just want to attach the file, just like I was attaching a JPG picture or PDF file.
Code I have so far, which just embeds the HTML:
import lxml.html
import smtplib
import sys
import os
page = 'report.html'
root = lxml.html.parse(page).getroot()
root.make_links_absolute()
content = lxml.html.tostring(root)
message = """From: <me#gmail.com>
To: <you#gmail.com>
MIME-Version: 1.0
Content-type: text/html
Subject: %s
%s""" %(page, content)
s = smtplib.SMTP('localhost')
s.sendmail('me#gmail.com', ['you#gmail.com'], message)
s.quit()
Appreciate the help. I'd hopefully like to find a dynamic way to send files that are in multiple formats, so that I don't have to worry about different functions to send different types of files.
In standard documentation see third example with module email
https://docs.python.org/3.6/library/email.examples.html#email-examples
# Import smtplib for the actual sending function
import smtplib
# And imghdr to find the types of our images
import imghdr
# Here are the email package modules we'll need
from email.message import EmailMessage
# Create the container email message.
msg = EmailMessage()
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'
# Open the files in binary mode. Use imghdr to figure out the
# MIME subtype for each specific image.
for file in pngfiles:
with open(file, 'rb') as fp:
img_data = fp.read()
msg.add_attachment(img_data, maintype='image',
subtype=imghdr.what(None, img_data))
# Send the email via our own SMTP server.
with smtplib.SMTP('localhost') as s:
s.send_message(msg)
EDIT: for other files you can get maintype, subtype
import mimetypes
filename = 'file.html'
ctype, encoding = mimetypes.guess_type(filename)
maintype, subtype = ctype.split("/", 1)
print(maintype, subtype)
# text html

How can I compress a folder and email the compressed file in Python?

I would like to compress a folder and all its sub-folders/files, and email the zip file as an attachment. What would be the best way to achieve this with Python?
You can use the zipfile module to compress the file using the zip standard, the email module to create the email with the attachment, and the smtplib module to send it - all using only the standard library.
Python - Batteries Included
If you don't feel like programming and would rather ask a question on stackoverflow.org instead, or (as suggested in the comments) left off the homework tag, well, here it is:
import smtplib
import zipfile
import tempfile
from email import encoders
from email.message import Message
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
def send_file_zipped(the_file, recipients, sender='you#you.com'):
zf = tempfile.TemporaryFile(prefix='mail', suffix='.zip')
zip = zipfile.ZipFile(zf, 'w')
zip.write(the_file)
zip.close()
zf.seek(0)
# Create the message
themsg = MIMEMultipart()
themsg['Subject'] = 'File %s' % the_file
themsg['To'] = ', '.join(recipients)
themsg['From'] = sender
themsg.preamble = 'I am not using a MIME-aware mail reader.\n'
msg = MIMEBase('application', 'zip')
msg.set_payload(zf.read())
encoders.encode_base64(msg)
msg.add_header('Content-Disposition', 'attachment',
filename=the_file + '.zip')
themsg.attach(msg)
themsg = themsg.as_string()
# send the message
smtp = smtplib.SMTP()
smtp.connect()
smtp.sendmail(sender, recipients, themsg)
smtp.close()
"""
# alternative to the above 4 lines if you're using gmail
server = smtplib.SMTP_SSL('smtp.gmail.com', 465)
server.login("username", "password")
server.sendmail(sender,recipients,themsg)
server.quit()
"""
With this function, you can just do:
send_file_zipped('result.txt', ['me#me.org'])
You're welcome.
Look at zipfile for compressing a folder and it's subfolders.
Look at smtplib for an email client.
You can use zipfile that ships with python, and here you can find an example of sending an email with attachments with the standard smtplib

Categories

Resources