文章目录
前言
对比了openxl…、pd.ExcelWriter和xlwings。发现xlwtings最好用了,既不会频频失效,也不需要一格一格地遍历单元格。
先记录一些。
新建excel
import xlwings as xs
## 方法一
wb = xs.Book()
sheet = wb.sheets[0]
## 方法二
app = xw.App(visible=False)
app.display_alerts = False # 关闭一些提示信息,可以加快运行速度。 默认为 True。当合并单元格时,会一直提示是否合并
# app.screen_updating = True # 更新显示工作表的内容。默认为 True。关闭它也可以提升运行速度。
wb = app.books[0] # 建立excel连接
sheet = wb.sheets[0] # 获取第一张表
读取excel
# 方法一,直接读取
wb = xw.Book('data/test (2).xlsx')
# 方法二,不打开excel
xw.App(visible=False, add_book=False).books.open(path)
# 方法二如果报错,可能要读取两次:
app = xw.App(visible=False, add_book=False)
try:
wb = app.books.open('data/test (2).xlsx')
except:
wb = app.books.open('data/test (2).xlsx')
获取所有行数和列数
sheet.used_range.last_cell.row
sheet.used_range.last_cell.column
如何合并单元格sheet.range().api.Merge()
https://www.cnblogs.com/Small-lucky/p/14485234.html
同一列中,合并内容相同的单元格
# 合并单元格
j = 0
value = df.iloc[0, 0]
for n, i in enumerate(df.iloc[:, 0]):
if i != value:
sheet[j+1:n+1, 0].api.Merge()
j = n # 更新初始合并单元格
value = i
设置换行 sheet.range().api.WrapText = True
sheet[0:nrows, n].api.WrapText = True
不要科学计数法:
对于数字很长的,比如身份证字段,写入excel会显示科学计数法,导致后面的几位数全变成0了!!!丢失了信息。即使将dataframe字段、excel格式设置为文本都没有作用。这时需要对dataframe字段做如下操作:
df['身份证'] = df['身份证'].astype(str) + '\t'
冻结窗口
import xlwings as xs
wb = xs.Book()
wb.app.range("A2").select() # 1. 选中要冻结的单元格 # "A2"冻结首行
wb.app.api.ActiveWindow.FreezePanes = True # 2. 进行冻结操作
方法参考VBA冻结窗口操作:https://zhidao.baidu.com/question/1435266045034157659.html
如果要冻结其他工作簿的窗口格,那么只需指定窗口格:
sheet = wb.sheets[n]
sheet.select() # 选中该sheet
wb.app.range("A2").select()
wb.app.api.ActiveWindow.FreezePanes = True
添加所有框线
sheet2[0:nrows+2, 0:ncolumns].api.Borders(12).Weight = 3 # 内部单元格横线
sheet2[0:nrows+1, 0:ncolumns+1].api.Borders(11).Weight = 3 # 内部单元格竖线
窗口显示比例
wb.app.api.ActiveWindow.Zoom = 70 # 窗口显示70%
添加数据条
https://www.cnblogs.com/dontbealarmedimwithyou/p/16034327.html
import xlwings as xw
from xlwings.utils import rgb_to_int
def add_bar(rng_dl, pcolor=[99, 142, 198], ncolor=[255, 0, 0]):
'''
# 增加数据条,设置数据条颜色
params:
rng_dl: sheet.range格式,如sheet[:, 1] <Range [工作簿3]Sheet1!$A:$A>
pcolor: list, 颜色RGB,当数据为正数时,数据条的颜色,默认为蓝色
ncolor: list, 颜色RGB,当数据为负数时,数据条的颜色,默认为红色
return:
rng_dl: sheet.range格式,返回增加的数据条
'''
rng_dl.api.FormatConditions.Delete()
# 添加数据条
rng_dl.api.FormatConditions.AddDatabar()
rng_dl.api.FormatConditions(rng_dl.api.FormatConditions.Count).SetFirstPriority()
rng_dl.api.FormatConditions(1).MinPoint.Modify.newtype = xw.constants.ConditionValueTypes.xlConditionValueAutomaticMin
rng_dl.api.FormatConditions(1).MaxPoint.Modify.newtype = xw.constants.ConditionValueTypes.xlConditionValueAutomaticMax
# # 数据条颜色
rng_dl.api.FormatConditions(1).BarColor.Color = rgb_to_int(pcolor)
rng_dl.api.FormatConditions(1).BarColor.TintAndShade = 0
# # 数据条颜色方向等设置
rng_dl.api.FormatConditions(1).BarFillType = xw.constants.DataBarFillType.xlDataBarFillGradient # 渐进填充 xlDataBarFillSolid 实心填充
rng_dl.api.FormatConditions(1).Direction = xw.constants.Constants.xlContext
rng_dl.api.FormatConditions(1).BarBorder.Type = xw.constants.DataBarBorderType.xlDataBarBorderSolid # 颜色条是否有外框线
rng_dl.api.FormatConditions(1).BarBorder.Color.Color = rgb_to_int([174, 171, 171]) # 外框线设置为灰色
rng_dl.api.FormatConditions(1).AxisPosition = xw.constants.DataBarAxisPosition.xlDataBarAxisAutomatic # 负数数据条反向
# 当数据为负数时,反向填充红色
rng_dl.api.FormatConditions(1).NegativeBarFormat.ColorType = xw.constants.DataBarNegativeColorType.xlDataBarColor
rng_dl.api.FormatConditions(1).NegativeBarFormat.Color.Color = rgb_to_int(ncolor)
rng_dl.api.FormatConditions(1).NegativeBarFormat.Color.TintAndShade = 0
# rng_dl.api.FormatConditions(1).NegativeBarFormat.BorderColor.Color = rgb_to_int([0, 0, 0]) # 外框线设置为红色,不知道为什么报错
# # 中轴线axis颜色 设置
rng_dl.api.FormatConditions(1).AxisColor.Color = rgb_to_int([255,255,255])
rng_dl.api.FormatConditions(1).AxisColor.TintAndShade = 0
自动设置列宽
sheet.autofit() # 自适应列宽('c')
sheet.autofit('r') # 自适应高度('r')
2021.12.01更新
# 新版,将数据写入excel,并设置格式
# 2021.12.01更新:增加合并单元格/自定义列宽/设置统一行高/合并连续且重复的单元格/自定义日期后缀格式/自动换行/冻结窗口格
def to_excel(df, path, prefix=None, suffix=None, today_format='%Y%m%d%H%M', formats={}, column_width={}, row_height=None,
wrap_text=[], merge=[], freeze='A2'):
'''
将dataframe存入excel
参数:
df: DateFrame,要写入excel的DataFrame
path:str,文件路径,如:../../data/test.xlsx
prefix:str,文件前缀,如"test_",后缀自动用今天的日期补齐,如"20211122"
suffix:str,文件后最,如"_test",前缀自动用今天的日期补齐,如"20211122"
today_format:文件的日期格式,默认精确到时分
formats: Dict,自定义列格式,{'列名':'excel格式'},eg:{'列1':'@', '列2':'0.00', ...}
column_width:Dict,自定义列宽,{'列名':列宽},eg:{'列1':10, '列2':50,...}
row_height:float, 默认为None,一键设置每一行的行高
wrap_text:List,默认为[],需要自动换行的列 ['列1', ...] # Dict,需要/不需要自动换行的列, {'列1':True, '列n':False}
merge:List, 默认为[],需要合并单元格的列,将该列中连续重复的单元格合并 ['列1', ...]
freeze:冻结窗口格,默认为'A2'冻结首行,False/None即不冻结
返回:
将数据存入指定路径下的excel
'''
import xlwings as xw
import time
from os.path import join as pathJoin
# 1. 设置文件名称 前缀/后缀 + 日期(精确到时分)
today_date = time.strftime(today_format, time.localtime(time.time()))
if prefix: # 如果有前缀
path = pathJoin(path, prefix+today_date+'.xlsx')
elif suffix:
path = pathJoin(path, today_date+suffix+'.xlsx')
app = xw.App(visible=False)
app.display_alerts = False # 关闭一些提示信息,可以加快运行速度。 默认为 True。当合并单元格时,会一直提示是否合并
# app.screen_updating = True # 更新显示工作表的内容。默认为 True。关闭它也可以提升运行速度。
wb = app.books[0] # 建立excel连接
sheet = wb.sheets[0] # 获取第一张表
# 获取行数列数
nrows = df.shape[0] + 2 # 最大行数
ncolumns = df.shape[1] # 最大列字母
# 设置列宽
def get_len(x):
try:
length = len(str(x).encode('gbk')) # 计算每列宽度
return length
except:
return None
# 遍历每一列,计算每一列宽度
# 优先考虑参数column_width的设置
for n, c in enumerate(df.columns):
# 如果该列在参数column_width中有指示,那么设置为指定的格式
if c in column_width.keys():
sheet[0, n].column_width = column_width[c]
else:
length = df[~df[c].isna()][c].apply(get_len).mean() # 非空值的平均宽度
# 空列的宽度赋值为3
if str(length) == 'nan':
length = 3 # 计算列名本身的宽度
col_length = len(str(c).encode('gbk')) # 计算列名本身的宽度
# 如果列名比列平均宽度宽,则列宽设为列名的宽度
if length > col_length:
if ('时间' in c) or ('日期' in c): # 对于日期列,已经够宽了,不用+4的宽度
sheet[0, n].column_width = length # 添加列宽为评价宽度
else:
sheet[0, n].column_width = length + 4 # 添加列宽为评价宽度
else:
sheet[0, n].column_width = col_length + 0.5 # 设置列宽为列名
# 遍历每一列,设置字体格式
# 优先考虑参数formats的设置
for n, (c, d) in enumerate(df.dtypes.items()):
# 如果该列在format中有指示,那么设置为指定的格式
if c in formats.keys():
sheet[0:nrows, n].api.NumberFormat = formats[c]
elif ('int' in str(d)) or ('联系方式' in str(c)) or ('号' in str(c)) or ('手机' in str(c)) or ('电话' in str(c)) or ('id' in str(c)):
# 需在外部添加'\t',防止转为科学计数法(仅仅是转为文本没用的,需要添加一列\t,才能保证导入excel时为文本格式而不是科学计数法格式)
#df.loc[:, c] = (df.loc[:, c].astype(str) + '\t').values
sheet[0:nrows, n].api.NumberFormat = "@"
print('【%s】调整为文本格式'%c)
elif ('码' in str(c)):
sheet[0:nrows, n].api.NumberFormat = "0"
print('【%s】调整为整数格式'%c)
# 如果是计算天数,则只需取整
elif ('(天)' in str(c)):
sheet[0:nrows, n].api.NumberFormat = '0'
print('【%s】调整为整数'%c)
# 修改时间格式为'yyy-mm-dd h:mm
elif sheet[0:nrows, n].number_format == 'yyyy/m/d h:mm':
sheet[0:nrows, n].api.NumberFormat = 'yyyy-mm-dd hh:mm'
print('【%s】调整为日期格式:'%c, d)
elif ('datetime' in str(d)) or ('时间' in str(c)):
sheet[0:nrows, n].api.NumberFormat = 'yyyy-mm-dd hh:mm'
print('【%s】调整为日期格式'%c)
# 否则将列名有“占比”的格式调整为保留四位小数
elif ('占比' in str(c)) or ('百分比' in str(c)) or ('比例' in str(c)) or ('比' in str(c)):
sheet[0:nrows, n].api.NumberFormat = '0.00%'
print('【%s】调整为百分比格式'%c)
# 否则将float格式调整为保留两位小数
elif 'float' in str(d):
sheet[0:nrows, n].api.NumberFormat = '0.00'
print('【%s】调整为小数格式(两位数)'%c)
# 自动换行,如果列在参数wrap_text,那么自动换行
if c in wrap_text:
print('【%s】自动换行'%c)
sheet[0:nrows, n].api.WrapText = True
# 写入数据
sheet['A1'].value = df.columns.values
sheet['A2'].value = df.values
# 合并单元格
for c in merge: # 遍历每个需要合并单元格的列
ind = list(df.columns).index(c) # 找到列名在excel的列索引
j = 0 # 初始化第一个单元格的行索引
value = df.iloc[0, 0] # 初始化第一个单元格的取值
for n, i in enumerate(df.iloc[:, 0]):
if i != value: # 当遇到了不相同的取值
sheet[j+1:n+1, ind].api.Merge() # 合并取值相同且连续的单元格
j = n # 更新下一个即将合并的单元格索引
value = i # 更新下一个即将合并的单元格取值
sheet[j+1:n+2, ind].api.Merge() # 记得合并尾部的单元格
print('【%s】合并单元格'%c)
# 冻结窗口格
if freeze:
wb.app.range(freeze).select() # "A2"冻结首行
wb.app.api.ActiveWindow.FreezePanes = True
## 其他设置
# 设置字体
sheet[0:nrows, 0:ncolumns].api.Font.Name = '微软雅黑'
# 首行加粗
sheet[0,0:ncolumns].api.Font.Bold = True
# 居中
sheet[0:nrows, 0:ncolumns].api.HorizontalAlignment = -4108 # -4108 水平居中。 -4131 靠左,-4152 靠右。
# 首行背景颜色
sheet[0, 0:ncolumns].color = [128, 128, 128]
# 首行字体颜色, 2表示白色(详见https://blog.csdn.net/weixin_42146296/article/details/103647940)
sheet[0, 0:ncolumns].api.Font.ColorIndex = 2
# 首行添加单元格线条
sheet[0, 0:ncolumns].api.Borders(11).Weight = 2
# 设置行高
if row_height:
sheet[0:nrows, 0].api.RowHeight = row_height
wb.save(path)
print('文件保存再%s路径下'%path)
wb.close()
参考文献:
xlwings官网_文档
https://blog.csdn.net/weixin_42146296/article/details/103647940
https://blog.csdn.net/qq_42374697/article/details/121327756
VBA代码–官方网站