好几个月没有写笔记了, 并非没有积累, 而是有点懒了. 想想还是要续上, 作为工作成长的一部分哦.

最近有做一些报表, 但一直找不到一个合适的报表工具, 又实在不想写前端, 后端... 思来想去, 感觉 Excel 就一定程度上能做可视化的, 除了不能动态交互外, 其他都挺好. 今天分享的就是一个关于如何用 Py 来自动化Excel 报表, 解放双手, 提高工作效率哦.

总体解决方案

输出报表

当然是测试用的假数据啦.

自动化Py脚本

基本思路:
1. 准备模板数据需要的 SQL
2. 用 Pandas 连接 数据库 并执行 SQL, 返回 DataFrame
3. 用 Xlwings 直接打开 Excel, 并将这些 DataFrame 填充到 写死的 单元格
4. 保存并退出

具体代码如下哦:

import pandas as pd 
import xlwings as xw
import pymssql


# 各品类月同期 
def get_last_year_sale(start_date, end_date):
  """各品类同期销量, 对比19年"""
  sql_01 = f"""
  SELECT 
   品类
   , SUM(数量) AS QTY
  FROM XXX
  WHERE 是否电商 = 1 
   AND 销售时间 BETWEEN DATEADD(YEAR, -2, '{start_date}') AND DATEADD(YEAR, -2, '{end_date}')   
  GROUP BY 品类
  """
  df = pd.read_sql(sql_01, con=con)
  df_xtc = df[df['品类'] == 'A品类'][['品类', 'QTY']]
  df_bbk = df[df['品类'] == 'B品类'][['品类', 'QTY']]
  return df_xtc, df_bbk 
  
def get_anget_sale(start_date, end_date):
    """返回各品类, 各区域的时间段销量"""
    sql = f"""
    SELECT 
     品类
     , AGENT
     , SUM(数量) AS QTY
     , ROW_NUMBER()OVER(PARTITION BY 品类 ORDER BY SUM(数量) DESC) MY_RANK
    FROM XXX
    WHERE 是否电商 = 1 
     AND 销售时间 BETWEEN '{start_date}' AND '{end_date}'
    GROUP BY AGENT, 品类
    """
    df = pd.read_sql(sql, con=con)
    df_xtc = df[df['品类'] == 'A品类'][['AGENT', 'QTY']]
    df_bbk = df[df['品类'] == 'B品类'][['AGENT', 'QTY']]
    df_pad = df[df['品类'] == 'C品类'][['AGENT', 'QTY']]

    return df_xtc, df_bbk, df_pad 
  
def get_machine_sale(start_date, end_date):
  """返回各品类, 各区域的时间段销量"""
  sql = f"""
  SELECT 
   品类
   , 机型
   , SUM(数量) AS QTY
   , ROW_NUMBER()OVER(PARTITION BY 品类 ORDER BY SUM(数量) DESC) MY_RANK
  FROM V_REALSALE
  WHERE 是否电商 = 1 
   AND 销售时间 BETWEEN '{start_date}' AND '{end_date}'
  GROUP BY 机型, 品类
  """
  df = pd.read_sql(sql, con=con)
  df_xtc = df[df['品类'] == 'A品类'][['机型', 'QTY']]
  df_bbk = df[df['品类'] == 'B品类'][['机型', 'QTY']]

  return df_xtc, df_bbk 


# main 
con = pymssql.connect('xxxxx', 'sxxx', 'xxxxxx', 'xxxxx')

# 基础配置: 根据用户输入当前日期, 输出当月, 当季度第一天 
print("欢迎哦, 此小程序专门为XX看板做数据自动更新呢~")
print()

today = input("请输入截止日期(昨天), 形如: 2021/5/20 按回车结束:  ")

if len(today.split('/')) != 3:
  raise "日期格式输入错误!!, 请按照形如 '2021/5/20'的格式重新输入"
else:
  m_cur = today.split('/')[1]
  m_first_day = '2021/' + m_cur + '/1'

# 季度第一天 
if m_cur in ('1', '01', '2', '02', '3', '03'):
  q_time_start = '2021/1/1'
  
elif m_cur in ('4', '04', '5', '05', '6', '06'):
  q_time_start = '2021/4/1'
  
elif m_cur in ('7', '07', '8', '08', '9', '09'):
  q_time_start = '2021/7/1'
else:
  q_time_start = '2021/10/1'

print()
print("正在开始更新....")
print("提示, 接下看到闪退, 是正常现象, 就程序模拟人去打开文件, 填充数据, 不要紧张哦~~~")

# 去年月, 季度同期 
df_mm_xtc, df_mm_bbk = get_last_year_sale(m_first_day, today)
df_qq_xtc, df_qq_bbk = get_last_year_sale(q_time_start, today)

# 当月各地区累积销量
df_m_xtc, df_m_bbk, df_m_pad = get_anget_sale(m_first_day, today)

# 各地区当季度销量 
df_q_xtc, df_q_bbk, df_q_pad = get_anget_sale(q_time_start, today)

# 各机型当季度销量 
df_q_type_xtc, df_q_type_bbk = get_machine_sale(q_time_start, today) 
# 过滤掉 销量为0的型号 
df_q_type_xtc = df_q_type_xtc[df_q_type_xtc.QTY > 0]
df_q_type_xtc.replace('Z6á?·?°?', 'Z6巅峰版', inplace=True)

df_q_type_bbk = df_q_type_bbk[df_q_type_bbk.QTY > 0]

# 打开excel 模板 等待数据填充 
app = xw.App(visible=True, add_book=False)

app.display_alerts = False  # 关闭一些提示信息,可以加快运行速度。 默认为 True。
app.screen_updating = True

wb = app.books.open("XXX_全品类_看板.xlsx")
data_sht = wb.sheets['数据']

# 19年当月同期销量
data_sht.range('B9').value = df_mm_xtc.values
data_sht.range('G9').value = df_mm_bbk.values

# 当季度同比
data_sht.range('B10').value = df_qq_xtc.values
data_sht.range('G10').value = df_qq_bbk.values

# 填充各品类当月销量, 注意单元格是写死的哦
data_sht.range('I72').value = df_m_xtc.values
data_sht.range('T72').value = df_m_bbk.values
data_sht.range('AE72').value = df_m_pad.values

# 填充当季度销量, 同理是写死的
data_sht.range('A54').value = df_q_xtc.values
data_sht.range('F54').value = df_q_bbk.values
data_sht.range('K54').value = df_q_pad.values

# 填充当季度各型号, 同理是写死的
data_sht.range('A21').value = df_q_type_xtc.values
data_sht.range('F21').value = df_q_type_bbk.values

wb.save()
app.quit()

print()
print("~~更新结束了哦~~")
print()
input("请按任意键退出~~")
print()
print('BYE~~ 人生若只如初见呢~~')

打包 EXE 桌面小程序

最好用一个纯净的 虚拟环境打包.

终端命令: python -m venv 虚拟环境名称

然后进入脚本目录下, 进行打包哦.

pyinstaller main.py -F

打包成功后的样子.

双击运行即可哦.

这时候再重新打开该目录下的 Excel 模板, 发现数据已经自动更新了.

我现在真的感受到, 用开发的思维做一些脚本工具, 真的会极大提高我现在当文员的很多重复性工作哦!

以上就是Python 实现自动化Excel报表的步骤的详细内容,更多关于python 自动化Excel报表的资料请关注程序员的世界其它相关文章!

Python 实现自动化Excel报表的更多相关文章

  1. python基于pexpect库自动获取日志信息

    1. 前言对大部分的人来说,解决 Bug 都是依靠关键字去日志去定位问题!在调试情况下,我们可以实时在控制台查看日志;但对于部署到服务器上的应用,日志都存放在服务器某个目录下,没法通过本地查看到这种情况下,就需要我们先登录服务器,然后进入到日志目录文件夹,最后通过日志文件去定位问题;如果涉及到 K8......

  2. Python学习(1) (python特点、优缺点)

    Python学习(1)一、python的特点二、python的优缺点1.优点2.缺点三、python源程序的基本概念一、python的特点1. python 是完全面向对象的语言 函数、模块、数字、字符串都是对象,在python中一切皆为对象完全支持继承、重载、多重继承支持重载运算符,也支持泛型设计......

  3. python如何实现单向链表及单向链表的反转

    链表的定义链表中的每个节点会存储相邻节点的位置信息,单链表中的每个节点只存储下一关节点的位置信息单向链表的实现class ListNode:def __init__(self, val):self.val = valself.next = None要实现单向链表只需要把几个节点关联起来就可以了,把一......

  4. Python统计列表元素出现次数的方法

    1. 引言在使用Python的时候,通常会出现如下场景:array = [1, 2, 3, 3, 2, 1, 0, 2]获取array中元素的出现次数比如,上述列表中:0出现了1次,1出现了2次,2出现了3次,3出现了2次。本文阐述了Python获取元素出现次数的几种方法。点击获取完整代码。2. 方......

  5. python中re模块知识点总结

    一、什么是正则表达式?正则表达式,又称规则表达式,通常被用来检索、替换那些符合某个模式(规则)的文本。正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。二、正则表达式的匹配规则1.表......

  6. opencv python 对指针仪表读数识别的两种方式

    我尝试了两种方式用opencv 对指针仪表进行读数识别,1. 先模板匹配,然后边缘检测 + 霍夫直线2. 按轮廓大小过滤,然后边缘检测 + 霍夫直线两种方式对光线都非常敏感其中第一种的应用范围更广,背景复杂一点也能识别到个人比较喜欢这种方式第二种的限制多一点,对背景、光线条件要求比较高对于固定位置,......

  7. Python基础(上篇)

    本篇文章主要内容:变量、注释、运算符、关键字、数据类型。本篇文章主要内容:变量、注释、运算符、关键字、数据类型。在入手变量之前我们先来看看经典的编程语句 → hello world 在python3中输出到控制台的函数是print()print("hello world") 一、......

  8. python 爬取知乎回答下的微信8.0状态视频

    微信 8.0 版本更新后,可以设置个人状态,状态里面可以添加火录制视频,很快状态视频就火了,可以看下知乎热榜有没有微信8.0状态沙雕又可爱的视频或图片?[1]。比如我也设置了一个:于是我就想把这些视频下载下来,也玩一玩。本文讲述如何使用 Python 一键下载知乎某个回答下的所有视频。思路:分析知乎......

  9. python 实现客户端与服务端的通信

    函数介绍Socket对象方法:服务端:函数描述.bind()绑定地址关键字,AF_INET下以元组的形式表示地址。常用bind((host,port)).listen()监听TCP,可以挂起的最大连接数,该值至少为1,一般设为5即可.accept()被动接受TCP客户端的连接客户端:函数描述.con......

  10. 利用python+request通过接口实现人员通行记录上传功能

    前言:脚本中包含以下几点常用功能:(1)实时获取当前时间(2)while循环提交(3)上传图片文件一、上述功能解释:(1)实时获取当前时间,下面展示三种格式化后的日期代码示例#!/usr/bin/python# -*- coding: UTF-8 -*-import time# 格式化成2016-0......

随机推荐

  1. 在nodejs中创建child process

    目录简介child process异步创建进程同步创建进程在nodejs中创建child process简介nodejs的main event loop是单线程的,nodejs本身也维护着Worker Pool用来处理一些耗时的操作,我们还可以通过使用nodejs提供的worker_threads来......

  2. 使用JS调用WebService接口

    <> $(document).ready(function () { var username = "admin"; var password = "123456";       /*==J......

  3. 前后端(PHP)使用AES对称加密

    前端代码:// 这个是加密用的 function encrypt(text){ var key = CryptoJS.enc.Utf8.parse('1234567890654321'); //为了避免补位,直接用16位的秘钥 var iv = CryptoJS.enc.Utf8.parse('12......

  4. python中yield的用法详解

    首先我要吐槽一下,看程序的过程中遇见了yield这个关键字,然后百度的时候,发现没有一个能简单的让我懂的,讲起来真TM的都是头头是道,什么参数,什么传递的,还口口声声说自己的教程是最简单的,最浅显易懂的,我就想问没有有考虑过读者的感受。接下来是正题:首先,如果你还没有对yield有个初步分认识,那么......

  5. Android事件分发机制全面解析

    事件分发机制事件分发机制的两个阶段:分发:事件从父视图往子视图分发,被拦截后不再传递,进入回溯阶段回溯:事件从子视图往父视图回溯,被消费后不再回溯关键方法:ViewGroup.dispatchTouchEvent 往子视图分发事件ViewGroup.onInterceptTouchEvent 返回 ......

  6. JavaScript中的Proxy对象

    Js中Proxy对象Proxy对象用于定义基本操作的自定义行为,例如属性查找、赋值、枚举、函数调用等。语法const proxy = new Proxy(target, handler);target: 要使用Proxy包装的目标对象,可以是任何类型的对象,包括原生数组,函数,甚至另一个代理。han......

  7. 关于Java下奇怪的Base64详解

    下面这一段代码中会报错。?123456789101112131415161718192021222324252627282930313233package jiangbo.java.lang;import java.io.IOException;import java.nio.charset.Cha......

  8. python shell 根据 ip 获取 hostname

    python shell 根据 ip 获取 hostname 或根据 hostname 获取 ip前言笔者有时候需要根据hostname获取ip 比如根据machine.company.com 获得ip 10.173.14.117本文地址 http://blog.csdn.net/never_cxb......

  9. MyBatis中的表关联查询实现示例

    Mybatis中的一对多对象关联查询查询模拟情景,商品与商品详情:一件商品可以对应多个商品详情信息,即从商品?商品详情方向看,属于一对多。 在一对多关系中,需要在属于一的一方的实体类中添加多的一方的集合,一般为List<>类型//(省去了get和set的方法)public class G......

  10. PHP实现大文件断点下载

    什么是断点续传下载?就是下载文件时,不必重头开始下载,而是从指定的位置继续下载,这样的功能就做断点续传下载。断点续传的理解可以分为两部分:一部分是断点,一部分是续传下载。断点的由来是在下载过程中,将一个下载文件分成了多个部分,同时进行多个部分一起的下载,当某个时间点,任务被暂停了或因网络原因断网、或......