代码编织梦想

我写在了知乎,图片没有搬运过来。链接:https://zhuanlan.zhihu.com/p/371767402

需求:日常工作中,需要对Excel表格的数据进行筛选计算整理,然后将数据做成Word报告。Word报告的格式已经标准化,数据填入Word过程机械化、耗费时间。
目的:通过使用Python进行自动化生成Word极大提高效率。

实现效果如下:
1.Excel基础数据:
在这里插入图片描述

2.运行我们编译好的python程序,生成Word文档(通过pyinstaller将.py打包成为.exe程序):
在这里插入图片描述

3.查看自动生成的Word文档:
在这里插入图片描述

一、整体思路:

编程语言:Python3
开发工具:VsCode(推荐,开源、轻量,Pycharm也可以)
开发环境:Win10(64) 8G I5-4代
利用python关键库如下:
openpyxl:对Excel表格的sheet、行、列、单元格、样式进行操作
Docx:对Word文档的文本、段落、格式等进行操作
过程:1.openpyxl对Excel表格进行遍历,通过计算"提取"需要的数据。
2.借助Docx,创建Word,将数据"格式化"输出到Word中。
学习路线:pyhon语言简单易学,借助其强大的库完成我们的需求。建议学习Pyhon3基础语法,然后学习openpyxl库、Python-docx库。(预计学习半个月就可以做出来)。

二、详细需求:

现有Excel表格:水果的【订单信息】,里面包含订单号、归属地州、来源、类型、订单金额

自动生成的Word我们将实现:【难度不是很大,但总体上为对Excel数值进行遍历,还有格式化输出到Word中】
(1)生成Word的名称:名称+订单日期时段(获取日期区间)

图一
(2)Word内容-前段:
大标题(居中、加粗)
日期区间(excel计算获取)
正文内容:订单数量(统计excel数据)、涉及金额(excel数据:单位万元,保留小数点2位)、订单结果(excel数据)

图二
(3)Word内容-第一部分:标题(加粗)
正文内容:订单来源种类(excel数据统计)

图三
(4)Word-第二部分:标题(加粗)
正文内容:自动序号+地州+数量+金额(遍历excel地州列,统计该地州的订单数量,并统计该地州的订单金额)【excel中不存在的地州不会纳入为:XX州0件:0.00元】

图四

(4)Word-第三部分:标题(加粗)、按水果类型统计并在该类型下统计各地州该类型情况。(多嵌套条件遍历,且要求不会出现数量为0起的地州段落。)
小节标题:序号+水果类型+数量+类型总金额
正文内容:归属地州+当前类型+该类型地州数量+该类型地州金额+订单结果
样例:酸性水果类型下各地州分布:

图五
甜心水果类型按地州分布:

图六

三、实战环节:

进行开发需要提前了解Python基础语法、Openpyxl、Docx库的基本使用

(一)基础库导入(openpyxl、python-docx库安装办法自行百度)
from openpyxl import load_workbook #Excel操作:用于加载已经存在的excel
from openpyxl import Workbook #Excel操作:用于创建新的excel
from docx import Document #Word操作:导入Docx基础包
from docx.shared import Cm,Inches,Pt #Word操作:导入单位换算函数
from docx.oxml.ns import qn #Word操作:中文字体模块
from docx.enum.text import WD_ALIGN_PARAGRAPH #导入对齐选项
import time #导入时间,用来控制程序运行时间
(#:python的注释)
如上为实现目的所需要的库。

(二)加载数据
Excel文档中包含sheet1,sheet2,首先加载excel文档,再加载sheet工作簿
#路径
LoadPath=“订单信息.xlsx” #加载excel路径(这里为相对路径,excel表与该程序在同一文件夹下就能识别,所以只用excel文件名即可)
SavePath=“ResultTest.xlsx” #另存为路径(用于保存修改过的excel)
WordSavePath=‘订单周报.docx’ #word另存为路径(自动生成Word的路径)

#excel表格初始化
book=load_workbook(LoadPath)   #加载已有Excel文档
try:
    sheet=book['订单信息']     #加载需要的工作簿(这里为excel表中的sheet工作簿)
except:
    print('优先处理异常:\nExcel中工作簿(sheet)名称必须为:订单信息。请修改Excel后重新运行程序')     #输出提示信息
print("—————————\n抓取工作簿名称:",sheet.title)     #sheet.title为工作簿名称

(try-except:当try语句执行出现问题,将执行except中的语句。上述用来发现是否加载成功sheet工作簿,方便后面发现问题所在)

(三)初始化数据:
初始化需要的数据,对数据进行统一、清洗不需要的数据。
#(1)剔除首行:删除第一行不需要的标题
sheet.delete_rows(1) #删除行,()里面数据对应第几行,这里为第一行
print(‘执行剔除首行成功’) #用来在程序中表现执行成功
#book.save(SavePath) #save用来保存excel,相当于我们在excel表格中修改后进行另存为。这一步可以最后去做,这里被注释掉了不执行,取消注释进行测试是否删除成功。

#输出工作簿相关参数
print('订单数量:',sheet.max_row)    #程序输出工作簿总共有几行
print('最大列:',sheet.max_column)   #程序输出工作簿总共有几列

#(2)遍历列,更新列值。统一‘订单归属州’的参数
#大理白族自治州—>大理州
#迪庆藏族自治州—>迪庆州
#楚雄彝族自治州—>楚雄州
for cell in sheet['D']:       #D列为“订单归属州”
    if cell.value=='大理白族自治州':
        cell.value='大理州'
    elif cell.value=='迪庆藏族自治州':
        cell.value='迪庆州'
    elif cell.value=='楚雄彝族自治州':
        cell.value='楚雄州'
print('执行统一“地州”成功')
#book.save(SavePath)

(1)openpyxl的遍历方式有行遍历、列遍历,所以我们不需要首行标题的数据。

(2)有一列数据可能出现不同规范的值,比如:该列有的值为“大理州”,有的为“大理白族自治州”,表达的意思是同一个,我们需要统一为“大理州”。
对该列进行遍历,出现“大理白族自治州”的单元格变为“大理州”。

添加图片注释,不超过 140 字(可选)
(3)获得订单归属州的列表(云南省总共16个地州)
使用set的特性,python的set和其他语言类似, 是一个无序不重复元素集。将list转换为set再转换回list来完成。
#(3)获取表中所有归属地-地州列表:去重复
formatList=[] #中间list,用于保存所有地州值
for col in sheet[‘D’]: #D列为“订单归属州”
formatList.append(col.value)
GsPalceList=list(set(formatList)) #使用set的特性,python的set和其他语言类似, 是一个无序不重复元素集。将list转换为set再转换回来完成。
print(‘抓取归属地州清单:\n’,GsPalceList)
获得不重复的订单归属地州列表,有9个。该列表用于后面对地州进行分别遍历

(4)提取订单时间列的日期范围:
#(4)提取数据日期
DateList=[]
for cell in sheet[‘S’]: #S列为“发起订单时间”
DateList.append(cell.value) #保存所有订单时间的列表。

MinDate=min(DateList)    #获取订单时间最小日期
MinDateYear=MinDate[0:4] #获取最小日期年份
MinDateMonth=MinDate[5:7]#获取最小日期月份
MinDateDay=MinDate[8:10] #获取最小日期日份

MaxDate=max(DateList)    #获取订单时间最大日期
MaxDateYear=MaxDate[0:4] #获取最大日期年份
MaxDateMonth=MaxDate[5:7]#获取最大日期月份
MaxDateDay=MaxDate[8:10] #获取最大日期日份
print(f'抓取日期范围:{MinDateYear}年{MinDateMonth}月{MinDateDay}日-{MaxDateMonth}月{MaxDateDay}日')  #以 f 开头,包含的{}表达式在程序运行时会被表达式的值代替。

使用List中的min()、max()方法获取列表中的最大值和最小值,即获得了最小、大的日期。
再通过对字符串进行切片,获得年、月、日的参数。
比如:字符串“2021-04-04” 对其进行切片“[0:4]” 获得 “2021”。
print语句的关键用法:Python3.6新增了一种f-字符串格式化,以 f 开头,包含的{}表达式在程序运行时会被表达式的值代替。

(5)涉及订单金额和计算:
#(5)涉及订单金额计算
SumGoodsMoney=0 #订单金额之和
for col in sheet[‘L’]: #L列为“订单金额”
if(col.value!=“”): #排除xlsx单元格内空值
m=float(col.value) #将单元格转换为浮点类型才能进行求和
SumGoodsMoney=SumGoodsMoney+m #累加
print(f"涉及订单金额和:{SumGoodsMoney}")

#成功订单金额 
SumGoodsRefund=0          #成功收款金额之和
for col in sheet['W']:    #W列为“用券折扣后价格”
    if(col.value!=""):    #排除xlsx单元格内空值
        n=float(col.value)
        SumGoodsRefund=SumGoodsRefund+n    
print(f"成功收款金额和:{SumGoodsRefund}")

上述代码对“金额”列进行“求和”计算。
首先遍历金额列,Openpyxl对Excel遍历时,数据类型都为字符串类型,排除空字符串后,对遍历的单元格转换为Float浮点类型才能进行计算。

(6)统计订单不同结果的数量:
#(6)统计订单结果
SumResultInHand=0 #订单结果:发货中
SumResultBackout=0 #订单结果:订单撤销
SumResultSuccess=0 #订单结果:订单完成
SumResultRefuse=0 #订单结果:订单缺货
SumResultWPending=0 #订单结果:待付款
for col in sheet[‘V’]: #v列为订单结果列
if col.value==‘发货中’:
SumResultInHand+=1
elif col.value==‘订单撤销’:
SumResultBackout+=1
elif col.value==‘订单完成’:
SumResultSuccess+=1
elif col.value==‘订单缺货’:
SumResultRefuse+=1
elif col.value==‘待付款’:
SumResultWPending+=1

#订单来源统计:(美团在线订单、电话订单、小程序订单)
AppOnlineOrder=0   #水果APP在线订单
WeChetOrder=0      #小程序订单
PhoneOrder=0       #电话订单
AppVoice=0         #现场订单
SystemIn=0         #系统录入
for col in sheet['G']:
    if col.value=='水果APP在线订单':
        AppOnlineOrder+=1
    elif col.value=='小程序在线订单':
        WeChetOrder+=1
    elif col.value=='电话订单':
        PhoneOrder+=1
    elif col.value=='现场订单':
        AppVoice+=1
    elif col.value=='系统录入':
        SystemIn+=1

首先遍历列值,对单元格进行归类判断,得到不同类型订单的数量如何分布。

(四)格式化输出数据至Word文件中
(1)全局设置,另存文件名设置(实现”图一“的效果。)
#将内容格式化输出到文本中
#保存文件名更新:按获取的日期进行命名
WordSavePath=(f’水果订单销售报告_{MinDateYear}_{MinDateMonth}月{MinDateDay}日-{MaxDateMonth}月{MaxDateDay}日.docx’)
Word=Document() #创建空Word

#全局设置字体
Word.styles['Normal'].font.name=u'宋体'
Word.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')

#设置页面布局为A4纸张
section=Word.sections[0]
section.page_width = Cm(21) # 设置A4纸的宽度
section.page_height = Cm(29.7) # 设置A4纸的高度
print('导出Word页面的宽度和高度(A4):', section.page_width.cm,section.page_height.cm)

对Word进行全局设置(其他相关全局设置需要了解Python-docx库)

(2)首段内容(实现效果如"图二")
#首段
str1=Word.add_paragraph(style=None) #增加一个段落
str1_run=str1.add_run(‘云南省水果商城销售报告’) #增加文字块
str1_run.bold=True #加粗
str1_run.font.size=Pt(18) #行距
str1.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER#居中

str2=Word.add_paragraph(style=None)
str2_run=str2.add_run(f'{MinDateYear}年{MinDateMonth}月{MinDateDay}日-{MaxDateMonth}月{MaxDateDay}日\n')
str2_run.bold=True
str2_run.font.size=Pt(14)
str2.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER#居中

str3=Word.add_paragraph()
str3.paragraph_format.line_spacing=Pt(28)  #行距
str3.paragraph_format.first_line_indent=Cm(1.10)#首行缩进
#判断如果完成收款金额大于10000即用XX万元表示,如果小于10000即用XXXX元表示。
if SumGoodsRefund<10000:
    str3_run=str3.add_run(f'{MinDateYear}年{MinDateMonth}月{MinDateDay}日-{MaxDateMonth}月{MaxDateDay}日 24:00,水果商城平台共收到订单{sheet.max_row}起,涉及金额{"{:.2f}万元".format(SumGoodsMoney/10000)},订单完成收款金额{"{:.2f}元".format(SumGoodsRefund)},其中发货订单{SumResultInHand+SumResultSuccess}起(发货中{SumResultInHand}起,订单完成{SumResultSuccess}起),待受理{SumResultWPending}起,订单撤销{SumResultBackout}起,订单缺货{SumResultRefuse}起。')
else:
    str3_run=str3.add_run(f'{MinDateYear}年{MinDateMonth}月{MinDateDay}日-{MaxDateMonth}月{MaxDateDay}日 24:00,水果商城平台共收到订单{sheet.max_row}起,涉及金额{"{:.2f}万元".format(SumGoodsMoney/10000)},订单完成收款金额{"{:.2f}万元".format(SumGoodsRefund/10000)},其中发货订单{SumResultInHand+SumResultSuccess}起(发货中{SumResultInHand}起,订单完成{SumResultSuccess}起),待受理{SumResultWPending}起,订单撤销{SumResultBackout}起,订单缺货{SumResultRefuse}起。')
str3_run.font.name=(u'仿宋')#字体样式
str3_run._element.rPr.rFonts.set(qn('w:eastAsia'),u'仿宋')#字体样式
str3_run.font.size=Pt(14)#字体大小

开始引用Python-docx的方法进行格式化输出数据到Word中。“段落"包含"块”,我们要展示的数值放到”块“中。具体请学习Python-docx相关资料。
关键: Python3.6新增了一种f-字符串格式化,以 f 开头,包含的{}表达式在程序运行时会被表达式的值代替。我们可以对{}中的数据再进行计算。

(3)第一部分内容(实现效果如"图三")
#第一部分、按订单来源类型划分
str4=Word.add_paragraph()
str4.paragraph_format.line_spacing=Pt(28) #行距
str4.paragraph_format.first_line_indent=Cm(1.10)#首行缩进
#一、订单种类分布
str4_run=str4.add_run(‘一、水果订单种类分布:’)
str4_run.font.name=(u’黑体’)#字体样式
str4_run._element.rPr.rFonts.set(qn(‘w:eastAsia’),u’黑体’)#字体样式
str4_run.font.size=Pt(14)#字体大小
str4_run.font.bold=True

str5=Word.add_paragraph()
str5.paragraph_format.line_spacing=Pt(28)  #行距
str5.paragraph_format.first_line_indent=Cm(1.10)#首行缩进
str5_run=str5.add_run(f'“水果商城”平台“水果APP在线订单”{AppOnlineOrder}起,现场订单{AppVoice}起,电话订单{PhoneOrder}起,小程序订单{WeChetOrder}起,系统录入{SystemIn}起。')
str5_run.font.name=(u'仿宋')#字体样式
str5_run._element.rPr.rFonts.set(qn('w:eastAsia'),u'仿宋')#字体样式
str5_run.font.size=Pt(14)#字体大小

标题为一个"段落",正文内容为一个"段落"。“段落”进行设置行距,缩进之类的。
对"段落(Paragraph)“增加"块(run)”,我们的数据内容存放在"块”中,再对"块"进行设置样式(字体、大小、加粗等)。

(4)第二部分内容(实现效果如“图四”)
#第二部分、按订单地区划分统计:序号+地区+金额
str6=Word.add_paragraph()
str6.paragraph_format.line_spacing=Pt(28) #行距
str6.paragraph_format.first_line_indent=Cm(1.10)#首行缩进
str6_run=str6.add_run(‘二、水果订单地区分布’)
str6_run.font.name=(u’黑体’)#字体样式
str6_run._element.rPr.rFonts.set(qn(‘w:eastAsia’),u’黑体’)#字体样式
str6_run.font.size=Pt(14)#字体大小
str6_run.font.bold=True

#按地州进行遍历统计订单金额[难点]
for GsPalce in GsPalceList: #按前面获得的不重复地州列表进行遍历
    GsPalceSum=0#按州市金额总数初始化为:0
    GsPalceCount=0#按州市订单件数初始化为:0
    for gsd in sheet['D']:#遍历归属地州市,D列为归属地州市
        if GsPalce==gsd.value:#筛选当前归属地
            GsPalceCount+=1#当前归属地订单件数量
            if sheet.cell(row=gsd.row,column=(sheet['L1'].column)).value!="":#通过归属地单元格,获取归属地金额值,并排除空值
                m=float(sheet.cell(row=gsd.row,column=(sheet['L1'].column)).value)#单元格数据转换为浮点型,L列为金额列
                GsPalceSum+=m#该单元格计入当前归属地总金额           
    str7=Word.add_paragraph()
    str7.paragraph_format.line_spacing=Pt(28)  #行距
    str7.paragraph_format.first_line_indent=Cm(1.10)#首行缩进

    #序号+归属地
    str7_run=str7.add_run(f'{GsPalceList.index(GsPalce)+1}.{GsPalce}{GsPalceCount}件')#序号+归属地:进行加粗设置
    str7_run.font.name=(u'仿宋')#字体样式
    str7_run._element.rPr.rFonts.set(qn('w:eastAsia'),u'仿宋')#字体样式
    str7_run.font.size=Pt(14)#字体大小
    str7_run.bold=True#加粗首段州市文字

    #金额
    str7_run=str7.add_run(f':{"{:.2f}元".format(GsPalceSum)}')#州市统计的金额
    str7_run.font.name=(u'仿宋')#字体样式
    str7_run._element.rPr.rFonts.set(qn('w:eastAsia'),u'仿宋')#字体样式
    str7_run.font.size=Pt(14)#字体大小

难点解释:通过遍历列“归属地”这一列,筛选出与“归属地州列表”对应的值。比如遍历找到一个满足“大理州”的单元格,这个单元格的"行"我们就知道了,那么"金额列"是固定的。所以可以直接获得"大理州"的"金额"。
简单来说:
sheet.cell(row=gsd.row,column=(sheet[‘L1’].column)).value
gsd.row为当前归属地行,sheet[‘L1’].column为金额列
那么sheet.cell(row=gsd.row,column=(sheet[‘L1’].column)).value为:符合"归属地"的"金额"单元格值。
"行"已经遍历获得,"列"固定。即可使用Worksheet.cell(row=X,column=X)方法访问它的金额单元格值。

(5)第三部分内容(实现效果如“图五”)
1.先按类型条件遍历:满足该类型的行
2.再对这些行进行按地州条件遍历。
嵌套遍历
#三、水果类型分布
str8=Word.add_paragraph()
str8.paragraph_format.line_spacing=Pt(28) #行距
str8.paragraph_format.first_line_indent=Cm(1.10)#首行缩进
str8_run=str8.add_run(‘三、水果类型分布’)
str8_run.font.name=(u’黑体’)#字体样式
str8_run._element.rPr.rFonts.set(qn(‘w:eastAsia’),u’黑体’)#字体样式
str8_run.font.size=Pt(14)#字体大小
str8_run.font.bold=True

#根据类型的种类按地州进行小节统计
for Type in TypeList:
    TypeSum=0#类型总数量
    TypeMoneySum=0#类型总金额

    for cell in sheet['k']:#k列为商品类型列
        if Type==cell.value:
            TypeSum+=1
            if sheet.cell(row=cell.row,column=(sheet['L1'].column)).value!="":#通过该类型单元格,并取该行金额值
                m=float(sheet.cell(row=cell.row,column=(sheet['L1'].column)).value)#单元格数据转换为浮点型,L列为金额列
                TypeMoneySum+=m#该单元格计入当前类型总金额
    str9=Word.add_paragraph()
    str9.paragraph_format.line_spacing=Pt(28)  #行距
    str9.paragraph_format.first_line_indent=Cm(1.10)#首行缩进
    str9_run=str9.add_run(f'{TypeList.index(Type)+1}.{Type}{TypeSum}起,涉及金额{"{:.2f}元".format(TypeMoneySum)}:')#序号+类型+数量+金额
    str9_run.font.name=(u'仿宋')#字体样式
    str9_run._element.rPr.rFonts.set(qn('w:eastAsia'),u'仿宋')#字体样式
    str9_run.font.size=Pt(14)#字体大小
    str9_run.bold=True#加粗金额类型统计字段

    for TypeGsPalce in GsPalceList:
        TypeGsPalceSum=0#当前类型归属地按州市金额总数初始化为:0
        TypeGsPalceCount=0#当前类型按州市订单件数
        TypeumResultInHand=0#当前类型订单结果:发货中
        TypeSumResultBackout=0#当前类型订单结果:订单撤销
        TypeSumResultSuccess=0#当前类型订单结果:订单成功
        TypeSumResultRefuse=0#当前类型订单结果:订单缺货
        TypeSumResultWPending=0#当前类型订单结果:待付款
        for cell in sheet['D']:#遍历归属地州市
            if TypeGsPalce==cell.value and sheet.cell(row=cell.row,column=(sheet['k1']).column).value==Type:#筛选当前归属地,并且该行商品类型为当前类型
                TypeGsPalceCount+=1#当前归属地订单件数量
                if sheet.cell(row=cell.row,column=(sheet['L1'].column)).value!="":#通过归属地单元格,获取归属地金额值,并排除空值
                    m=float(sheet.cell(row=cell.row,column=(sheet['L1'].column)).value)#单元格数据转换为浮点型,L列为金额列
                    TypeGsPalceSum+=m#该单元格计入当前归属地总金额
                if sheet.cell(row=cell.row,column=(sheet['V1'].column)).value=="发货中":
                    TypeumResultInHand+=1
                elif sheet.cell(row=cell.row,column=(sheet['V1'].column)).value=="订单撤销":
                    TypeSumResultBackout+=1
                elif sheet.cell(row=cell.row,column=(sheet['V1'].column)).value=="订单完成":
                    TypeSumResultSuccess+=1
                elif sheet.cell(row=cell.row,column=(sheet['V1'].column)).value=="订单缺货":
                    TypeSumResultRefuse+=1
                elif sheet.cell(row=cell.row,column=(sheet['V1'].column)).value=="待付款":
                    TypeSumResultWPending+=1
        if TypeGsPalceCount==0:
            continue
        else:
            str10=Word.add_paragraph()
            str10.paragraph_format.line_spacing=Pt(28)  #行距
            str10.paragraph_format.first_line_indent=Cm(1.10)#首行缩进
            str10_run=str10.add_run(f'{TypeGsPalce}')#根据类型的种类按地州进行小节统计
            str10_run.font.name=(u'仿宋')#字体样式
            str10_run._element.rPr.rFonts.set(qn('w:eastAsia'),u'仿宋')#字体样式
            str10_run.font.size=Pt(14)#字体大小
            str10_run.font.bold=True
            str10_run=str10.add_run(f'{Type}{TypeGsPalceCount}起,涉及金额{"{:.2f}元".format(TypeGsPalceSum)},发货中{TypeumResultInHand}起,待付款{TypeSumResultWPending}起,订单撤销{TypeSumResultBackout}起,订单成功{TypeSumResultSuccess}起,订单缺货{TypeSumResultRefuse}起。')#根据类型的种类按地州进行小节统计
            str10_run.font.name=(u'仿宋')#字体样式
            str10_run._element.rPr.rFonts.set(qn('w:eastAsia'),u'仿宋')#字体样式
            str10_run.font.size=Pt(14)#字体大小
Word.save(WordSavePath)

五、完整代码
#!/usr/bin/env python3
#-- coding:utf-8 --
#author:贤仔
#e-mail:972912623@qq.com
from openpyxl import load_workbook
from openpyxl import Workbook#导入excel包
from docx import Document#导入Docx包
from docx.shared import Cm,Inches,Pt#导入单位换算函数
from docx.oxml.ns import qn#docx中文字体模块
from docx.enum.text import WD_ALIGN_PARAGRAPH#导入对齐选项
import time#导入时间

try:
#路径
LoadPath=“订单信息.xlsx” #加载excel路径(这里为相对路径,excel表与该程序在同一文件夹下就能识别,所以只用excel文件名即可)
SavePath=“ResultTest.xlsx”#另存为路径(用于保存修改过的excel)
WordSavePath=‘订单周报.docx’#word另存为路径(自动生成Word的路径)
#excel表格初始化
book=load_workbook(LoadPath)#加载已有Excel文档
try:
sheet=book[‘订单信息’]#加载需要的工作簿(这里为excel表中的sheet工作簿)
except:
print(‘优先处理异常:\nExcel中工作簿(sheet)名称必须为:订单信息。请修改Excel后重新运行程序’)
print(“————————————————————————————————————————————————————————\n抓取工作簿名称:”,sheet.title)#sheet.title为工作簿名称

#(1)剔除首行:删除第一行不需要的数据
sheet.delete_rows(1)    #删除行,()里面数据对应第几行,这里为第一行
print('执行剔除首行成功')#用来在程序中表现执行成功
#book.save(SavePath)

print('订单数量:',sheet.max_row)  #程序输出工作簿总共有几行
print('最大列:',sheet.max_column) #程序输出工作簿总共有几列

#(2)遍历列,更新列值。统一‘订单归属州’的参数
#大理白族自治州—>大理州
#迪庆藏族自治州—>迪庆州
#楚雄彝族自治州—>楚雄州
for cell in sheet['D']:     #D列为“订单归属州”
    if cell.value=='大理白族自治州':
        cell.value='大理州'
    elif cell.value=='迪庆藏族自治州':
        cell.value='迪庆州'
    elif cell.value=='楚雄彝族自治州':
        cell.value='楚雄州'
print('执行统一“地州”成功')
#book.save(SavePath)

#(3)获取表中所有归属地-地州列表:去重复
formatList=[]#中间list
for col in sheet['D']:#D列为“订单归属州”
    formatList.append(col.value)
GsPalceList=list(set(formatList)) #使用set的特型,python的set和其他语言类似, 是一个无序不重复元素集。将list转换为set再转换回来完成。
print('抓取归属地州清单:\n',GsPalceList)

#(4)提取数据日期
DateList=[]
for cell in sheet['S']:    #S列为“发起订单时间”
    DateList.append(cell.value)

MinDate=min(DateList)    #获取订单时间最小日期
MinDateYear=MinDate[0:4] #获取最小日期年份
MinDateMonth=MinDate[5:7]#获取最小日期月份
MinDateDay=MinDate[8:10] #获取最小日期日份

MaxDate=max(DateList)    #获取订单时间最大日期
MaxDateYear=MaxDate[0:4] #获取最大日期年份
MaxDateMonth=MaxDate[5:7]#获取最大日期月份
MaxDateDay=MaxDate[8:10] #获取最大日期日份
print(f'抓取日期范围:{MinDateYear}年{MinDateMonth}月{MinDateDay}日-{MaxDateMonth}月{MaxDateDay}日')#以 f 开头,包含的{}表达式在程序运行时会被表达式的值代替。

#(5)涉及订单金额计算
SumGoodsMoney=0#订单金额之和
for col in sheet['L']:#L列为“订单金额”
    if(col.value!=""):#排除xlsx单元格内空值
        m=float(col.value)
        SumGoodsMoney=SumGoodsMoney+m

print(f"涉及订单金额和:{SumGoodsMoney}")

#成功订单金额
SumGoodsRefund=0#成功收款金额之和
for col in sheet['W']:#W列为“用券折扣后价格”
    if(col.value!=""):#排除xlsx单元格内空值
        n=float(col.value)
        SumGoodsRefund=SumGoodsRefund+n    
print(f"成功收款金额和:{SumGoodsRefund}")

#(6)统计订单结果
SumResultInHand=0   #订单结果:发货中
SumResultBackout=0  #订单结果:订单撤销
SumResultSuccess=0  #订单结果:订单完成
SumResultRefuse=0   #订单结果:订单缺货
SumResultWPending=0 #订单结果:待付款
for col in sheet['V']:  #v列为订单结果列
    if col.value=='发货中':
        SumResultInHand+=1
    elif col.value=='订单撤销':
        SumResultBackout+=1
    elif col.value=='订单完成':
        SumResultSuccess+=1
    elif col.value=='订单缺货':
        SumResultRefuse+=1
    elif col.value=='待付款':
        SumResultWPending+=1

#订单来源统计:(美团在线订单、电话订单、小程序订单)
AppOnlineOrder=0#水果APP在线订单
WeChetOrder=0#小程序订单
PhoneOrder=0#电话订单
AppVoice=0#现场订单
SystemIn=0#系统录入
for col in sheet['G']:
    if col.value=='水果APP在线订单':
        AppOnlineOrder+=1
    elif col.value=='小程序在线订单':
        WeChetOrder+=1
    elif col.value=='电话订单':
        PhoneOrder+=1
    elif col.value=='现场订单':
        AppVoice+=1
    elif col.value=='系统录入':
        SystemIn+=1
#print('水果APP在线订单:{0},小程序在线订单:{1},电话订单:{2},现场订单:{3}'.format(AppOnlineOrder,WeChetOrder,PhoneOrder,AppVoice))

#*****************************************************************
#保存输出成为.Docx文件

#将内容格式化输出到文本中
WordSavePath=(f'水果订单销售报告_{MinDateYear}_{MinDateMonth}月{MinDateDay}日-{MaxDateMonth}月{MaxDateDay}日.docx')
Word=Document()

#全局设置字体
Word.styles['Normal'].font.name=u'宋体'
Word.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')

#设置A4纸张
section=Word.sections[0]
section.page_width = Cm(21) # 设置A4纸的宽度
section.page_height = Cm(29.7) # 设置A4纸的高度
print('导出Word页面的宽度和高度(A4):', section.page_width.cm,section.page_height.cm)

#首段
str1=Word.add_paragraph(style=None) #增加一个段落
str1_run=str1.add_run('云南省水果商城销售报告') #增加文字块
str1_run.bold=True          #加粗
str1_run.font.size=Pt(18)   #行距
str1.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER#居中

str2=Word.add_paragraph(style=None)
str2_run=str2.add_run(f'{MinDateYear}年{MinDateMonth}月{MinDateDay}日-{MaxDateMonth}月{MaxDateDay}日\n')
str2_run.bold=True
str2_run.font.size=Pt(14)
str2.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER#居中

str3=Word.add_paragraph()
str3.paragraph_format.line_spacing=Pt(28)  #行距
str3.paragraph_format.first_line_indent=Cm(1.10)#首行缩进
if SumGoodsRefund<10000:
    str3_run=str3.add_run(f'{MinDateYear}年{MinDateMonth}月{MinDateDay}日-{MaxDateMonth}月{MaxDateDay}日 24:00,水果商城平台共收到订单{sheet.max_row}起,涉及金额{"{:.2f}万元".format(SumGoodsMoney/10000)},订单完成收款金额{"{:.2f}元".format(SumGoodsRefund)},其中发货订单{SumResultInHand+SumResultSuccess}起(发货中{SumResultInHand}起,订单完成{SumResultSuccess}起),待受理{SumResultWPending}起,订单撤销{SumResultBackout}起,订单缺货{SumResultRefuse}起。')
else:
    str3_run=str3.add_run(f'{MinDateYear}年{MinDateMonth}月{MinDateDay}日-{MaxDateMonth}月{MaxDateDay}日 24:00,水果商城平台共收到订单{sheet.max_row}起,涉及金额{"{:.2f}万元".format(SumGoodsMoney/10000)},订单完成收款金额{"{:.2f}万元".format(SumGoodsRefund/10000)},其中发货订单{SumResultInHand+SumResultSuccess}起(发货中{SumResultInHand}起,订单完成{SumResultSuccess}起),待受理{SumResultWPending}起,订单撤销{SumResultBackout}起,订单缺货{SumResultRefuse}起。')
str3_run.font.name=(u'仿宋')#字体样式
str3_run._element.rPr.rFonts.set(qn('w:eastAsia'),u'仿宋')#字体样式
str3_run.font.size=Pt(14)#字体大小

#第一部分、按订单来源类型划分
str4=Word.add_paragraph()
str4.paragraph_format.line_spacing=Pt(28)  #行距
str4.paragraph_format.first_line_indent=Cm(1.10)#首行缩进
#一、订单种类分布
str4_run=str4.add_run('一、水果订单种类分布:')
str4_run.font.name=(u'黑体')#字体样式
str4_run._element.rPr.rFonts.set(qn('w:eastAsia'),u'黑体')#字体样式
str4_run.font.size=Pt(14)#字体大小
str4_run.font.bold=True

str5=Word.add_paragraph()
str5.paragraph_format.line_spacing=Pt(28)  #行距
str5.paragraph_format.first_line_indent=Cm(1.10)#首行缩进
str5_run=str5.add_run(f'“水果商城”平台“水果APP在线订单”{AppOnlineOrder}起,现场订单{AppVoice}起,电话订单{PhoneOrder}起,小程序订单{WeChetOrder}起,系统录入{SystemIn}起。')
str5_run.font.name=(u'仿宋')#字体样式
str5_run._element.rPr.rFonts.set(qn('w:eastAsia'),u'仿宋')#字体样式
str5_run.font.size=Pt(14)#字体大小

#第二部分、按订单地区划分统计:序号+地区+金额
str6=Word.add_paragraph()
str6.paragraph_format.line_spacing=Pt(28)  #行距
str6.paragraph_format.first_line_indent=Cm(1.10)#首行缩进
str6_run=str6.add_run('二、水果订单地区分布')
str6_run.font.name=(u'黑体')#字体样式
str6_run._element.rPr.rFonts.set(qn('w:eastAsia'),u'黑体')#字体样式
str6_run.font.size=Pt(14)#字体大小
str6_run.font.bold=True
#按地州进行遍历统计订单金额
for GsPalce in GsPalceList: #按前面获得的不重复地州列表进行遍历
    GsPalceSum=0#按州市金额总数初始化为:0
    GsPalceCount=0#按州市订单件数初始化为:0
    for gsd in sheet['D']:#遍历归属地州市,D列为归属地州市
        if GsPalce==gsd.value:#筛选当前归属地
            GsPalceCount+=1#当前归属地订单件数量
            if sheet.cell(row=gsd.row,column=(sheet['L1'].column)).value!="":#通过归属地单元格,获取归属地金额值,并排除空值
                m=float(sheet.cell(row=gsd.row,column=(sheet['L1'].column)).value)#单元格数据转换为浮点型,L列为金额列
                GsPalceSum+=m#该单元格计入当前归属地总金额           
    str7=Word.add_paragraph()
    str7.paragraph_format.line_spacing=Pt(28)  #行距
    str7.paragraph_format.first_line_indent=Cm(1.10)#首行缩进

    #序号+归属地
    str7_run=str7.add_run(f'{GsPalceList.index(GsPalce)+1}.{GsPalce}{GsPalceCount}件')#序号+归属地:进行加粗设置
    str7_run.font.name=(u'仿宋')#字体样式
    str7_run._element.rPr.rFonts.set(qn('w:eastAsia'),u'仿宋')#字体样式
    str7_run.font.size=Pt(14)#字体大小
    str7_run.bold=True#加粗首段州市文字
    #金额
    str7_run=str7.add_run(f':{"{:.2f}元".format(GsPalceSum)}')#州市统计的金额
    str7_run.font.name=(u'仿宋')#字体样式
    str7_run._element.rPr.rFonts.set(qn('w:eastAsia'),u'仿宋')#字体样式
    str7_run.font.size=Pt(14)#字体大小

#第三部分、按类型划分
TypeList=['酸性水果','亚酸性水果','甜性水果','温热性水果','凉性水果']#订单类型列表

#三、水果类型分布
str8=Word.add_paragraph()
str8.paragraph_format.line_spacing=Pt(28)  #行距
str8.paragraph_format.first_line_indent=Cm(1.10)#首行缩进
str8_run=str8.add_run('三、水果类型分布')
str8_run.font.name=(u'黑体')#字体样式
str8_run._element.rPr.rFonts.set(qn('w:eastAsia'),u'黑体')#字体样式
str8_run.font.size=Pt(14)#字体大小
str8_run.font.bold=True

#根据类型的种类按地州进行小节统计
for Type in TypeList:
    TypeSum=0#类型总数量
    TypeMoneySum=0#类型总金额
    for cell in sheet['k']:#k列为商品类型列
        if Type==cell.value:
            TypeSum+=1
            if sheet.cell(row=cell.row,column=(sheet['L1'].column)).value!="":#通过该类型单元格,并取该行金额值
                m=float(sheet.cell(row=cell.row,column=(sheet['L1'].column)).value)#单元格数据转换为浮点型,L列为金额列
                TypeMoneySum+=m#该单元格计入当前类型总金额
    str9=Word.add_paragraph()
    str9.paragraph_format.line_spacing=Pt(28)  #行距
    str9.paragraph_format.first_line_indent=Cm(1.10)#首行缩进
    str9_run=str9.add_run(f'{TypeList.index(Type)+1}.{Type}{TypeSum}起,涉及金额{"{:.2f}元".format(TypeMoneySum)}:')#序号+类型+数量+金额
    str9_run.font.name=(u'仿宋')#字体样式
    str9_run._element.rPr.rFonts.set(qn('w:eastAsia'),u'仿宋')#字体样式
    str9_run.font.size=Pt(14)#字体大小
    str9_run.bold=True#加粗金额类型统计字段
    for TypeGsPalce in GsPalceList:
        TypeGsPalceSum=0#当前类型归属地按州市金额总数初始化为:0
        TypeGsPalceCount=0#当前类型按州市订单件数
        TypeumResultInHand=0#当前类型订单结果:发货中
        TypeSumResultBackout=0#当前类型订单结果:订单撤销
        TypeSumResultSuccess=0#当前类型订单结果:订单成功
        TypeSumResultRefuse=0#当前类型订单结果:订单缺货
        TypeSumResultWPending=0#当前类型订单结果:待付款
        for cell in sheet['D']:#遍历归属地州市
            if TypeGsPalce==cell.value and sheet.cell(row=cell.row,column=(sheet['k1']).column).value==Type:#筛选当前归属地,并且该行商品类型为当前类型
                TypeGsPalceCount+=1#当前归属地订单件数量
                if sheet.cell(row=cell.row,column=(sheet['L1'].column)).value!="":#通过归属地单元格,获取归属地金额值,并排除空值
                    m=float(sheet.cell(row=cell.row,column=(sheet['L1'].column)).value)#单元格数据转换为浮点型,L列为金额列
                    TypeGsPalceSum+=m#该单元格计入当前归属地总金额
                if sheet.cell(row=cell.row,column=(sheet['V1'].column)).value=="发货中":
                    TypeumResultInHand+=1
                elif sheet.cell(row=cell.row,column=(sheet['V1'].column)).value=="订单撤销":
                    TypeSumResultBackout+=1
                elif sheet.cell(row=cell.row,column=(sheet['V1'].column)).value=="订单完成":
                    TypeSumResultSuccess+=1
                elif sheet.cell(row=cell.row,column=(sheet['V1'].column)).value=="订单缺货":
                    TypeSumResultRefuse+=1
                elif sheet.cell(row=cell.row,column=(sheet['V1'].column)).value=="待付款":
                    TypeSumResultWPending+=1
        if TypeGsPalceCount==0:
            continue
        else:
            str10=Word.add_paragraph()
            str10.paragraph_format.line_spacing=Pt(28)  #行距
            str10.paragraph_format.first_line_indent=Cm(1.10)#首行缩进
            str10_run=str10.add_run(f'{TypeGsPalce}')#根据类型的种类按地州进行小节统计
            str10_run.font.name=(u'仿宋')#字体样式
            str10_run._element.rPr.rFonts.set(qn('w:eastAsia'),u'仿宋')#字体样式
            str10_run.font.size=Pt(14)#字体大小
            str10_run.font.bold=True
            str10_run=str10.add_run(f'{Type}{TypeGsPalceCount}起,涉及金额{"{:.2f}元".format(TypeGsPalceSum)},发货中{TypeumResultInHand}起,待付款{TypeSumResultWPending}起,订单撤销{TypeSumResultBackout}起,订单成功{TypeSumResultSuccess}起,订单缺货{TypeSumResultRefuse}起。')#根据类型的种类按地州进行小节统计
            str10_run.font.name=(u'仿宋')#字体样式
            str10_run._element.rPr.rFonts.set(qn('w:eastAsia'),u'仿宋')#字体样式
            str10_run.font.size=Pt(14)#字体大小
Word.save(WordSavePath)

except:
print(‘\n注意!\n确保系统导出的Excel文件已经放到与该程序同目录下,重新运行该程序(文件必须为:订单信息.xlsx)\nauthor:贤仔 e-mail:972912623@qq.com’)
#显示倒计时
for i in range(0,10):
print(f’\r系统{10-i}秒后自动退出。',end=“”)
time.sleep(1)

else:
print(‘————————————————————————————————————————————————————————\n\nauthor:贤仔 e-mail:972912623@qq.com\n执行成功,请查看该目录下:订单数据报告.docx’)
for i in range(0,10):
print(f’\r系统{10-i}秒后自动退出。',end=“”)
time.sleep(1)
六、打包生成.exe程序
首先需要安装pyinstaller:
pip install pyinstaller
使用命令:pyinstaller -F -w *.py(星号是.py的全部路径)
pyinstaller -F *.py
成功后会提示:Successfully installed pyinstaller-x.x.x
在该目录下获得.exe程序,后续直接运行就能得到我们想要的word了。

感谢阅读,给我一个点赞+收藏。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_43485999/article/details/130429421