xlwings笔记

前言

对比了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代码–官方网站

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值