I am trying to refresh a macro enabled excel file through a python script. I have used win32com.client package for launching an excel instance and refreshing the file and trying to save it. I have also set DisplayAlerts = False. I am getting an alert like:
A file named 'F:\User\output\testfile.xlsm' already exists. Do you want to overwrite it?
I am not getting this alert for other xlsm files that I am trying to refresh even though there are files present with the same name in the destination folder. Here is a sample of the code I used to refresh the files:
import win32com.client as win32
xl = win32.DispatchEx('Excel.Application')
xl.DisplayAlerts = False
xl.Visible = False
xl.Interactive = False
xl.EnableEvents=False
wb = xl.Workbooks.Open(os.path.join(excel_path),ReadOnly=False, IgnoreReadOnlyRecommended =True)
xl.DeferAsyncQueries = True
wb.RefreshAll()
xl.CalculateUntilAsyncQueriesDone()
xl.DeferAsyncQueries = False
wb.SaveAs(output_filepath,ReadOnlyRecommended =False)
wb.Close(False)
xl.Quit()
Can anyone help me figure out why am I getting the alert only for that particular file?
Thanks in advance.
Right now I am doing the following.
import xlrd
resp = requests.get(url, auth=auth).content
output = open(r'temp.xlsx', 'wb')
output.write(resp)
output.close()
xl = xlrd.open_workbook(r'temp.xlsx')
sh = 1
try:
for sheet in xl.sheets():
xls.append(sheet.name)
except:
xls = ['']
It's extracting the sheets but I don't know how to read the file or if saving the file as an .xlsx is actually working for macros. All I know is that the code is not working right now and I need to be able to catch the data that is being generated in a macro. Please help! Thanks.
I highly recommend using xlwings if you want to open, modify, and save .xlsm files without corrupting them. I have tried a ton of different methods (using other modules like openpyxl) and the macros always end up being corrupted.
import xlwings as xw
app = xw.App(visible=False) # IF YOU WANT EXCEL TO RUN IN BACKGROUND
xlwb = xw.Book('PATH\\TO\\FILE.xlsm')
xlws = {}
xlws['ws1'] = xlwb.sheets['Your Worksheet']
print(xlws['ws1'].range('B1').value) # get value
xlws['ws1'].range('B1').value = 'New Value' # change value
yourMacro = xlwb.macro('YourExcelMacro')
yourMacro()
xlwb.save()
xlwb.close()
Edit - I added an option to keep Excel invisible at users request
For some reason the following code runs fine but the file overwrite alert keeps coming up even though I have set xl.EnableEvents = False, the code won't execute further unless I manually click the overwrite file popup. Does anyone know how to fix this?
The code opens an excel file which contains a string which allows the excel file to connect to the bloomberg api, I used this solution here to get this to work. As long as the file is open for enough time the data is pulled into the file and then saves and exits. It takes around ~35 seconds to get the data and the pandas table starts displaying the content I'm requesting
The problem is the popups! - I need to see when the string '#N/A Requesting Data...' is no longer in the file and can't see a way to do it without periodically saving the file. A solution that allows me to see the file contents dynamically without having to save would be great.
The solution here didn't work for me to stop the popups, I could probably make a new file each time and then delete them all at the end but this seems a bit clunky. This question extends this problem here if anyone wants to see the code and the problem in a more full context.
WB = 'C:/path/to/my/file.xlsx'
location = "EGLL"
def run_VWA(WB, location):
"""open the excel file, allow enough time to pull the data, then close and save"""
bb = 'C:/blp/API/Office Tools/BloombergUI.xla'
xl=win32com.client.DispatchEx("Excel.Application")
xl.Workbooks.Open(bb)
xl.AddIns("Bloomberg Excel Tools").Installed = True
wb = xl.Workbooks.Open(Filename=WB) #opens workbook in readonly mode.
xl.Visible = False
xl.EnableEvents = False
xl.DisplayAlerts = False
total=0
colstring='#N/A Requesting Data...'
while total < 40:
wb.Save()
df = df_from_excel(WB, location)
if colstring not in df:
break
time.sleep(3)
total+=3
wb.Close(SaveChanges=1)
xl.DisplayAlerts = True
xl.Quit()
#Cleanup the com reference.
del xl
return
Any help with this is much appreciated, I have very limited experience with the win32com library.
After a good few hours digging I've found how to solve this dynamically without the need for saving the file each iteration. If anyone else comes up against this problem most of the solution was found here. Many thanks assylias for some useful pointers.
def run_VWA(WB, location):
"""open the excel file, allow enough time to pull the data, then close and save"""
bb = 'C:/blp/API/Office Tools/BloombergUI.xla'
xl=win32com.client.DispatchEx("Excel.Application")
xl.Workbooks.Open(bb)
xl.AddIns("Bloomberg Excel Tools").Installed = True
wb = xl.Workbooks.Open(Filename=WB) #opens workbook in readonly mode.
xl.Visible = False
xl.EnableEvents = False
xl.DisplayAlerts = False
count=0
while True:
readData = wb.Worksheets(location)
allData = readData.UsedRange
if allData.Rows.Count > 1 or allData.Columns.Count > 1:
print('rows: {}'.format(allData.Rows.Count))
print('cols: {}'.format(allData.Columns.Count))
break
print(wb)
print(count)
time.sleep(3)
count+=3
wb.Close(SaveChanges=1)
xl.DisplayAlerts = True
xl.Quit()
#Cleanup the com reference.
del xl
return
I am working on automating an Excel file which is linked to certain .csv files.
Those .csv files are created from a SAS Code which is run every Quarter.
The files created are timestamped accordingly for example XYZ_201603.csv and XYZ_201606.csv and so on.
I need to update the links on my Excel File so that it automatically changes the link to the file from next quarter. I am trying to do this using Python win32com.client and my code looks like
from win32com import Dispatch
xl_app = Dispatch("Excel.Application")
xl_app.Visible = True
xl_app.DisplayAlerts = False
wb = xl_app.workbooks.open(r"C:\Users\XYZ\Desktop\Test\Summary.xlsx")
xl_app.AskToUpdateLinks = False
try:
wb.UpdateLink(Name=r"C:\Users\XYZ\Desktop\Test\XYZ_201606.csv")
except Exception as e:
print(e)
finally:
wb.Close(True)
wb = None
return True
xl_app.Quit()
xl = None
Whenever I run this, I get the following error
(-2147352567,'Exception occured.',(0,'Microsoft Excel','UpdateLink method of
Workbook class failed','xlmain11.chm',0,-2146827284),None)
Can Somebody tell me what is going wrong here. Also, incase I have multiple links, how do I tell which link needs to be changed to what? Can I pass a dictionary of directories of updated datasets
The code and the approach has been taken from this answer on Stack Overflow
Update Links in for Excel Spreadsheet Using Python
If you review the Microsoft Documentation, it seems that the UpdateLink method can be called without any parameters. Therefore this program should work:
import win32com.client as win32
xl_app = win32.gencache.EnsureDispatch("Excel.Application")
xl_app.Visible = True
xl_app.DisplayAlerts = False
wb = xl_app.workbooks.open(r"C:\Users\XYZ\Desktop\Test\Summary.xlsx")
wb.UpdateLink()
wb.Save()
wb.Close()
xl_app.Quit()
I'm not sure if my solution solves your issue, but I had the same problem and I used LinkSources() and ChangeLink() instead
newSource = r"C:\Users\XYZ\Desktop\Test\XYZ_201606.csv"
oldSource = wb.LinkSources()
wb.ChangeLink(Name = oldSource[0], NewName = newSource, Type = 1)
Hope it helps!
When using this code:
import win32com.client as win32
input_files = os.listdir(parent_dir)
input_files = [parent_dir + i for i in input_files if i.endswith('.xls') and not i.endswith('.xlsx')]
for input_file in input_files:
if not os.path.isfile(input_file.replace('.xls', '.xlsx')):
excel = win32.gencache.EnsureDispatch('Excel.Application')
wb = excel.Workbooks.Open(input_file)
wb.SaveAs(input_file + "x", FileFormat=51) # FileFormat = 51 is for .xlsx extension
wb.Close() # FileFormat = 56 is for .xls extension
excel.Application.Quit()
on excel files containing some macros/VB Project often messagebox shows up with warning, that all macros/VB Project will be lost, I would like to somehow automatically answer it, for example, "Yes", or maybe there is some parameter for SaveAs function or settings for
win32.gencache.EnsureDispatch('Excel.Application')?
Now I can just resave files as xlsm with FileFormat=51, but by some security reasons I don't want to do this, I really don't need this macros/VB Projects in my files.
Tried excel.DisplayAlerts = False - not helped.
Also thinking about something like pywinauto, but maybe it overkill and maybe there is more elegant solution?
Using
wb.Close(True) #if you need to save .xls file also
or using
wb.Close(False) # if you not decide to save .xls file
On the other hand, it may have other file opened, so when you use excel.Application.Quit() and those file does not save, excel will show confirmation dialog before closing.
Well, interesting thing, if you save in xlsm, not in xlsx, Excel would not ask questions about containing macros/VB Project, and you can open xlsm in openpyxl like it xlsm, so, how to resave in xlsm:
def resave_xls_file_as_xlsx_or_xlsm(in_xls_file_path, out_excel_file_type='xlsm'):
excel = win32.gencache.EnsureDispatch('Excel.Application')
wbxls = excel.Workbooks.Open(in_xls_file_path)
in_xls_file_path = in_xls_file_path.replace('/', '\\')
out_xlsx_or_xlsm_file_path = in_xls_file_path + out_excel_file_type[-1]
# FileFormat = 51 is for .xlsx extension, 52 for xlsm, no questions about containing VB script for xlsm
if out_excel_file_type == 'xlsm':
excel_file_format = 52
elif out_excel_file_type == 'xlsx':
excel_file_format = 51
else:
excel_file_format = 52 # or do some error corrections:
# print('ERROR, wrong excel file type:', out_excel_file_type)
# return None # sys.exit ('taihen taihen') # raise cthulhu
wbxls.SaveAs(out_xlsx_or_xlsm_file_path, FileFormat=excel_file_format)
wbxls.Close()
excel.Application.Quit()
return out_xlsx_or_xlsm_file_path
Also, sometime we have some sort of corrupted xlsx-file, and Excel start crying about it, and script stops, to autorecover it you can use this code, pay attention to xlsx files path, for example, this path won't work in this case:
resaved_xlsx_on_disk = 'c:/this_wont_work/2.xlsx' # usually works, but not with win32.client
corrupted_xlsx_on_disk = 'c:\\fyi_if_you_dont_use_two_backslashes_and_use_slashes_in_path_it_will_not_open\\1.xlsx'
resaved_xlsx_on_disk = r'c:\you_can_also_use_this\2.xlsx'
xl = win32.gencache.EnsureDispatch('Excel.Application')
# xl.Visible = True # otherwise excel is hidden (btw if corrupted it anyway show some message, but autoclose it)
wb = xl.Workbooks.Open(corrupted_xlsx_on_disk, CorruptLoad=1)
xl.SendKeys("{Enter}", Wait=1)
xl.DisplayAlerts = 0
wb.SaveAs(resaved_xlsx_on_disk) # You can try wb.Save(), but I can't get it to work :-(
wb.Close()
xl.Application.Quit()
===
upd. on 9 aug 2021:
Considering the code above, when using opposite operation - resaving xlsx to xls, often windows about Compatibility Check occures (also, if Excel just looks like hang up - that maybe because this window did not get focus and stays hidden between other windows), tl;dr, excel.DisplayAlerts = False do the job, code example (I will also check, maybe it applicable for xls->xlsx transformation without using this xlsm extension, if xls contains macroses):
import os
import win32com.client
import win32com
# if strange errors on start occures, delete everything from this path
# (maybe dynamic loading from now do this action unnecessary):
print(win32com.__gen_path__)
def resave_xlsx_files_as_xls(files_parent_dir):
input_files = os.listdir(files_parent_dir) # todo: use the glob, Luke!
input_files = [files_parent_dir + i for i in input_files if i.endswith('.xlsx')]
for input_file in input_files:
print(input_file)
if not os.path.isfile(input_file.replace('.xlsx', '.xls')):
excel = win32com.client.dynamic.Dispatch('Excel.Application')
wbxls = excel.Workbooks.Open(input_file)
# wbxls.DoNotPromptForConvert = True # seems that this line has no effect if uncommented
# wbxls.CheckCompatibility = False # seems that this line also has no effect if uncommented
excel.DisplayAlerts = False # this line do the main job - compatibility check window did not shows up!
# AFAIK this library hardly understand normal os.path.join or os.path.sep
input_file = input_file.replace('/', '\\')
input_file = input_file.replace('.xlsx', '.xls')
# FileFormat = 51 is for .xlsx extension, 52 for xlsm
excel_file_format = 56
# Compatibility check window will show up here on SaveAs line, you may did not locate it at first, cause it
# usually don't get focus, so it may be hidden somewhere under couple of windows, best
# way to find it - click "-" button on every window until wallpaper shows up, pressing
# hotkeys like Win-D will not help - this command will hide this compatibility check
# window also
wbxls.SaveAs(input_file, FileFormat=excel_file_format)
wbxls.Close() # FileFormat = 56 is for .xls extension
excel.Application.Quit()
else:
print('There is already converted file in folder!')
output_files = os.listdir(files_parent_dir)
output_files = [files_parent_dir + i for i in output_files if i.endswith('.xls')]
return output_files
resave_xlsx_files_as_xls('c:/test/')