用Python的xlwings库分解报表并调用Outlook发送

为了公司DSR写的,有多个重复动作,所以把大方向定义成def函数,最后四次调用,最大限度让代码美观一点

import xlwings as xw
import win32com.client as win32
import datetime

# 程序运行的时候不显示在前端,也不新建Excel窗口
app = xw.App(visible=False, add_book=False)


# 定义一个时间函数,用来生成文件名的一部分
def yesterday():
    today = datetime.date.today()
    oneday = datetime.timedelta(days=1)
    yesterday = today - oneday
    return yesterday


# 时间定义结束

ywb = ['福建部',"浙江部", "河南部", "江西部", "海南部", "广东部"]
syb = ['事业一部合计', '事业二部合计', '事业三部合计']
yys = ['厦门所', '杭州所', '济南所']
fjq = ['浙江区']


# -----------------以下是一个DSR()函数,函数有2个参数,a和b-----------------------------------------
# 第一个参数a:表达组织机构(ywb,syb,yys,fjq)
# 第二个参数b:表示在哪一列寻找匹配a的数值,syb应输入1,fuq、ywb和yys应输入2
def DSR(a, b):
    for i in range(0, len(a)):  # 这是循环的开始,读取业务部的数量,每个业务部作为一个最大的循环
        # print(i)
        iw = app.books.open(r'E:\Documnet\工作\07_数据分析\临时文件\副本.xlsm')
        sheet_count = iw.sheets.count  # 读取整个工作簿的sheet数量

        # 由于源文件里还有很多隐藏的工作表不可以下发给各区域,而所有文件格式都#是一样,因此这里定义了一个函数first_sheet()
        # 用来读取第一个“全品项销额”在工作表中的位置,并以此作为下发给区域的第一个表格

        def first_sheet():
            for ii in range(0, sheet_count):
                if iw.sheets[ii].name == "全品项销额":
                    return ii

        # break # 写代码的时候测试用,阶段性中断程序
        # range左闭右开,所以需要需要页面总数+1,与python计数序列从0开始的习惯不同,xlwings的行列计数都是从1开始,下面会用到
        for sht in range(first_sheet(), sheet_count + 1):  # 开始对每个页面做循环
            oldsht = iw.sheets(sht)
            # print(oldsht.name)
            rownumber = oldsht.range('A10000').end('up').row
            # 另一个写法是rownumber  = oldsht.used_range.last_cell.row
            # print(rownumber)
            # 由于暂无法实现先复制再粘贴,所以我用的是直接在源文件里改动,然后另存为的办法
            # 以下这个循环是先从最后一行往上找,直到找到对应的单元格,rownumber和1表示范围, -1表示每次循环的step


            if a == yys: # 当a是业务所时,根据以下规则进行判断
                for fj in range(rownumber, 4, -1): # 先把福建区删除
                    if oldsht.cells(fj, b).value == '福建区':
                        oldsht.range(str(fj) + ':' + str(fj)).delete()
                        break
                if i == 0:
                    oldsht.range('6:500').delete()
                else:
                    for r3 in range(rownumber,4,-1):
                        if oldsht.cells(r3,b).value == a[i]:
                            oldsht.range(str(r3 + 1) + ':' + str(500)).delete()
                            oldsht.range('5' + ':' + str(r3-1)).delete()
                            break
            elif a == fjq:
                for r5 in range(rownumber, 1, -1):
                    if oldsht.cells(r5, b).value == a[i]:
                        oldsht.range(str(r5 + 1) + ':' + str(500)).delete()
                        break
            else:
                # 当a为ywb和syb时根据以下规则
                for fj in range(rownumber, 4, -1): # 先把浙江区删除
                    if oldsht.cells(fj, b).value == '浙江区':
                        oldsht.range(str(fj) + ':' + str(fj)).delete()
                        break
                for r1 in range(rownumber, 1, -1):
                    # print(oldsht.cells(r,2).value)
                    # print(oldsht.cells(r,2).value)
                    # print(ywb[i])
                    if oldsht.cells(r1, b).value == a[i]:
                        # 可以用range直接表示行或者列,但是无论是行还是列都应以文本形式出现,字母指代列,数字指代行
                        oldsht.range(str(r1 + 1) + ':' + str(500)).delete()
                        break
                # 由于目标业务部的上方还有可能有其他的业务部,因此,还需要从上往下再删一次
                # print('阶段一完成')
                while i != 0:
                    for r2 in range(5, rownumber):
                        if oldsht.cells(r2, b).value == a[i - 1]:
                            oldsht.range('5' + ':' + str(r2)).delete()
                            break
                    break
                # print('阶段二完成')
                # ----以上就是把不需要的行都删除了
            # -----以下是调用宏,把原有的组合取消,并取消隐藏的行,宏代码如下:
            '''
            Sub ungroup()
            Rows("3:150").ClearOutline
            Rows("3:150").EntireRow.Hidden = False
            Range("B5").Select
            End Sub
            '''
            oldsht.activate()
            ungroup = iw.macro('ungroup')
            ungroup()
            # -----然后下面的循环是把多余的sheet删除,一共有多少个sheet,就删多少次
        for iii in range(0, first_sheet()):
            iw.sheets[0].delete()
        print('sheet操作完成')
        # -----多余sheet删除完毕
        # 最后让第一个sheet成为active sheet
        iw.sheets[0].activate()

        # 由于试了很多办法,都没有简便的办法先将sheet带格式复制出来再粘贴,只好反其道而行之,直接在源文件
        # 中做改变,然后将改变后的源文件另存为并关闭
        iw.save(r'E:\Documnet\工作\07_数据分析\临时文件\test' + '\\' + a[i] + '.xlsm')
        iw.close()
        # 到这里改造基本完成,但问题是由于源工作簿中有宏,所以必须另存为xlsm格式,xlwings暂时没找到直接另存为xlsx的办法
        # 因此只能用win32com模块调用excel本身的saveas接口进行另存为操作,正好发邮件也得用到win32com
        # --------------以下是另存为xlsx
        xlApp = win32.DispatchEx("Excel.Application")
        xlApp.Visible = False
        xlApp.DisplayAlerts = 0
        xlBook = xlApp.Workbooks.Open(r'E:\Documnet\工作\07_数据分析\临时文件\test' + '\\' + a[i] + '.xlsm', False)
        xlBook.SaveAs(r'E:\Documnet\工作\07_数据分析\临时文件\test' + '\\' + a[i] + '.xlsx', FileFormat=51)
        # 上面的FileFormat是调用了Excel本身的SaveAs接口,51就是xlsx的代码
        xlBook.Close(False)
        xlApp.Quit()
        # -------------另存为结束
        # ---------以上是获取工作簿并进行操作,以下是进行邮件操作,用到的模块为win32----------#
        outlook = win32.Dispatch('Outlook.Application')
        Mail_Item = outlook.CreateItem(0)
        Mail_Item.Recipients.Add('[email protected]')
        # 应先在邮箱通讯录中建立邮件组,直接输入邮件组名字可以发送
        Mail_Item.Subject = str(yesterday()) + '日报表' + a[i]
        Mail_Item.BodyFormat = 2
        Mail_Item.HtmlBody = '''
                             Hello Guys,
    This a Mail send by Jerry''' Mail_Item.Attachments.Add(r'E:\Documnet\工作\07_数据分析\临时文件\test' + '\\' + a[i] + '.xlsx') Mail_Item.Save() # ------------邮件发送完成----------------------------------------------------------# print(a[i] + '完成') app.quit() DSR(syb,1) DSR(ywb,2) DSR(yys,2) DSR(fjq,2)

你可能感兴趣的:(Python,Excel)