写这段代码的起因是因为公司要下发一个销售日报表,但是由于不同部门的销售区域老总看到的只能是自己区域的数据,每个工作簿有10多个工作表,如果靠手工来做工作量极大,因此,有迫切的需求要对报表做一些自动化操作,并能自动发送邮件。在做到以上的基础上,还要顾及美观。
表格的截图如下:
可以看到这个表格处理的几个关键点:
1、有组合,要取消组合,目前python还没法实现(我没找到办法),所以必须要调用VBA
2、有隐藏的sheet,其实是3级组合,要展示出来
3、要根据2级组合里的区域,分别发给对应的区域
4、要保留原格式
import xlwings as xw
import win32com.client as win32
程序运行的时候不显示在前端,也不新建Excel窗口
app = xw.App(visible=False,add_book=False)
ywb = ["浙江部","河南部","KA部","黑龙江部","广西部","江西部","海南部","广东部"]
for i in range(0,len(ywb)): # 这是循环的开始,读取业务部的数量,每个业务部作为一个最大的循环
# print(i)
iw = app.books.open(r'E:\Documnet\工作\07_数据分析\临时文件\副本.xlsm')
sheet_count = iw.sheets.count # 读取整个工作簿的sheet数量
# 由于源文件里还有很多隐藏的工作表不可以下发给各区域,而所有文件格式都#是一样,因此这里定义了一个函数sheet_names(),
用来读取第一个“全品项销额”在工作表中的位置,并以此作为下发给区域的第一个表格
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
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,2).value == ywb[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,2).value == ywb[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()
# -----多余sheet删除完毕
# 最后让第一个sheet成为active sheet
iw.sheets[0].activate()
# 由于试了很多办法,都没有简便的办法先将sheet带格式复制出来再粘贴,只好反其道而行之,直接在源文件
# 中做改变,然后将改变后的源文件另存为并关闭
iw.save(r'E:\Documnet\工作\07_数据分析\临时文件\test' + '\\' + ywb[i] + '.xlsm')
iw.close()
# 到这里改造基本完成,但问题是由于源工作簿中有宏,所以必须另存为xlsm格式,xlwings暂时没找到直接另存为xlsx的办法
# 因此只能用win32com模块调用excel本身的saveas接口进行另存为操作,正好发邮件也得用到win32com
# --------------以下是另存为xlsx
xlApp = win32.DispatchEx("Excel.Application")
xlApp.Visible = True
xlApp.DisplayAlerts = 0
xlBook = xlApp.Workbooks.Open(r'E:\Documnet\工作\07_数据分析\临时文件\test' + '\\' + ywb[i] + '.xlsm', False)
xlBook.SaveAs(r'E:\Documnet\工作\07_数据分析\临时文件\test' + '\\' + ywb[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('zhuce@cuixiaoyuan.cn')
# 应先在邮箱通讯录中建立邮件组,直接输入邮件组名字可以发送
Mail_Item.Subject = 'zhuce@cuixiaoyuan.cn'
Mail_Item.BodyFormat = 2
Mail_Item.HtmlBody = '''
Hello Guys,<br/>
This a Mail send by Jerry'''
Mail_Item.Attachments.Add(r'E:\Documnet\工作\07_数据分析\临时文件\test' + '\\' + ywb[i] + '.xlsx')
Mail_Item.Save()
#------------邮件发送完成----------------------------------------------------------#
print(ywb[i]+'完成')
app.quit()