I have a pandas dataframe which I want attach as xls in an automated email triggered from python. How can this be done
I am able to send the email without attachment successfully but not with the attachment.
My code
import os
import pandas as pd
#read and prepare dataframe
data= pd.read_csv("C:/Users/Bike.csv")
data['Error'] = data['Act'] - data['Pred']
df = data.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'] = "xyz#gmail.com"
msg['To'] = "abc#gmail.com"
msg['Subject'] = "Messgae"
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())
As pointed out in comment you are not attaching the file, so it would not be sent.
msg.attach(MIMEText(body, 'plain'))
filename = "Your file name.xlsx"
attachment = open("/path/to/file/Your file name.xlsx","rb")
part = MIMEBase('application', 'octet-stream')
part.set_payload((attachment).read())
encoders.encode_base64(part)
part.add_header('Content-Disposition',"attachment; filename=%s" % filename)
msg.attach(part)
text = msg.as_string()
smtp0bj.sendmail(msg['From'], msg['To'], text)
Hope it helps
Related
I currently have a script that manipulates a .csv file using pandas. I'm trying to send a MIMEMultipart email with the latest .csv version of the file that has been amended but for some reason, the email recipients keep receiving an older unaltered version of the .csv that I trying to send. I'm trying to make sense of it in my head because the old version of the .csv file is written over before it is sent but the original version of the .csv is sent to the recipients.
Maybe I need to specify a path for smtplib to get the file as opposed to just giving the name of the file. Is there a way to do that or is there another way around my problem? I've already tried to change the name to something else in order for smtplib to be able to differentiate between the old .csv and the new one.
This doesn't work though as the file is placed in the directory but my script says that the new file doesn't exist
This is my current code:
email_user = 'Bot#gmail.com'
email_password = 'Business101'
email_send = ('myemail#gmail.com', 'myfriendsemail#gmail.com')
subject = 'TOP 5 CONTRACTS'
msg = MIMEMultipart()
msg['From'] = email_user
msg['To'] = ",".join(email_send)
msg['Subject'] = subject
body = 'These are the latest contracts for this week!'
msg.attach(MIMEText(body,'plain'))
filename='CC.csv'
attachment =open(filename,'rb')
part = MIMEBase('application','octet-stream')
part.set_payload((attachment).read())
encoders.encode_base64(part)
part.add_header('Content-Disposition',"attachment; filename= "+filename)
msg.attach(part)
text = msg.as_string()
server = smtplib.SMTP('smtp.gmail.com',587)
server.starttls()
server.login(email_user,email_password)
server.sendmail(email_user,email_send,text)
server.quit()
print("Emailed Recipients")
Might be worth mentioning that this process is an automated one so the script is being run from a Unix Executable file on my mac.
If you can assist, I'd really appreciate it!
This has been the best way to do it, thanks
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from smtplib import SMTP
import smtplib
import sys
import pandas as pd
df_test = pd.read_csv('/Users/emmanuelafoke/Documents/Selenium/CC.csv')
email_user = 'myemailaddress#gmail.com'
email_password = 'mypassword'
recipients = ['theiremailaddress#gmail.com']
emaillist = [elem.strip().split(',') for elem in recipients]
msg = MIMEMultipart()
msg['Subject'] = 'SUBJECT'
msg['From'] = 'myemailaddress#gmail.com'
html = """\
<html>
<head></head>
<body>
{0}
</body>
</html>
""".format(df_test.to_html())
part1 = MIMEText(html, 'html')
msg.attach(part1)
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login(email_user,email_password)
server.sendmail(msg['From'], emaillist , msg.as_string())
Thanks for all of your help!
You could actually do this by using the following libraries: email.mime.application, MIMEApplication, email.mime.multipart and email.mime.text. Now, I don't know which client you are using and you might have to do some adjustments to this.
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import smtplib
SEND_FROM = 'myaddress#client.com' ## Your email address
TOSEND = {'dataframe.csv': export_csv, 'dataframe.xlsx': export_my_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(TOSEND[filename](df)) ### Here is where the df is attached
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()
In send_dataframe() you'll have to put the df you want to attach. This is where TOSEND come in. As you see, there is a function called export_my_excel. You can create it as
import io
import pandas as pd
def export_my_excel(df):
with io.BytesIO() as buffer:
writer = pd.ExcelWriter(buffer)
df.to_excel(writer)
writer.save()
return buffer.getvalue()
I'm trying to send a csv file to my email address using smtplib library. When I run the script below, it sends the email without any issues. However, when I open that email I could see that there is no attachment in there.
I've tried with:
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
attachment = "outputfile.csv"
msg = MIMEMultipart()
msg['Subject'] = "Email a csv file"
msg['Body'] = "find the attachment"
msg['From'] = "someemail#gmail.com"
msg['To'] = "anotheremail#gmail.com"
part = MIMEBase('application', "octet-stream")
part.set_payload(open(attachment, "rb").read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment', filename=attachment)
msg.attach(part)
msg = f"Subject: {msg['Subject']}\n\n{msg['Body']}"
with smtplib.SMTP('smtp.gmail.com',587) as server:
server.ehlo()
server.starttls()
server.ehlo()
server.login('someemail#gmail.com','ivfpklyudzdlefhr')
server.sendmail(
'someemail#gmail.com',
'anotheremail#gmail.com',
msg
)
What possible change should I bring about to send a csv file to my email?
The code needs two changes
msg = f"Subject: {msg['Subject']}\n\n{msg['Body']}" is overwriting the message object msg with a string. It is not required and may be deleted.
To send a message object (as opposed to a string), use SMTP.send_message.
This code ought to work:
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
attachment = "outputfile.csv"
msg = MIMEMultipart()
msg['Subject'] = "Email a csv file"
msg['Body'] = "find the attachment"
msg['From'] = "someemail#gmail.com"
msg['To'] = "anotheremail#gmail.com"
part = MIMEBase('application', "octet-stream")
part.set_payload(open(attachment, "rb").read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment', filename=attachment)
msg.attach(part)
with smtplib.SMTP('smtp.gmail.com',587) as server:
server.ehlo()
server.starttls()
server.ehlo()
server.login('someemail#gmail.com','ivfpklyudzdlefhr')
server.send_message(msg)
I have written a script that sends individual emails to all contacts in an Excel table. The table looks like this:
Names Emails PDF
Name1 Email1#email.com PDF1.pdf
Name2 Email2#email.com PDF2.pdf
The code I have so far is as follows:
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
from string import Template
import pandas as pd
e = pd.read_excel("Contacts.xlsx")
emails = e['Email'].values
PDF = e['PDF'].values
print(emails, PDF)
server = smtplib.SMTP(host='smtp.outlook.com', port=587)
server.starttls()
server.login('sender_email','sender_password')
msg = ("""
Hi there
Test message
Thankyou
""")
subject = "Send emails with attachment"
body = "Subject: {}\n\n{}".format(subject,msg)
for emails in emails:
server.sendmail('sender_email',emails,body)
print("Emails sent successfully")
server.quit()
Question: How can I look up the correct attachment (column 3) for each e-mail address (column 2) and attach it to the email using Python?
I made a little changes , and make you sure the pdf field has the pdf path
Edit:
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
from string import Template
import pandas as pd
#e = pd.read_csv("Contacts.csv")
e = pd.read_excel("Contacts.xlsx")
server = smtplib.SMTP(host='smtp.outlook.com', port=587)
server.starttls()
server.login('yourmail#mail.com','yourpass')
body = ("""
Hi there
Test message
Thankyou
""")
subject = "Send emails with attachment"
fromaddr='yourmail#mail.com'
#body = "Subject: {}\n\n{}".format(subject,msg)
#Emails,PDF
for index, row in e.iterrows():
print (row["Emails"]+row["PDF"])
msg = MIMEMultipart()
msg['From'] = fromaddr
msg['Subject'] = subject
msg.attach(MIMEText(body, 'plain'))
filename = row["PDF"]
toaddr = row["Emails"]
attachment = open(row["PDF"], "rb")
part = MIMEBase('application', 'octet-stream')
part.set_payload((attachment).read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', "attachment; filename= %s" % filename)
msg.attach(part)
text = msg.as_string()
server.sendmail(fromaddr, toaddr, text)
#server.sendmail('sender_email',emails,body)
print("Emails sent successfully")
server.quit()
I have the following code to send a html file SeleniumTestReport_part1.html in an email in Python.
I want to send more than 1 file in the email. How do can i do this?
The files I want to send are:
SeleniumTestReport_part1.html
SeleniumTestReport_part2.html
SeleniumTestReport_part3.html
My code to send 1 file is:
def send_selenium_report():
fileToSend = r"G:\test_runners\selenium_regression_test_5_1_1\TestReport\SeleniumTestReport_part1.html"
with open(fileToSend, "rt") as f:
text = f.read()
msg = MIMEText(text, "html")
msg['Subject'] = "Selenium ClearCore_Regression_Test_Report_Result"
msg['to'] = "4_server_dev#company.com"
msg['From'] = "system#company.com"
s = smtplib.SMTP()
s.connect(host=SMTP_SERVER)
s.sendmail(msg['From'], msg['To'], msg.as_string())
s.close()
Thanks,
Riaz
If you want to attach files to the email you can use just iterate over files and attach them to the message. You also may want to add some text to the body.
Here is the code:
import smtplib
import os
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
def send_selenium_report():
dir_path = "G:/test_runners/selenium_regression_test_5_1_1/TestReport"
files = ["SeleniumTestReport_part1.html", "SeleniumTestReport_part2.html", "SeleniumTestReport_part3.html"]
msg = MIMEMultipart()
msg['To'] = "4_server_dev#company.com"
msg['From'] = "system#company.com"
msg['Subject'] = "Selenium ClearCore_Regression_Test_Report_Result"
body = MIMEText('Test results attached.', 'html', 'utf-8')
msg.attach(body) # add message body (text or html)
for f in files: # add files to the message
file_path = os.path.join(dir_path, f)
attachment = MIMEApplication(open(file_path, "rb").read(), _subtype="txt")
attachment.add_header('Content-Disposition','attachment', filename=f)
msg.attach(attachment)
s = smtplib.SMTP()
s.connect(host=SMTP_SERVER)
s.sendmail(msg['From'], msg['To'], msg.as_string())
print 'done!'
s.close()
I have implemented this for sending mail from gmail.
import smtplib
from email.mime.text import MIMEText
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email import Encoders
def send_mail_gmail(username,password,toaddrs_list,msg_text,fromaddr=None,subject="Test mail",attachment_path_list=None):
s = smtplib.SMTP('smtp.gmail.com:587')
s.starttls()
s.login(username, password)
#s.set_debuglevel(1)
msg = MIMEMultipart()
sender = fromaddr
recipients = toaddrs_list
msg['Subject'] = subject
if fromaddr is not None:
msg['From'] = sender
msg['To'] = ", ".join(recipients)
if attachment_path_list is not None:
for each_file_path in attachment_path_list:
try:
file_name=each_file_path.split("/")[-1]
part = MIMEBase('application', "octet-stream")
part.set_payload(open(each_file_path, "rb").read())
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment' ,filename=file_name)
msg.attach(part)
except:
print "could not attache file"
msg.attach(MIMEText(msg_text,'html'))
s.sendmail(sender, recipients, msg.as_string())
You can pass multiple address as element of toaddrs_list to whom you want to send mail and multiple attachments files names with their path in attachment_path_list.
How do i add a document attachment when sending an email with python ?
i get the email to send
(please ignore: i am looping the email to send every 5 seconds, only for testing purposes, i want it to send every 30 min, just have to change 5 to 1800)
here is my code so far. how do i attach a document from my computer?
#!/usr/bin/python
import time
import smtplib
while True:
TO = 'xxxx#gmail.com'
SUBJECT = 'Python Email'
TEXT = 'Here is the message'
gmail_sender = 'xxxx#gmail.com'
gmail_passwd = 'xxxx'
server = smtplib.SMTP('smtp.gmail.com',587)
server.ehlo()
server.starttls()
server.ehlo()
server.login(gmail_sender, gmail_passwd)
BODY = '\n'.join([
'To: %s' % TO,
'From: %s' % gmail_sender,
'Subject:%s' % SUBJECT,
'',
TEXT
])
try:
server.sendmail(gmail_sender,[TO], BODY)
print 'email sent'
except:
print 'error sending mail'
time.sleep(5)
server.quit()
This is the code that worked for me- to send an email with an attachment in python
#!/usr/bin/python
import smtplib,ssl
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.utils import formatdate
from email import encoders
def send_mail(send_from,send_to,subject,text,files,server,port,username='',password='',isTls=True):
msg = MIMEMultipart()
msg['From'] = send_from
msg['To'] = send_to
msg['Date'] = formatdate(localtime = True)
msg['Subject'] = subject
msg.attach(MIMEText(text))
part = MIMEBase('application', "octet-stream")
part.set_payload(open("WorkBook3.xlsx", "rb").read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="WorkBook3.xlsx"')
msg.attach(part)
#context = ssl.SSLContext(ssl.PROTOCOL_SSLv3)
#SSL connection only working on Python 3+
smtp = smtplib.SMTP(server, port)
if isTls:
smtp.starttls()
smtp.login(username,password)
smtp.sendmail(send_from, send_to, msg.as_string())
smtp.quit()
I found an easy way to do it using what Corey Shafer explains in this video on sending emails with python.
import smtplib
from email.message import EmailMessage
SENDER_EMAIL = "sender_email#gmail.com"
APP_PASSWORD = "xxxxxxx"
def send_mail_with_excel(recipient_email, subject, content, excel_file):
msg = EmailMessage()
msg['Subject'] = subject
msg['From'] = SENDER_EMAIL
msg['To'] = recipient_email
msg.set_content(content)
with open(excel_file, 'rb') as f:
file_data = f.read()
msg.add_attachment(file_data, maintype="application", subtype="xlsx", filename=excel_file)
with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp:
smtp.login(SENDER_EMAIL, APP_PASSWORD)
smtp.send_message(msg)
Here is just a slight tweak on SoccerPlayer's post above that got me 99% of the way there. I found a snippet Here that got me the rest of the way. No credit is due to me. Just posting in case it helps the next person.
file = 'File.xlsx'
username=''
password=''
send_from = ''
send_to = 'recipient1 , recipient2'
Cc = 'recipient'
msg = MIMEMultipart()
msg['From'] = send_from
msg['To'] = send_to
msg['Cc'] = Cc
msg['Date'] = formatdate(localtime = True)
msg['Subject'] = ''
server = smtplib.SMTP('smtp.gmail.com')
port = '587'
fp = open(file, 'rb')
part = MIMEBase('application','vnd.ms-excel')
part.set_payload(fp.read())
fp.close()
encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment', filename='Name File Here')
msg.attach(part)
smtp = smtplib.SMTP('smtp.gmail.com')
smtp.ehlo()
smtp.starttls()
smtp.login(username,password)
smtp.sendmail(send_from, send_to.split(',') + msg['Cc'].split(','), msg.as_string())
smtp.quit()
Using python 3, you can use MIMEApplication:
import os, smtplib, traceback
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
def sendMail(sender,
subject,
recipient,
username,
password,
message=None,
xlsx_files=None):
msg = MIMEMultipart()
msg["Subject"] = subject
msg["From"] = sender
if type(recipient) == list:
msg["To"] = ", ".join(recipient)
else:
msg["To"] = recipient
message_text = MIMEText(message, 'html')
msg.attach(message_text)
if xlsx_files:
for f in xlsx_files:
attachment = open(f, 'rb')
file_name = os.path.basename(f)
part = MIMEApplication(attachment.read(), _subtype='xlsx')
part.add_header('Content-Disposition', 'attachment', filename=file_name)
msg.attach(part)
try:
server = smtplib.SMTP_SSL('smtp.gmail.com', 465)
server.ehlo()
server.login(username, password)
server.sendmail(sender, recipient, msg.as_string())
server.close()
except Exception as e:
error = traceback.format_exc()
print(error)
print(e)
Note* I simply used print(error) in this example. Typically, I send errors to logging.critical(error)
To send an attachment create a MIMEMultipart object and add the attachment to that. Here is an example from the python email examples.
# Import smtplib for the actual sending function
import smtplib
# Here are the email package modules we'll need
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
COMMASPACE = ', '
# Create the container (outer) email message.
msg = MIMEMultipart()
msg['Subject'] = 'Our family reunion'
# me == the sender's email address
# family = the list of all recipients' email addresses
msg['From'] = me
msg['To'] = COMMASPACE.join(family)
msg.preamble = 'Our family reunion'
# Assume we know that the image files are all in PNG format
for file in pngfiles:
# Open the files in binary mode. Let the MIMEImage class automatically
# guess the specific image type.
fp = open(file, 'rb')
img = MIMEImage(fp.read())
fp.close()
msg.attach(img)
# Send the email via our own SMTP server.
s = smtplib.SMTP('localhost')
s.sendmail(me, family, msg.as_string())
s.quit()
You can also accomplish this with Red Mail nicely:
from redmail import EmailSender
from pathlib import Path
import pandas as pd
gmail = EmailSender(
host='smtp.gmail.com',
port=465,
user_name="you#gmail.com",
password="<YOUR PASSWORD>"
)
gmail.send(
subject="Python Email",
receivers=["you#gmail.com"],
text="Here is the message",
attachments={
# From path on disk
"my_file.xlsx": Path("path/to/file.xlsx"),
# Or from Pandas dataframe
"my_frame.xlsx": pd.DataFrame({"a": [1,2,3]})
}
)
You may also pass bytes if you wish to attach your Excel file that way.
To install Red Mail:
pip install redmail
Red Mail is an open source email library full of features. It is well tested and well documented. Documentation is found here: https://red-mail.readthedocs.io/en/latest/