I am trying to use the library pywin32 to write Outlook e-mails in Python. I have a dataframe that has some recipients' emails and attachment paths. I'd like to know how to add all attachments under each unique recipient?
print(my_df)
Receiver_Email Attachment_Path HTMLBody
john.doe#gmail.com C:/Users/Desktop/Attachment1.pdf HTMLBODY_1
john.doe#gmail.com C:/Users/Desktop/Attachment2.pdf HTMLBODY_1
john.doe#gmail.com C:/Users/Desktop/Attachment3.pdf HTMLBODY_1
sean.smith#hotmail.com C:/Users/Desktop/Attachment11.pdf HTMLBODY_2
sean.smith#hotmail.com C:/Users/Desktop/Attachment22.pdf HTMLBODY_2
sean.smith#hotmail.com C:/Users/Desktop/Attachment33.pdf HTMLBODY_2
My coding is showing below:
for index, row in my_df.iterrows():
outlook = client.Dispatch('Outlook.Application')
mail = outlook.CreateItem(0)
mail.To = row['Receiver_Email']
mail.Subject = 'Greetings from Here!'
mail.Body = 'Please find your attachment(s)'
mail.Attachments.Add(row['Attachment_Path'])
mail.HTMLBody = row['HTMLBODY']
mail.Send()
However, this would send 3 emails to john.doe#gmail.com and 3 emails to sean.smith#hotmail.com. My expected outputs would be sending 2 emails total (1 for john and 1 for sean) with 3 attachments each.
Is there a way to do that? Thank you for the help!
Do a groupby on the email addresses, which creates unique dataframes, then iterate on the mail attachments. Something like this should work:
GB = my_df.groupby("Receiver_Email")
for ID , DF in GB:
outlook = client.Dispatch('Outlook.Application')
mail = outlook.CreateItem(0)
mail.To = row['Receiver_Email']
mail.Subject = 'Greetings from Here!'
mail.Body = 'Please find your attachment(s)'
for id , row in DF.iterrows():
mail.Attachments.Add(row['Attachment_Path'])
mail.Send()
Related
I want to group recipients by reason and sent out tailored emails for each reason. The data looks like this.
Email reason
0 one#outlook.com Address
1 two#outlook.com Address
2 three#outlook.com Phone Number
3 one#outlook.com Postcode
import Pandas as pd
data = { 'Email': ['one#outlook.com','two#outlook.com','three#outlook.com','one#outlook.com'],
'reason': ['Address','Address', 'Phone Number','Postcode']}
df = pd.DataFrame(data)
df
This is how I started.
for e in df['reason'].unique():
print(f"Reason: {e}")
print(df[df['reason'] == e]['Email'].to_list())
print('\n')
Reason: Address
['one#outlook.com', 'two#outlook.com']
Reason: Phone Number
['three#outlook.com']
Reason: Postcode
['one#outlook.com']
Not sure how to use this list below.
message = "Update"
email_subject = "Test"
recipients = e
# sending email function
def send_mail(text, subject, recipients):
o = win32com.client.Dispatch("Outlook.Application")
Msg = o.CreateItem(0)
Msg.to = ''
Msg.BCC = "; ".join(recipients)
Msg.Subject = subject
Msg.HTMLBody = text
Msg.Send()
# sending email
send_mail(message,email_subject,recipients)
How to set 3(or more) different email body's and loop around the grouped list to sent separate emails for each reason?
You need to repeat the following code in the loop:
Msg = o.CreateItem(0)
Msg.to = recipient;
Msg.Subject = subject
Msg.HTMLBody = text
Msg.Send()
where recipient is an email address of a single recipient.
You may find the Using Visual Basic for Applications in Outlook article helpful.
This question already has answers here:
How to send email to multiple recipients using python smtplib?
(18 answers)
Closed 1 year ago.
I have the below code to send out emails specific to certain addresses all in a pandas dataframe. The type says pandas.series. How can I be able to send each list with multiple addresses? Some have upto 8 email addresses separated by a semi-colon(;). Only possibility of sending out is to a single address. From the below dataframe, I'm able to send emails specific to the IDs to TestEmail values. Nothing works for Emails. I get the below error :
SMTPRecipientsRefused: {'': (421, b'4.7.0 Too many protocol errors (6) on this connection, closing transmission channel.')}
Dataframe df
IDs CompanyNames Emails TestEmail
2003 XXX xx#gmail.com;yy#ryt.com;dd#xx.com;ii#dd.com kk#kk.com
2004 XXXYY xx#gmail.com;yy#ryt.com;dd#xx.com kk#kk.com
2005 XXTTNN xx#gmail.com;yy#ryt.com;dd#xx.com;ii#dd.com kk#kk.com
2006 BBOOLL xx#gmail.com;yy#ryt.com kk#kk.com
The code is below. To the single address, I'm able to send attachments with names matching df['IDs'] values to the corresponding email address.
import smtplib, ssl
from email.message import EmailMessage
import getpass
ids = df['IDs']
emails_to = df['Emails'] #Pandas column with email addresses split by semi-colon(;)
namesofcompanies = df["CompanyNames"]
sendfrom = df["SenderList"]
date_7days = (datetime.now() + timedelta(days=7)).strftime('%d/%m/%Y')
date_14days = (datetime.now() + timedelta(days=13)).strftime('%d/%m/%Y')
email_pass = input() #Office 365 password
context=ssl.create_default_context()
for i in range(len(emails_to)): # iterate through the records
# for every record get the name and the email addresses
ID = str(ids[i])
Emaitstosendto = emails_to[i]
companynames = namesofcompanies[i]
tosendfrom = sendfrom[i]
if my_files_dict.get(ID): #Looks for attachments in the same folder with same name as the corresponding record
smtp_ssl_host = 'smtp.office365.com'
smtp_ssl_port = 587
email_login = "xxx#xxx.com" #Office 365 email
email_from = tosendfrom
email_to = Emaitstosendto
msg = MIMEMultipart()
msg['Subject'] = "Received Emails between "+date_7days+" - "+date_14days
msg['From'] = email_from
msg['To'] = email_to
msg['X-Priority'] = '2'
text = ("XXXX,\n"
f"xxxxxx\n\n")
msg.attach(MIMEText(text))
filename = my_files_dict.get(ID)#Files in the folder matching the ID
fo = open(filename,'rb')
s2 = smtplib.SMTP(smtp_ssl_host, smtp_ssl_port)
s2.starttls(context=context)
s2.login(email_login, email_pass)
attachment = email.mime.application.MIMEApplication(fo.read(),_subtype="xlsx")
fo.close()
attachment.add_header('Content-Disposition','attachment',filename=filename)
msg.attach(attachment)
s2.send_message(msg)
s2.quit()
Taken from this answer: How to send email to multiple recipients using python smtplib?
This is the line you have to change:
First split the email_to into a list of different emails and then join using ", "
msg['To'] = ", ".join(email_to.split(";"))
I have a desktop folder with 3 XLSX files: ID1.xlsx, ID2.xlsx, ID3.xlsx
I can use the following code to loop through a list of 3 email addresses and send individual emails to each address, but it attaches all XLSX files in the folder.
Expected Result: I want to attach ID1.xlsx to the first email and send, ID2.xlsx to the 2nd email and send, and finally ID3.xlsx to the 3rd email and send.
email_addresses = ['test1#test.com', 'test2#test.com', 'test3#test.com']
class EmailsSender:
def __init__(self):
self.outlook = win32.Dispatch('outlook.application')
def send_email(self, to_email_address, attachment_path):
mail = self.outlook.CreateItem(0)
mail.To = to_email_address
mail.Subject = 'Report'
mail.Body = """Report is attached."""
if attachment_path:
mail.Attachments.Add(Source=attachment_path, Type=olByValue)
mail.Send()
def send_emails(self, email_addresses, attachment_path=None):
for email in email_addresses:
self.send_email(email, attachment_path)
attachment_path = r'C:\Users\Desktop\Test\*.xlsx'
email_sender = EmailsSender()
email_sender.send_emails(email_addresses, attachment_path)
You will need to reference the file name with email so switch to Dictionaries.
Example
email_addresses = {'test1#test.com': 'ID1.xlsx',
'test2#test.com': 'ID2.xlsx',
'test3#test.com': 'ID3.xlsx'
}
attachment_path = r'C:\Users\Desktop\Test'
for email, file in email_addresses.items():
print(email, attachment_path + file)
i need to read all email from a particular address and get "some content" into excel
i have had some success in reading emails but i am stuck to get the content of these emails in to excel
import win32com.client
import xlsxwriter
import pandas
wb = xlsxwriter.Workbook('Planned_power_outage.xlsx')
ws = wb.add_worksheet()
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI").Folders
folder = outlook(1)
inbox = folder.Folders("Inbox")
messages = inbox.Items
for message in messages:
if (message.SenderEmailAddress) == 'addess here':
sent_date = message.senton.date()
sender = message.Sender
subject = message.Subject
content = message.body
possible_node = subject.split(" ")[1]
print(sender,subject,content)
i expect to see the output as below in a excel file
SENDER SUBJECT CONTENT
alpha this is subject0 blahbalahblahablah
alpha this is subject1 blahbalahblahablah
alpha this is subject2 blahbalahblahablah
I have put together a code to send emails to multiple recipients. However each recipient receives all mails instead of his own.
Dataframe:
email content
mark#gmail.com Hi Mark, bla bla
eve#gmail.com Hi Eve, bla bla
john#gmail.com Hi, John bla bla
for content in df['content']:
for email in df['email']:
message = MIMEMultipart()
message['Subject'] = "Subject"
message['From'] = 'my email'
message['Reply-to'] = 'my email'
message['To'] = '{}'.format(email)
text = MIMEText(mail)
message.attach(text)
server = smtplib.SMTP ('smtp.gmail.com',587)
server.ehlo()
server.starttls()
server.login(login, password)
server.sendmail(message['From'], message['To'], message.as_string())
server.quit()
Try this:
df = {
'content': ['test1', 'test2', 'test3'],
'email': ['mail1#mail.com', 'mail2#mail.com', 'mail3#mail.com']
}
x = 0
for email in df['email']:
print(email)
print(df["content"][x]+"\n")
x+=1
Just zip columns together and omit one loop:
for email, content in zip(df['email'], df['content']):
message = MIMEMultipart()
message['Subject'] = "Subject"
message['From'] = 'my email'
message['Reply-to'] = 'my email'
message['To'] = '{}'.format(email)
text = MIMEText(mail)
message.attach(text)
server = smtplib.SMTP ('smtp.gmail.com',587)
server.ehlo()
server.starttls()
server.login(login, password)
server.sendmail(message['From'], message['To'], message.as_string())
server.quit()
I was iterating it twice. Instead of using 2 for loops, the correct way to iterate through out DataFrame is using
Instead of this:
for content in df['content']:
for email in df['email']:
Use this:
for c in df.itertuples():
message = MIMEMultipart()
message['To'] = '{}'.format(c[0])
text = MIMEText(c[1])
message.attach(text)