EDIT : SOLVED IT!
I added this snippet of code below, in order to trace the position of the main inbox folder
for folder in outlook.Folders:
print(folder)
This highlighted that something had changed within the underlying Outlook structure and Folder[0] was no longer valid. I will now tweak code to make it more robust and dynamically choose folder
END EDIT
I wrote some code to pull emails from Outlook and save the attachments. It worked perfectly up until a few days ago.
I had not touched the code, so I can only assume that something within Outlook has changed. I work in a corporate environment, so there is remote update of software.
Does anybody have any idea what this error means and why its suddenly cropped up ? I am very bleak, as the code worked so well before this hiccup. Alternatively, any better way to retrieve emails and attachments from Outlook, using Python ?
import win32com.client
def main():
pass
def saveAttachments():
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI") # Opens Microsoft Outlook
mailbox = outlook.Folders[0] # Based off email address
inbox = mailbox.Folders["Inbox"]
emails = inbox.Items
emails.Sort("[ReceivedTime]", True)
destPath = "\\\\servername\\path\\"
try:
for mail in emails:
if ("Detailed MTM," in mail.subject) and (mail.Attachments.Count > 0):
print(mail.Sender)
print(mail.Subject)
print(mail.Receivedtime)
attachments = mail.Attachments
for file in attachments:
if "MTMDetailed" in str(file):
file.SaveAsFile(destPath + str("MTMDetailed.xls"))
break
except:
file = open(destPath + "error.log", "w")
file.write("Problem")
file.close()
if __name__ == '__main__':
main()
saveAttachments()
File "C:\Tools\Python\lib\site-packages\win32com\client\dynamic.py", line 256, in __getitem__
return self._get_good_object_(self._oleobj_.Invoke(dispid, LCID, invkind, 1, index))
pywintypes.com_error: (-2147352567, 'Exception occurred.', (4096, 'Microsoft Outlook', 'The attempted operation failed. An object could not be found.', None, 0, -2147221233), None)
I added code to iterate through the outlook.Folders to find the one I need, without relying on specific hardcoded position
Related
I have a short function that compiles a list of all the emails in a specific outlook folder and then saves the attachments to a specific folder. I last used this function successfully about 3 months ago and it seems something has broken it.
The exception:
com_error: (-2147352567, 'Exception occurred.', (4096, 'Microsoft Outlook', 'Array index out of bounds.', None, 0, -2147352567), None)
I'm not familiar enough with win32.com to understand what it's trying to tell me and it's a difficult error to get information on since it seems there can be many causes. I've marked in the function where it occurs.
It runs just fine for about 5,000 emails. There are about 1,000 attachments out of 6,500 that I can't download.
def outlook_attachments() -> None:
files_list = glob.glob('sort_test/*.txt')
files_list = [x[11:] for x in files_list]
# Connect to outlook.
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
# Navigate to the Oasis folder.
oasis_folder = outlook.GetDefaultFolder(4).folders("Oasis")
# Define items inside Oasis folder.
messages = oasis_folder.Items
# Start reading messages in Oasis folder, start from 0.
message = messages.GetFirst()
# Defining path to save attachment to.
path = 'fxg-notebooks/sort_test/'
# Iterating through all Oasis messages, saving files that have not yet been saved.
new_file_count = 0
message_count = 0
while True:
# print('Messages processed: ', message_count, end='\r')
message_count += 1
# Read attachment from outlook message.
try:
attachment = message.Attachments.Item(1) <--- EXCEPTION OCCURS HERE
# If GetNext() returned None, all messages have been read, break loop.
except AttributeError:
break
# Check for attachment membership in files_list.
try:
if files_list.index(str(attachment)) >= 0:
message = messages.GetNext()
continue
# If no membership, save attachment.
except ValueError:
attachment.SaveASFile(path + str(attachment))
message = messages.GetNext()
new_file_count += 1
return None
Your code is assuming there is at least one attachment in the message. Check that message.Attachments.Count > 0 first.
I am a first time user of stack overflow. I am reaching out here because I have trouble saving outlook email (.MSG) using python. The idea is to archive email as it is in physical drive.
Everything works, except the save As command which throw a very generic error. It would be great help if anyone can help me please.
Here is the code I am I Using:
import win32com.client as win32
from win32com.client import Dispatch
import os
import re
os.chdir("C:\\Users\\username\\Downloads\\RPA")
outlook = win32.gencache.EnsureDispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6)
print(inbox)
messages = inbox.Items
for message in messages:
message = messages.GetNext()
name = str(message.Subject)
name = re.sub('[^A-Za-z0-9]+', '', name) + '.msg'
print(name)
# message.Display(True)
message.SaveAs(os.getcwd() + '//' + name)
The Error I get executing the code
return self._oleobj_.InvokeTypes(61521, LCID, 1, (24, 0), ((8, 1), (12, 17)),Path
pywintypes.com_error: (-2147467260, 'Operation aborted', None, None)
In the code you are iterating over all items in the Inbox folder:
for message in messages:
message = messages.GetNext()
name = str(message.Subject)
name = re.sub('[^A-Za-z0-9]+', '', name) + '.msg'
If the folder contains a lot of items the operation may take some time to complete. I'd suggest processing items in chunks, so you may keep Outlook under control and prevent freezing the UI (if any). The Find/FindNext or Restrict methods can help with that. Read more about these methods in the following articles:
How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
How To: Use Restrict method to retrieve Outlook mail items from a folder
Another point is that a file name should be unique for all items in the folder. What is the actual string argument passed to the SaveAs method?
Also you need to make sure that no forbidden symbols are used in the filename.
I am trying to automate some python code that will automatically save some attachments from certain emails with a specific title.
Below is what I currently have:
import win32com.client as client
outlook = client.Dispatch('Outlook.Application')
namespace = outlook.GetNameSpace('MAPI')
inbox = namespace.GetDefaultFolder(6)
target_subject = 'Testing attachment'
mail_items = [item for item in inbox.Items if item.Class == 43]
filtered = [item for item in mail_items if item.Subject == target_subject]
if len(filtered) != 0:
target_email = filtered[0]
if target_email.Attachments.Count > 0:
attachments = target_email.Attachments
save_path = 'C:'
for file in attachments:
file.SaveAsFile(save_path.format(file.FileName))
However I seem to be getting an error with permissions?
com_error: (-2147352567, 'Exception occurred.', (4096, 'Microsoft Outlook', "Cannot save the attachment. You don't have appropriate permission to perform this operation.", None, 0, -2147024891), None)
Not sure how to work around this, I am the Admin etc.
I am also wondering what would be the changes required to actually deploy this online and have it running, i.e. I am not passing any credentials as it's local, if operating stand alone I would like it to access my inbox every 7 days or so and download this specific attachments from this specific email.
Any help will be greatly appreciated.
Thanks!
Choose another drive or folder, for example, My Documents doesn't require admin privileges for writing. Otherwise, you will have to run Outlook with admin privileges if you want to write anything to the system drive (C:).
Also I've noticed the following lines of code:
mail_items = [item for item in inbox.Items if item.Class == 43]
filtered = [item for item in mail_items if item.Subject == target_subject]
Iterating over all items in the folder is not really a good idea, moreover, you are doing that twice!
I'd recommend using the Find/FindNextorRestrict` methods of the Items class that allow getting only items that correspond to the specified condition. Read more about these methods in the following articles:
How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
How To: Use Restrict method to retrieve Outlook mail items from a folder
Users by default do not have write access to the root drive (C:).
Change it to something like 'c:\temp\'
I've written a short code to download and rename files from a specific folder in my outlook account. The code works great, the only problem is that I typically need to run the code several times to actually download all of the messages. It seems the code is just failing to acknowledge some of the messages, there are no errors when I run through it.
I've tried a few things like walking through each line step by step in the python window, running the code with outlook closed or opened, and trying to print the files after they're successfully saved to see if there are specific messages that are causing the problem.
Here's my code
#! python3
# downloadAttachments.py - Downloads all of the weight tickets from Bucky
# Currently saves to desktop due to instability of I: drive connection
import win32com.client, os, re
#This line opens the outlook application
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
#Not exactly sure why the inbox is default folder 6 but it works
inbox = outlook.GetDefaultFolder(6)
#box where the messages are to save
TicketSave = inbox.Folders('WDE').Folders('SAVE').Folders('TicketSave')
#box where the messages are moved to
done = inbox.Folders('WDE').Folders('CHES').Folders('Weight Tickets')
ticketMessages = TicketSave.Items
#Key is used to verify the subject line is correct. This script only works if the person sends
# their emails with a consistent subject line (can be altered for other cases)
key = re.compile(r'wde load \d{3}') #requires regulars expressions (i.e. 'import re')
for message in ticketMessages:
#will skip any message that does not match the correct subject line format (non-case sensitive)
check = str(message.Subject).lower()
if key.search(check) == None:
continue
attachments = message.Attachments
tic = attachments.item(1)
ticnum = str(message.Subject).split()[2]
name = str(tic).split()[0] + ' ticket ' + ticnum + '.pdf' #changes the filename
tic.SaveAsFile('C:\\Users\\bhalvorson\\Desktop\\Attachments' + os.sep + str(name))
if message.UnRead == True:
message.UnRead = False
message.Move(done)
print('Ticket pdf: ' + name + ' save successfully')
Alright I found the answer to my own question. I'll post it here in case any other youngster runs into the same problem as me.
The main problem is the "message.Move(done)" second from the bottom.
Apparently the move function alters the current folder thus altering the number of loops that the for loop will go through. So, the way it's written above, the code only ever processes half of the items in the folder.
An easy work around is to switch the main line of the for loop to "for message in list(ticketMessages):" the list is not affected by the Move function and therefore you'll be able to loop through every message.
Hope this helps someone.
I have windows 10 environment with Python 2.7, win32com package 219 is installed.
I was able to run below code which runs a macro in excel and generate a pie chart that will get attached(also get embedded in email body) to email and sent.
This program was working fine, earlier, however after some windows update, the same is giving AttributeError: olEmbeddeditem, i have imported win32com.client and its constant.
Want the embedded image in the email body, so replacing olEmbeddeditem with olByValue, etc. will not help, i think, though i have tried, which also didn't worked.
I have also done reinstallation of win32com package of python, however problem persist.
Earlier working code does not included "from win32com.client import constants", however since it was not working, have thought of adding this line, but this too didn't helped.
Any help would be appreciated.
import sys
import os
import win32com.client
import codecs
from win32com.client import constants
sys.stdout = codecs.getwriter("iso-8859-1")(sys.stdout, 'xmlcharrefreplace')
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6)
all_inbox = inbox.Items
folders = inbox.Folders
olMailItem = 0x0
obj = win32com.client.Dispatch("Outlook.Application")
xlApp = win32com.client.Dispatch("Excel.Application")
ExcelWorkBook = xlApp.Workbooks.Open('C:\Users\xxx\Desktop\data.xlsm')
xlSheet1 = ExcelWorkBook.Sheets("Sheet1")
xlApp.Application.Run("data.xlsm!Macro1")
chart1 = xlSheet1.ChartObjects(1)
chart1.Chart.Export("C:\Users\xxx\Desktop\photo.gif", "GIF", False)
xlApp.Workbooks(1).Close(SaveChanges=0)
xlApp.Application.Quit()
newMail = obj.CreateItem(olMailItem)
newMail.Subject = "Presentation of Automation"
attachment = newMail.Attachments.Add("C:\Users\xxx\Desktop\photo.gif", win32com.client.constants.olEmbeddeditem, 0, "photo")
imageCid = "photo.gif"
attachment.PropertyAccessor.SetProperty("http://schemas.microsoft.com/mapi/proptag/0x3712001E", imageCid)
newMail.HTMLBody = "<body>Dear Sir,Madam,<br>Please find the requested details.<br><br><p><img src=\"cid:{0}\"></body>".format(imageCid)
newMail.To = x
attachment1 = "C:\Users\xxx\Desktop\photo.gif"
newMail.Attachments.Add(attachment1)
newMail.Send()
os.remove("C:\Users\xxx\Desktop\photo.gif")
msg.UnRead = False
The root cause of the issue was not a Windows update as suspected, however it was because of a group email in the Inbox which was giving the error. After deleting that group mail or moving to different folder than Inbox the issue got resolved. Still not sure about the reason why it was giving the error and what is the way out going forward to ensure that such emails does not end up into a traceback.
The main reason for this attribute error is because your COM-server has shifted from late-binding (dynamic) to early binding (static).
Delete the gen_py folder in Temp which will revert the Dispatch to dynamic from static and your code should work fine.
instead of using
attachment = newMail.Attachments.Add("C:\Users\xxx\Desktop\photo.gif", win32com.client.constants.olEmbeddeditem, 0, "photo")
you can do
attachment = newMail.Attachments.Add("C:\Users\xxx\Desktop\photo.gif", 0x5, 0, "photo")