I need to open an xlsx document and color it. But I don't understand why it shows cell error it. My algorithm works like this:
Open xlsx, writer = pd.ExcelWriter(path, engine='xlsxwriter')
worksheet = writer.sheets['Sheet1']
col_style = Font(name = "Reem Kufi", size = 12, color = "DB3B22", italic =True)
for i in range(2,40):
worksheet.cell(row = i, column = 3).font = col_style
Error:- 'Worksheet' object has no attribute 'cell'
you will need to use Openpyxl's loadworkbook instead of ExcelWriter to achieve what you are looking for. Updated code here. Note that I have only changed the initial open file and sheet using the new code and required libraries and not changed rest of your code.
from openpyxl.styles import Font
from openpyxl import load_workbook
writer = load_workbook(filename='YourFile.xlsx')
worksheet = writer['Sheet1']
col_style = Font(name = "Reem Kufi", size = 12, color = "DB3B22", italic =True)
for i in range(2,40):
worksheet.cell(row = i, column = 3).font = col_style
writer.save('YourFile.xlsx')
Related
So, given:
dttm = datetime.datetime.strptime("2014-06-23 13:56:30", "%Y-%m-%d %H:%M:%S")
ws['A1'] = dttm
The result in excel is that the correct date-time is written to the cell (you can see it where you'd input formulas). BUT, the cell display format is only MM/DD/YYYY.
I need the cell to display like "6/23/2014 13:56" instead of just "6/23/2014".
How can I explicitly format the cell to accomplish this?
Thanks!
Edit
#alecxe This solution works and is exactly what I asked for. I would like to be able to save styles like the solution by #Woodham. Unfortunately it raises a typeError (see comment). Any suggestions?
The simplest way to format a cell is using .number_format = "format" as in:
value = datetime.datetime.strptime("2014-06-23 13:56:30", "%Y-%m-%d %H:%M:%S")
cell = ws['A1']
cell.value = value
cell.number_format = 'YYYY MMM DD'
This is tested in openpyxl (2.2.2)
For openpyxl 2.4.5 you'll no longer have access to NumberFormat and Style and will have to use NamedStyle. Here's some sample usage:
from openpyxl.styles import NamedStyle
date_style = NamedStyle(name='datetime', number_format='DD/MM/YYYY HH:MM:MM')
ws['A1'].style = date_style
Alternatively with the new NamedStyle class, you can set the style by the string name once NamedStyle has been instantiated:
from openpyxl.styles import NamedStyle
NamedStyle(name='custom_datetime', number_format='DD/MM/YYYY HH:MM:MM')
ws['A1'].style = 'custom_datetime'
Documentation here: https://openpyxl.readthedocs.io/en/stable/styles.html
I believe you will need to set a openpyxl.styles.Style on the cell(s) that you want to format.
Looking at the documentation here, something like this should work:
dttm = datetime.datetime.strptime("2014-06-23 13:56:30", "%Y-%m-%d %H:%M:%S")
s = Style(number_format=NumberFormat('dd-mm-yyyy h:mm:ss'))
ws['A1'] = dttm
ws['A1'].styles = s
Update:
Style class is no longer used, for the solution refer to this answer.
For openpyxl 2.3.4 the NumberFormat cannot be imported, but this code works to set the style:
from openpyxl.styles import Style
…
date_style = Style(number_format="DD/MM/YYYY HH:MM:MM")
ws['A1'].style = date_style
from openpyxl import load_workbook
from openpyxl.styles import NamedStyle
xlsx_file = args.xlsx_file.name
# openning:
wb = load_workbook(filename = xlsx_file)
# create date style:
date_style = NamedStyle(name='date_style', number_format='DD.MM.YYYY HH:MM:MM')
# apply the style to the column H of the default sheet:
ws = wb.active
for row in ws[2:ws.max_row]: # skip the header
cell = row[7] # column H
cell.style = date_style
# saving:
wb.save(xlsx_file)
Edit: the above works for me, but somehow does not work on my coleagues machine. Converting the cell to string fixed that:
import datetime
from openpyxl import load_workbook
from openpyxl.styles import Alignment
xlsx_file = 'file.xlsx'
date_format = '%Y-%b-%d'
# openning:
wb = load_workbook(filename = xlsx_file)
# we also center align that column:
alignment = Alignment(horizontal='center')
# apply python date format to column H of the default sheet, and convert the column to Excel text:
ws = wb.active
for row in ws[2:ws.max_row]: # skip the header
cell = row[7] # column H
if isinstance(cell.value, datetime.datetime):
cell.value = cell.value.strftime(date_format)
cell.alignment = alignment
# saving:
wb.save(xlsx_file)
The same wrapped in a script:
#!/usr/bin/env python3
import argparse
import datetime
from openpyxl import load_workbook
from openpyxl.styles import Alignment
# ==============
## parsing args:
desc="""
Applies python date format to a given column of the xlsx file (default sheet) and converts the column to a Excel text format.
Dependencies:
pip3 install --user --upgrade openpyxl
"""
parser = argparse.ArgumentParser(description=desc, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('--version', action='version', version='%(prog)s 0.01')
parser.add_argument('-f', '--file',
help = "xlsx file",
dest = 'xlsx_file',
type = argparse.FileType('r'),
)
parser.add_argument('-c', '--column',
help = "column (starting from A) (default to %(default)s)",
dest = 'column',
type = str,
default = "A",
)
parser.add_argument('-d', '--date-format',
help = "date format to use, e.g. %%d.%%m.%%Y (default to %(default)s)",
dest = 'date_format',
type = str,
default = '%Y-%b-%d',
)
args = parser.parse_args()
# =========
## program:
xlsx_file = args.xlsx_file.name
column_number = sum(
[ ord(char) - 97 + i*26 for i,char in enumerate(
list( args.column.lower() )
) ]
)
# openning:
wb = load_workbook(filename = xlsx_file)
# we also center align that column:
alignment = Alignment(horizontal='center')
# apply python date format to a given column of the default sheet, and convert the column to Excel text:
ws = wb.active
for row in ws[2:ws.max_row]: # skip the header
cell = row[column_number]
if isinstance(cell.value, datetime.datetime):
cell.value = cell.value.strftime(args.date_format)
cell.alignment = alignment
# saving:
wb.save(xlsx_file)
I found that this worked. Although number_format is used it seems to recognise the date format specified when put into the excel wb.
import datetime
date = datetime.date(2020, 2, 24) # python datetime format is yyyy mm dd
ws.cell(row=[row_ref], column=[col_ref], value=date)
ws.cell(row=[row_ref], column=[col_ref]).number_format = 'dd/mm/yy'
I have a script that scrapes data from list of websites using beautifulSoup package and save in an excel file using pandas and xlsxwriter packages.
What i want is to be able to format the excel file as i need like the width of the columns
but when i run the script it crash and display the below error.
AttributeError: 'NoneType' object has no attribute 'write'
code:
import pandas as pd
import requests
from bs4 import BeautifulSoup
import xlsxwriter
def scrap_website():
url_list = ["https://www.bayt.com/en/international/jobs/executive-chef-jobs/",
"https://www.bayt.com/en/international/jobs/head-chef-jobs/",
"https://www.bayt.com/en/international/jobs/executive-sous-chef-jobs/"]
joineddd = []
for url in url_list:
soup = BeautifulSoup(requests.get(url).content,"lxml")
links = []
for a in soup.select("h2.m0.t-regular a"):
if a['href'] not in links:
links.append("https://www.bayt.com"+ a['href'])
for link in links:
s = BeautifulSoup(requests.get(link).content, "lxml")
### update Start ###
alldd = dict()
alldd['link'] = link
dd_div = [i for i in s.select("div[class='card-content is-spaced'] div")
if ('<dd>' in str(i) ) and ( "<dt>" in str(i))]
for div in dd_div:
k = div.select_one('dt').get_text(';', True)
v = div.select_one('dd').get_text(';', True)
alldd[k] = v
### update End ###
joineddd.append(alldd)
# result
df = pd.DataFrame(joineddd)
df_to_excel = df.to_excel(r"F:\\AIenv\web_scrapping\\jobDesc.xlsx", index = False, header=True)
workbook = xlsxwriter.Workbook(df_to_excel)
worksheet = workbook.add_worksheet()
worksheet.set_column(0, 0,50)
workbook.close()
where is the error and how to fix it ?
To access and format the Excel workbook or worksheet created by to_excel() you need to create an ExcelWriter object first. Something like this:
import pandas as pd
# Create a Pandas dataframe from some data.
df = pd.DataFrame({'Data': [10, 20, 30, 20, 15, 30, 45]})
# Create a Pandas Excel writer using XlsxWriter as the engine.
writer = pd.ExcelWriter('pandas_simple.xlsx', engine='xlsxwriter')
# Convert the dataframe to an XlsxWriter Excel object.
df.to_excel(writer, sheet_name='Sheet1', index=False, header=True)
# Get the xlsxwriter objects from the dataframe writer object.
workbook = writer.book
worksheet = writer.sheets['Sheet1']
# Set the column width.
worksheet.set_column(0, 0, 50)
# Close the Pandas Excel writer and output the Excel file.
writer.save()
Output:
See Working with Python Pandas and XlsxWriter for more details.
to_excel function returns nothing. It's why you got the error message.
# save excel file
excel_file_name = r"jobDesc.xlsx"
df.to_excel(excel_file_name, index = False, header=True)
# open excel file for change col width or something
workbook = xlsxwriter.Workbook(excel_file_name)
Basically, you can't change existing file with xlsxwriter. There is a way to do so, but it is not recommended. I recommend openpyxl package instead of this. FYI, xlsxwriter: is there a way to open an existing worksheet in my workbook?
Most of the tutorials are outdated and I am unsuccessful at writing on an xlsx file with the help of python.
Here is some code I used:
import openpyxl
wb = openpyxl.load_workbook('C:/Users/user/Desktop/pytranslate.xlsx')
path = 'C:/Users/user/Desktop/pytranslate.xlsx'
ws = wb['Sheet1']
ws.cell(row = 1,column = 1,value = 'need for meas')
val1 = ws['A1'].value
print(val1)
wb.save(path)
The program ends with no error but I cannot see any output on the xlsx file.
I used this code to test. I created an empty excel document first. It updated correctly.
import openpyxl
path = 'pytranslate.xlsx'
wb = openpyxl.load_workbook(path)
ws = wb['Sheet1']
ws.cell(row = 1,column = 1,value = 'need for meas')
val1 = ws['A1'].value
print(val1)
wb.save(path)
Output (excel)
If your document is not updating, confirm you have correct permissions to the file and folder.
With openpyxl, you can use built-in styles defined in Excel like such:
wb = load_workbook("My_Template.xlsx")
ws = wb["My_super_Worksheet"]
ws["B2"].value = '=Hyperlink("https://stackoverflow.com", "SO")'
ws["B2"].style = "Hyperlink"
However, how would you use custom-styles defined in the Excel file? Something that could look like that:
wb = load_workbook("My_Template.xlsx")
ws = wb["My_super_Worksheet"]
wb.register_style("My_Custome_style") # defined as custom style in My_Template.xlsx
ws["B2"].value = '=Hyperlink("https://stackoverflow.com", "SO")'
ws["B2"].style = ""My_Custome_style""
The only way I have found was to re-define the styles in your program as Named Styles and save it but this would involve defining again the style instead of re-using what already exists.
Thanks a lot.
There is a workaround that does not answer to the question but can help solve the issue.
Setting a format in some given cells (for example in a worksheet called "STYLES")
Retrieving all the styles and registering them with openpyxl
Using the styles with their names
In Excel, that would give something like this:
In Python, you can add the following function and call it:
from copy import copy
import logging
from openpyxl.styles import NamedStyle
from openpyxl.workbook import Workbook
from openpyxl.worksheet.worksheet import Worksheet
from typing import Dict
def _register_styles(wb:Workbook, ws_style: Worksheet) -> Dict[str, NamedStyle]:
"""Parse a column of cells and register the styles
(the style names are the values of cells)
Args:
wb: The workbook
ws_style: The worksheet with the styles defined
Returns:
Dictionary of styles, by names
"""
list_styles = dict() # type: Dict[str, NamedStyle]
# Register styles of a config cells
col, row = "A", 2
cell = ws_style[col + str(row)] # or in Python 3.6+: f"{col}{row}"
while cell.value:
style = NamedStyle(name=cell.value)
style.font = copy(cell.font)
style.fill = copy(cell.fill)
style.border = copy(cell.border)
style.alignment = copy(cell.alignment)
style.number_format = copy(cell.number_format)
try:
wb.add_named_style(style)
except ValueError as e:
logging.warning("W006: style creation skipped because {}".format(e))
list_styles[cell.value] = style
row += 1
cell = ws_style[col + str(row)] # or in Python 3.6+: f"{col}{row}"
return list_styles
wb = load_workbook("My_Template.xlsx")
_register_styles(wb, wb["STYLES"])
NOTE: if you use Python prior to version 3.4, just remove the type hints at the beginning of the function prototype:
def _register_styles(wb, ws_style):
So as seen in the code below, all cells in column A get the font style 'italic24Font' except for cell 'A1'.
import openpyxl
from openpyxl.styles import Font
wb = openpyxl.Workbook()
sheet = wb["Sheet"]
italic24Font = Font( size = 24, italic = True )
column = sheet.column_dimensions['A']
column.font = italic24Font
sheet['A1'] = 'Hello world'
wb.save( 'test.xlsx' )
Look like when you write to a cell the column settings gets reset to default.
You can set the font to the column as you write content to the cell
EX:
import openpyxl
from openpyxl.styles import Font
wb = openpyxl.Workbook()
sheet = wb["Sheet"]
italic24Font = Font( size = 24, italic = True, color='00FF0000' )
column = sheet.column_dimensions['A']
column.font = italic24Font
sheet['A1'] = 'Hello world'
sheet['A1'].font = italic24Font
wb.save( 'test.xlsx' )
print "Done!!!"
I had the same issue. Not sure if it is a bug with openpyxl