I am trying to password protect an entire Excel file (same functionality as File > Protect Workbook > Encrypt with Password) using Python.
I have come across openpyxl and the protection features it offers (https://openpyxl.readthedocs.io/en/stable/protection.html) seems to fulfill this need. I have the following code:
from openpyxl import Workbook
from openpyxl import load_workbook
test_spreadsheet = "test.xlsx"
wb = load_workbook(test_spreadsheet)
wb.security.workbookPassword = "password"
However, I am getting the following error:
AttributeError: 'NoneType' object has no attribute 'workbookPassword'
Does anyone have an idea of what is causing this AttributeError? I have printed the sheetnames from wb and that is correctly printing the tabs in my Excel document.
For a default-constructed workbook, the security property is initialized by default:
self.security = DocumentSecurity()
However, workbooks constructed by reading a workbook are not just default-constructed; they are also manipulated by a Parser object:
wb_part = _find_workbook_part(self.package)
self.parser = WorkbookParser(self.archive, wb_part.PartName[1:], keep_links=self.keep_links)
self.parser.parse()
wb = self.parser.wb
...
self.wb = wb
Parser.init does default-construct a Workbook, but then overrides specific properties with those of the source document:
self.wb.security = package.workbookProtection
This means that for files that had no security settings, the imported workbook object has a value of None for its security property (and thus your error, as None clearly has no attribute workbookPassword).
Your solution is then to create a default WorkbookProtection(), assign it to the workbook, and then set the workbook password.
As the Openpyxl document states "Workbook Protection
To prevent other users from viewing hidden worksheets, adding, moving, deleting, or hiding worksheets, and renaming worksheets, you can protect the structure of your workbook with a password."
It's not the same as File > Protect Workbook > Encrypt with Password.
Also does not work with an existing workbook.
If you run the following code, and open the newly created book 'test.xlsx' you should see it will open without a password however you cannot do any of those actions in italics above unless you go to the 'changes' toolbar and click 'Protect Workbook' then enter the password.
from openpyxl import Workbook
from openpyxl import load_workbook
test_spreadsheet = "test.xlsx"
wb = Workbook()
wb.security.workbookPassword = 'password'
wb.security.lockStructure = True
wb.save(test_spreadsheet)
I don't believe openpyxl or other Python module supports the option you want.
Related
I want to open and edit an excel workbook. However, when I run the following, it always create a new book (Book1) which I don't want.
import xlwings as xw
mypath= #path
app= xw.App()
wb=app.books.open(mypath)
After running, there will always be an unnecessary new Book1 created. Is there anyway to make it tidier?
I tried replacing app=xw.App() with app=xw.App(add_book=False), but it shows error below:
raise XlwingsError("Couldn't find any active App!")
xlwings.XlwingsError: Couldn't find any active App!
I also tried removing the line app=xw.App() and directly open the book with
wb=xw.books.open(mypath)
If I already have an excel file opened, then this worked as I wish, opened the book with any new book created. But if there is no other excel file opened, then the same error as above is raised.
Also tried the following from previous questions. https://stackoverflow.com/questions/11018237/open-excel-application-without-new-workbook
import xlwings as xw
mypath= #path
app= xw.App()
app.ActiveWorkbook.Close(False);
app.visible = True;
wb=app.books.open(mypath)
Error occured
app.ActiveWorkbook.Close(False);
AttributeError: 'App' object has no attribute 'ActiveWorkbook'
This seems to be a very simple question, please bear me since I am very new to Python (and xlwings) and this is my first time asking questions here.
I like to use a context manager since it cleans up nicely at close.
import xlwings as xw
workbook = 'Book1.xlsx'
with xw.App() as app:
wb = xw.Book(workbook)
ws = wb.sheets('Sheet1')
...
wb.save(workbook) # needed only if data is written to the workbook
wb.close()
##----------------------------##
##----------------------------##
import xlwings as xw
workbook = 'yourExcelFile.xlsm'
with xw.App(visible=False, add_book=False) as app:
wb = xw.Book(workbook)
ws = wb.sheets('Sheet1')
# ...
print(ws.range('A1').value)
wb.save(workbook) # <-- Needed only where sheet data changed
wb.app.quit()
I am trying to write some data into an excel file (using openpyxl) after a user filled out a form created in Flask (WTForms). The data gets written to an excel sheet, but every time, I open the excel file, I get a below error. I am not sure what I need to do to stop that warning from appearing.
Excel error
My code:
from openpyxl import Workbook, load_workbook
class MyForm(Form):
Name = StringField('Name: ', [InputRequired("Please enter your name.")])
#app.route('/submit', methods=['POST'])
def submit_form():
Name = form.Name.data
workbook = load_workbook(filename='C:\\users\\TestUser\\Desktop\\Test.xlsx')
worksheet = workbook.active
appendData = worksheet.cell(row=1, column=1, value=(Name))
worksheet.append(appendData)
workbook.save('C:\\users\\TestUser\\Desktop\\Test.xlsx')
worksheet.append([Name]) resolves the issue.
i have a weekly report that i need to do, i chooseed to create it with openpyxl python module, and send it via mail, when i open the received mail (outlook), the cells with formulas appears as empty, but when downloading the file and open it, the data appears, OS fedora 20. parts of the code :
# imported modules from openpyxl ...
wb = Workbook()
ws = wb.active
counter = 3
ws.append(row)
for day in data :
row = ['']*(len(hosts)*2 +5)
row[0] = day.dayDate
row[1] ='=SUM(F'+str(counter)+':'+get_column_letter(len(hosts)+5)+str(counter)+\
')/(COUNT(F'+str(counter)+':'+get_column_letter(len(hosts)+5)+str(counter)+'))'
row[2] = '=SUM('+get_column_letter(len(hosts)+6)+str(counter)+':'+\
get_column_letter(len(hosts)*2+5)+str(counter)+')/COUNT('+\
get_column_letter(len(hosts)+6)+str(counter)+':'+\
get_column_letter(len(hosts)*2+5)+str(counter)+')'
row[3] = '=MAX('+get_column_letter(len(hosts)+6)+str(counter)+':'+\
get_column_letter(len(hosts)*2+5)+str(counter)+')'
row[4] = '=_xlfn.STDEV.P('+get_column_letter(len(hosts)+6)+str(counter)\
+':'+get_column_letter(len(hosts)*2+5)+str(counter)+')'
counter += 1
then, i create from the date some charts, etc.. and save, then send via mail :
wb.save(pathToFile+fileName+'.xlsx')
os.system('echo -e "'+msg+'" | mail -s "'+fileName+'" -a '+\
pathToFile+fileName+'.xlsx -r '+myUsr+' '+ppl2send2)
those are parts of the actual code, any one have an idea why the email don't show the results of the formulas in the cells ? Thanks in advance :)
openpyxl doesn't compute a result for formulas inserted into a spreadsheet; if you open the sheet with excel and save it, the result will have values filled in.
opepyxl has a problem with formulas, after you update your excel file you need to open it and save it to get the values generated. There are two ways to solve this problem.
(I won't advice you to use this unless you really want it.)
You can automate the process of opening the file and saving it from python before reading it. You can do this by using the win32com module
import win32com.client
wb.save('PUT YOUR FILE PATH HERE')
ab = win32com.client.Dispatch("Excel.Application")
wb2 = ab.Workbooks.Open('PUT YOUR FILE PATH HERE')
ws = ab.Sheets('PUT THE SHEET NAME HERE')
ab.DisplayAlerts = False
wb2.Save()
wb2.Close()
ab.Application.Quit()
#Now you can read from the file and you can see the values generated from the formula
Or you can use xlwings instead of openpyxl. if you use this module you don't have to worry about saving the excel file. The module will do it for you.
import xlwings
wb= xlwings.Book('PUT YOUR FILE PATH HERE')
ws = wb.sheets[0]
#Do your update here example ws.range(2, 8).value = 34
wb.save()
wb.close()
I havent found much of the topic of creating a password protected Excel file using Python.
In Openpyxl, I did find a SheetProtection module using:
from openpyxl.worksheet import SheetProtection
However, the problem is I'm not sure how to use it. It's not an attribute of Workbook or Worksheet so I can't just do this:
wb = Workbook()
ws = wb.worksheets[0]
ws_encrypted = ws.SheetProtection()
ws_encrypted.password = 'test'
...
Does anyone know if such a request is even possible with Python? Thanks!
Here's a workaround I use. It generates a VBS script and calls it from within your python script.
def set_password(excel_file_path, pw):
from pathlib import Path
excel_file_path = Path(excel_file_path)
vbs_script = \
f"""' Save with password required upon opening
Set excel_object = CreateObject("Excel.Application")
Set workbook = excel_object.Workbooks.Open("{excel_file_path}")
excel_object.DisplayAlerts = False
excel_object.Visible = False
workbook.SaveAs "{excel_file_path}",, "{pw}"
excel_object.Application.Quit
"""
# write
vbs_script_path = excel_file_path.parent.joinpath("set_pw.vbs")
with open(vbs_script_path, "w") as file:
file.write(vbs_script)
#execute
subprocess.call(['cscript.exe', str(vbs_script_path)])
# remove
vbs_script_path.unlink()
return None
Looking at the docs for openpyxl, I noticed there is indeed a openpyxl.worksheet.SheetProtection class. However, it seems to be already part of a worksheet object:
>>> wb = Workbook()
>>> ws = wb.worksheets[0]
>>> ws.protection
<openpyxl.worksheet.protection.SheetProtection object at 0xM3M0RY>
Checking dir(ws.protection) shows there is a method set_password that when called with a string argument does indeed seem to set a protected flag.
>>> ws.protection.set_password('test')
>>> wb.save('random.xlsx')
I opened random.xlsx in LibreOffice and the sheet was indeed protected. However, I only needed to toggle an option to turn off protection, and not enter any password, so I might be doing it wrong still...
You can use python win32com to save an excel file with a password.
import win32com.client as win32
excel = win32.gencache.EnsureDispatch('Excel.Application')
#Before saving the file set DisplayAlerts to False to suppress the warning dialog:
excel.DisplayAlerts = False
wb = excel.Workbooks.Open(your_file_name)
# refer https://learn.microsoft.com/en-us/previous-versions/office/developer/office-2007/bb214129(v=office.12)?redirectedfrom=MSDN
# FileFormat = 51 is for .xlsx extension
wb.SaveAs(your_file_name, 51, 'your password')
wb.Close()
excel.Application.Quit()
Here is a rework of MichaĆ Zawadzki's solution that doesn't require creating and executing a separate vbs file:
def PassProtect(Path, Pass):
from win32com.client.gencache import EnsureDispatch
xlApp = EnsureDispatch("Excel.Application")
xlwb = xlApp.Workbooks.Open(Path)
xlApp.DisplayAlerts = False
xlwb.Visible = False
xlwb.SaveAs(Path, Password = Pass)
xlwb.Close()
xlApp.Quit()
PassProtect(FullExcelWorkbookPathGoesHere, DesiredPasswordGoesHere)
If you wanted to choose a file name that's in your project's folder, you could also do:
from os.path import abspath
PassProtect(abspath(FileNameInsideProjectFolderGoesHere), DesiredPasswordGoesHere)
openpyxl is unlikely ever to provide workbook encryption. However, you can add this yourself because Excel files (xlsx format version >= 2010) are zip-archives: create a file in openpyxl and add a password to it using standard utilities.
I tried to search many places but dit not see any example snippet of code about how to delete an existing worksheet in excel file by using xlutils or xlwt with python. Who can help me, please?
I just dealt with this and although this is not generally a good coding choice, you can use the internal Workbook_worksheets to access and set the worksheets for a workbook object.
write_book._Workbook__worksheets = [write_book._Workbook__worksheets[0]]
this would strip everything but the first worksheet associated with a Workbook
I just wanted to confirm that I got this to work using the answer David gave. Here is an example of where I had a spreadsheet (workbook) with 40+ sheets that needed to be split into their own workbooks. I copied the master workbook removed all but the one sheet and saved to a new spreadsheet:
from xlrd import open_workbook
from xlutils import copy
workbook = open_workbook(filepath)
# Process each sheet
for sheet in workbook.sheets():
# Make a copy of the master worksheet
new_workbook = copy.copy(workbook)
# for each time we copy the master workbook, remove all sheets except
# for the curren sheet (as defined by sheet.name)
new_workbook._Workbook__worksheets = [ worksheet for worksheet in new_workbook._Workbook__worksheets if worksheet.name == sheet.name ]
# Save the new_workbook based on sheet.name
new_workbook.save('{}_workbook.xls'.format(sheet.name))
The following method does what you need:
def deleteAllSheetBut(workingFolder, xlsxFILE, sheetNumberNotToDelete=1):
import win32com.client as win32
import os
excel = win32.gencache.EnsureDispatch('Excel.Application')
excel.Visible = False
excel.DisplayAlerts = False
wb = excel.Workbooks.Open( os.path.join( workingFolder, xlsxFILE ) )
for i in range(1, wb.Worksheets.Count):
if i != sheetNumberNotToDelete:
wb.Worksheets(i).Delete()
wb.Save()
excel.DisplayAlerts = True
excel.Application.Quit()
return
not sure about those modules but u can try win32
from win32com import client
def delete(self, number = 1):
"""
(if the sheet is the first use 1. -1 to use real number)
example: r.delete(1)
"""
sheetnumber = int(number) - 1