Python: Downloading Outlook Attachments and Change Filename on Save - python

the following code works below, however i am trying to add a parameter that changes the filename on the SaveAsFile method to the iteration of (a) the message that i am on.
As an Example the Current output is
Returned mail see transcript for details
Returned mail see transcript for details
The Desired output is
Returned mail see transcript for details1
Returned mail see transcript for details2
Returned mail see transcript for details3
Currently this code just overwrites the same save file in my folder, however i need to accomplish saving that same file from different messages to a new file name.
Code Below:
import win32com.client
import os
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6) # "6" refers to the index of a folder - in this case the inbox. You can change that number to reference
messages = inbox.Items
message = messages.GetFirst()
subject = message.Subject
i = 0
#
get_path = r'S:\Corporate Shared\Contracting Shared\DATA_PROJECTS\James\Email Extraction\Undeliverable Items'
for m in messages:
i = i + 1 #numeration
a = str(i) #Creates i as a string
if m.Subject == ("Returned mail: see transcript for details"):
#print(message)
attachments = message.Attachments
num_attach = len([x for x in attachments])
for x in range(1, num_attach + 1):
attachment = attachments.Item(x)
attachment.SaveASFile(os.path.join(get_path,attachment.FileName))
print(attachment)
#print(a)
message = messages.GetNext()
else:
message = messages.GetNext()

Instead of using attachment.FileName in the call to os.path.join, store attachment.FileName in a variable, then replace the last "." with "_" + x + "."

Related

Unable to loop correctly

I am working on assignment to extract emails from the mailbox.
Below are my codes, I am referencing from this case and combine with some other research online:
import win32com.client
import pandas as pd
import os
outlook = win32com.client.Dispatch("Outlook.Aplication").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6).Folders["Testmails"]
condition = pd.read_excel(r"C:\Users\Asus\Desktop\Python\Condition.xlsx", sheet_name = 'endword')
emails = condition.iloc[:,1].tolist()
done = outlook.GetDefaultFolder(6).Folders["Testmails"].Folders["Done"]
Item = inbox.Items.GetFirst()
add = Item.SenderEmailAddress
for attachment in Item.Attachments:
if any([add.endswith(m) for m in condition]) and Item.Attachments.Count > 0:
print(attachment.FileName)
dir = "C:\\Users\\Asus\\Desktop\\Python\\Output\\"
fname = attachment.FileName
outpath = os.path.join(dir, fname)
attachment.SaveAsFile(outpath)
Item.Move(done)
The code above is running, but it only saves the first email attachment, and the other email that matches the condition is not saving.
The condition file is like below, if is gmail to save in file A. But I am not sure if we can do by vlookup in loops.
mail end Directory
0 gmail.com "C:\\Users\\Asus\\Desktop\\Output\\A\\"
1 outlook.com "C:\\Users\\Asus\\Desktop\\Output\\A\\"
2 microsoft.com "C:\\Users\\Asus\\Desktop\\Output\\B\\"
Thanks for all the gurus who is helping much. I have edited the codes above but now is facing other issues on looping.
Fix Application on Dispatch("Outlook.Aplication") should be double p
On filter add single quotation mark round 'emails'
Example
Filter = "[SenderEmailAddress] = 'emails'"
for loop, you are using i but then you have print(attachment.FileName) / attachment.SaveAsFile
use i for all - print(i.FileName) / i.SaveAsFile or attachment
import win32com.client
Outlook = win32com.client.Dispatch("Outlook.Application")
olNs = Outlook.GetNamespace("MAPI")
Inbox = olNs.GetDefaultFolder(6)
Filter = "[SenderEmailAddress] = '0m3r#email.com'"
Items = Inbox.Items.Restrict(Filter)
Item = Items.GetFirst()
if Item.Attachments.Count > 0:
for attachment in Item.Attachments:
print(Item.Attachments.Count)
print(attachment.FileName)
attachment.SaveAsFile(r"C:\path\to\my\folder\Attachment.xlsx")
The 'NoneType' object has no attribute 'Attachments' error means that you're trying to get attachments from something that is None.
You're getting attachments in only one place:
for i in Item.Attachments:
...
so we can conclude that the Item here is None.
By looking at Microsoft's documentation we can see that the method...
Returns Nothing if no first object exists, for example, if there are no objects in the collection
Therefore, I'd imagine there's an empty collection, or no emails matching your filter
To handle this you could use an if statement
if Item is not None:
for i in Item.Attachments:
...
else:
pass # Do something here if there's nothing matching your filter

How to make a time restriction in outlook using python?

I am making a program that:
opens outlook
find emails per subject
extract some date from emails (code and number)
fills these data in excel file in.
Standard email looks like this:
Subject: Test1
Hi,
You got a new answer from user Alex.
Code: alex123fj
Number1: 0611111111
Number2: 1020
Number3: 3032
I encounter 2 main problems in the process.
Firstly, I do not get how to make time restriction for emails in outlook. For example, if I want to read emails only from yesterday.
Secondly, all codes and numbers from email I save in lists. But every item gets this ["alex123fj/r"] in place from this ["alex123fj"]
I would appreciate any help or advice, that is my first ever program in Python.
Here is my code:
import win32com.client
import re
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.Folders('myemail#....').Folders('Inbox')
messages = inbox.Items
def get_code(messages):
codes_lijst = []
for message in messages:
subject = message.subject
if subject == "Test1":
body = message.body
matches = re.finditer("Code:\s(.*)$", body, re.MULTILINE)
for match in matches:
codes_lijst.append(match.group(1))
return codes_lijst
def get_number(messages):
numbers_lijst = []
for message in messages:
subject = message.subject
if subject == "Test1":
body = message.body
matches = re.finditer("Number:\s(.*)$", body, re.MULTILINE)
for match in matches:
numbers_lijst.append(match.group(1))
return numbers_lijst
code = get_code(messages)
number = get_number(messages)
print(code)
print(number)
Firstly, never loop through all items in a folder. Use Items.Find/FindNext or Items.Restrict with a restriction on ConversationTopic (e.g. [ConversationTopic] = 'Test1').
To create a date/time restriction, add a range restriction ([ReceivedTime] > 'some value') and [ReceivedTime] < 'other value'

Python, forward and modify an outlook email while keeping formating

New on the platform, I would like to forward an email to several address. I do not find a way to do it while keeping the format and adding some content. Actually in the email I have text (bold, normal, colored) but also picture.
I tried it with two ways :
- save the mail in a folder (.msg) and parse it, but format is only text
- forward it directly from inbox, format is good but if I modify it same problem as the first way
Last problem, I can't find a way to delete the forward header
def Emailer(text, subject, recipient):
outlook = win32.Dispatch('outlook.application')
mail = outlook.CreateItem(0)
mail.To = recipient
mail.Subject = subject
mail.HTMLBody = text
Path_Att = "D:/***/Test123.pdf"
mail.Attachments.Add(Source=Path_Att)
mail.Display(True)
import extract_msg
#f = r'D:\***\test13.msg'
msg = extract_msg.Message(f)
msg_sender = msg.sender
msg_date = msg.date
msg_subj = msg.subject
msg_message = msg.body
Text_email = msg_message
Emailer(Text_email,'Our Research','xxx#xxx.com')
outlook = win32.Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6).Folders('Hugues')
messages = inbox.Items
message = messages.GetLast()
NewMsg = message.Forward()
TestSub = NewMsg.Subject
print(type(NewMsg.Body))
NewMsg.Body = 'Hello' + NewMsg.Body
NewMsg.Subject = TestSub.replace('FW: ','')
NewMsg.To = "xxxx#xxx.com"
NewMsg.Display(True)
Tks for your help
To preserve any message body formatting you need to deal with the HTMLBody or the Word object model where the body is represented by the Document class. In the code listed above you deal with a plain text string:
NewMsg.Body = 'Hello' + NewMsg.Body
And even when you set the HTMLBody property:
mail.HTMLBody = text
You should add any text inside the and elements to preserve the existing formatting and keep the HTML document well-formed.

Python read last 10 emails from Outlook

I can read my last email from my Outlook and send all the results according to each line's content.
However, I am unable to find the way to read my last 10 emails to be added to the fileCollect.txt file.
Any ideas how I could do this? Here is my current code:
import win32com.client
import csv
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6) # "6" refers to the index of a folder - in this case,
# the inbox. You can change that number to reference
# any other folder
messages = inbox.Items
message = messages.GetLast()
fileCollect = open("fileCollect.txt",'a')
delimiter = "¿"
fileCollect.write( str(message.Sender) + delimiter + str(message.Subject)+ delimiter + str(message.Body) )
fileCollect.close()
csvfile = open("csvfile.csv",'a')
with open("fileCollect.txt","r") as outfile:
for line in outfile:
if line.find("test") != -1:
csvfile.write(line)
csvfile.close()
The Items collection will not be sorted in any particular order until you actually sort it by calling Items.Sort. The VB script below sorts the collection by ReceivedTime in the descending order:
set messages = inbox.Items
messages.Sort("ReceivedTime", True)
set message = messages.GetFirst()
while not (message Is Nothing)
MsgBox message.Subject
set message = messages.GetNext()
wend
You can get the last 10 messages by specifying a negative index:
last_10_messages = messages[-10:]
This will return an array from messages[-10], which is the 10th to the last message, to the last message in the messages array.
use len(inbox.Items) to get the length of the inbox.
use inbox.Items.Item(i) to get i-th email in the inbox.
Ref:
https://learn.microsoft.com/en-us/office/vba/api/outlook.items.item

Getting n most recent emails using IMAP and Python

I'm looking to return the n (most likely 10) most recent emails from an email accounts inbox using IMAP.
So far I've cobbled together:
import imaplib
from email.parser import HeaderParser
M = imaplib.IMAP4_SSL('my.server')
user = 'username'
password = 'password'
M.login(user, password)
M.search(None, 'ALL')
for i in range (1,10):
data = M.fetch(i, '(BODY[HEADER])')
header_data = data[1][0][1]
parser = HeaderParser()
msg = parser.parsestr(header_data)
print msg['subject']
This is returning email headers fine, but it seems to be a semi-random collection of emails that it gets, not the 10 most recent.
If it helps, I'm connecting to an Exchange 2010 server. Other approaches also welcome, IMAP just seemed the most appropriate given that I only wanted to read the emails not send any.
The sort command is available, but it is not guaranteed to be supported by the IMAP server. For example, Gmail does not support the SORT command.
To try the sort command, you would replace:
M.search(None, 'ALL')
with
M.sort(search_critera, 'UTF-8', 'ALL')
Then search_criteria would be a string like:
search_criteria = 'DATE' #Ascending, most recent email last
search_criteria = 'REVERSE DATE' #Descending, most recent email first
search_criteria = '[REVERSE] sort-key' #format for sorting
According to RFC5256 these are valid sort-key's:
"ARRIVAL" / "CC" / "DATE" / "FROM" / "SIZE" / "SUBJECT" / "TO"
Notes:
1. charset is required, try US-ASCII or UTF-8 all others are not required to be supported by the IMAP server
2. search critera is also required. The ALL command is a valid one, but there are many. See more at http://www.networksorcery.com/enp/rfc/rfc3501.txt
The world of IMAP is wild and crazy. Good luck
This is the code to get the emailFrom, emailSubject, emailDate, emailContent etc..
import imaplib, email, os
user = "your#email.com"
password = "pass"
imap_url = "imap.gmail.com"
connection = imaplib.IMAP4_SSL(imap_url)
connection.login(user, password)
result, data = connection.uid('search', None, "ALL")
if result == 'OK':
for num in data[0].split():
result, data = connection.uid('fetch', num, '(RFC822)')
if result == 'OK':
email_message = email.message_from_bytes(data[0][1])
print('From:' + email_message['From'])
print('To:' + email_message['To'])
print('Date:' + email_message['Date'])
print('Subject:' + str(email_message['Subject']))
print('Content:' + str(email_message.get_payload()[0]))
connection.close()
connection.logout()
# get recent one email
from imap_tools import MailBox
with MailBox('imap.mail.com').login('test#mail.com', 'password', 'INBOX') as mailbox:
for msg in mailbox.fetch(limit=1, reverse=True):
print(msg.date_str, msg.subject)
https://github.com/ikvk/imap_tools
this is work for me~
import imaplib
from email.parser import HeaderParser
M = imaplib.IMAP4_SSL('my.server')
user = 'username'
password = 'password'
M.login(user, password)
(retcode, messages) =M.search(None, 'ALL')
news_mail = get_mostnew_email(messages)
for i in news_mail :
data = M.fetch(i, '(BODY[HEADER])')
header_data = data[1][0][1]
parser = HeaderParser()
msg = parser.parsestr(header_data)
print msg['subject']
and this is get the newer email function :
def get_mostnew_email(messages):
"""
Getting in most recent emails using IMAP and Python
:param messages:
:return:
"""
ids = messages[0] # data is a list.
id_list = ids.split() # ids is a space separated string
#latest_ten_email_id = id_list # get all
latest_ten_email_id = id_list[-10:] # get the latest 10
keys = map(int, latest_ten_email_id)
news_keys = sorted(keys, reverse=True)
str_keys = [str(e) for e in news_keys]
return str_keys
Workaround for Gmail. Since the The IMAP.sort('DATE','UTF-8','ALL') does not work for gmail ,we can insert the values and date into a list and sort the list in reverse order of date. Can check for the first n-mails using a counter. This method will take a few minutes longer if there are hundreds of mails.
M.login(user,password)
rv,data= M.search(None,'ALL')
if rv=='OK':
msg_list=[]
for num in date[0].split():
rv,data=M.fetch(num,'(RFC822)')
if rv=='OK':
msg_object={}
msg_object_copy={}
msg=email.message_from_bytes(data[0][1])
msg_date=""
for val in msg['Date'].split(' '):
if(len(val)==1):
val="0"+val
# to pad the single date with 0
msg_date=msg_date+val+" "
msg_date=msg_date[:-1]
# to remove the last space
msg_object['date']= datetime.datetime.strptime(msg_date,"%a, %d %b %Y %H:%M:%S %z")
# to convert string to date time object for sorting the list
msg_object['msg']=msg
msg_object_copy=msg_object.copy()
msg_list.append(msg_object_copy)
msg_list.sort(reverse=True,key=lambda r:r['date'])
# sorts by datetime so latest mails are parsed first
count=0
for msg_obj in msg_list:
count=count+1
if count==n:
break
msg=msg_obj['msg']
# do things with the message
To get the latest mail:
This will return all the mail numbers contained inside the 2nd return value which is a list containing a bytes object:
imap.search(None, "ALL")[1][0]
This will split the bytes object of which the last element can be taken by accessing the negative index:
imap.search(None, "ALL")[1][0].split()[-1]
You may use the mail number to access the corresponding mail.

Categories

Resources