I don't know if this is still relevant for the person that asked the question, but I am dealing with the same problem and found a possible solution.
- open the original file (say:
1.xlsm
) and do magic with openpyxl;
- save as
2.xlsx
;
- both files are actually zipped files: extract them to a temporary directory;
- copy files from the directory of the original file to the directory of the
xlsx
files: one of the files is the macro (vbaProject.bin
) and 2 of the files are necessary because they describe the type of the file among other things;
- put all of the files that belong to the
xlsx
directory back into a zip file and rename this from zip
to xlsm
. This file contains the original macro and has been edited with openpyxl
;
- (Optional) delete the two temporary directories and the
2.xlsx
file.
Example code:
import openpyxl
import zipfile
from shutil import copyfile
from shutil import rmtree
import os
PAD = os.getcwd()
wb = openpyxl.load_workbook('1.xlsm')
#####
# do magic with openpyxl here and save
ws = wb.worksheets[0]
ws.cell(row=2, column=3).value = 'Edited' # example
#####
wb.save('2.xlsx')
with zipfile.ZipFile('1.xlsm', 'r') as z:
z.extractall('./xlsm/')
with zipfile.ZipFile('2.xlsx', 'r') as z:
z.extractall('./xlsx/')
copyfile('./xlsm/[Content_Types].xml','./xlsx/[Content_Types].xml')
copyfile('./xlsm/xl/_rels/workbook.xml.rels','./xlsx/xl/_rels/workbook.xml.rels')
copyfile('./xlsm/xl/vbaProject.bin','./xlsx/xl/vbaProject.bin')
z = zipfile.ZipFile('2.zip', 'w')
os.chdir('./xlsx')
for root, dirs, files in os.walk('./'):
for file in files:
z.write(os.path.join(root, file))
z.close()
#clean
os.chdir(PAD)
rmtree('./xlsm/')
rmtree('./xlsx/')
os.remove('./2.xlsx')
os.rename('2.zip', '2.xlsm')