• 需求介绍
  • 数据示例
  • 为每个房间都生成一个excel
  • 使用xlwings调整结果
  • xlwings简介
  • 开始使用
  • 设置自动列宽和行高
  • 设置边框
  • 生成图片
  • 完整代码实现
  • 打包成exe文件
  • 使用效果

用python为每个房间生成一个房租单

需求介绍

房东将整栋楼各房间的应缴房租详情用一个excel表记录了下来,现在需要给每个房间都以图片或excel表形式发送一个房租单。

本程序需要做的事就是,将一个excel按照指定字段分组,分解成n个不同的excel文件,然后再对这些excel文件,批量的调整列宽,设置边框,再截图并保存图片。

数据示例

import pandas as pd

import pandas as pd
data = pd.read_excel(r"F:/pandas/item_img/dist/15栋6月单.xlsx", header=1)
data.head()

data = pd.read_excel(r"F:/pandas/item_img/dist/15栋6月单.xlsx", header=1)
data.head()

房号

本月水度

上月水度

实用+1

水价

水费

上月电数

本月电数

实用+2

电价

电费

物业费

宽带费

房租

其它

合计

0

201

327.0

323.0

5.0

7.0

35

4843.0

4915.0

74.0

1.4

103.6

20.0

NaN

340

NaN

498.6

1

206

285.0

280.0

6.0

7.0

42

3719.0

3756.0

39.0

1.4

54.6

20.0

NaN

380

NaN

496.6

2

207

197.0

195.0

3.0

7.0

21

4041.0

4195.0

156.0

1.4

218.4

20.0

50.0

360

NaN

669.4

3

208

204.0

204.0

1.0

7.0

7

2585.0

2586.0

3.0

1.4

4.2

20.0

50.0

350

NaN

462.0

4

302

258.0

257.0

2.0

7.0

14

654.0

677.0

25.0

1.4

35.0

20.0

50.0

260

NaN

379.0

为每个房间都生成一个excel

现在我将结果放入result的子文件夹中,示例:

for field, df in data.head().groupby('房号'):
    print(field)
    df.to_excel(f"F:/pandas/item_img/dist/result/{field}.xlsx", index=False)

for field, df in data.head().groupby('房号'):
    print(field)
    df.to_excel(f"F:/pandas/item_img/dist/result/{field}.xlsx", index=False)
201
206
207
208
302

使用xlwings调整结果

xlwings简介

xlwings包括以下4个模块:

  • 「Scripting」: 使用接近VBA的语法从Python自动化/与Excel交互。
  • 「Macros」: 用干净而强大的Python代码替换VBA宏。
  • 「UDFs」: 在Python中编写用户定义函数(UDF)(仅限Windows)。
  • 「REST API」: 通过REST API操作Excel工作簿。

xlwings本质上只是Windows上Pywin32和Mac上appscript的智能包装,可以通过调用api属性来访问基础对象。

参考文档:

https://www.kancloud.cn/gnefnuy/xlwings-docs/1127474

开始使用

先导包:

import xlwings as xw

import xlwings as xw
# 打开office的excel组件,设置应用为不可见
app = xw.App(visible=False, add_book=False)  # 设置应用,关闭可视化,取消添加book
app

# 打开office的excel组件,设置应用为不可见
app = xw.App(visible=False, add_book=False)  # 设置应用,关闭可视化,取消添加book
app

为了提升处理速度,设置两个参数:

# 将此属性设置为false可在代码运行时抑制提示和警报消息;当消息需要响应时,Excel将选择默认响应。
app.display_alerts = False
# 关闭屏幕更新,可视模式下将无法看到执行情况,需要看的时候重新设置为True即可
app.screen_updating=False

# 将此属性设置为false可在代码运行时抑制提示和警报消息;当消息需要响应时,Excel将选择默认响应。
app.display_alerts = False
# 关闭屏幕更新,可视模式下将无法看到执行情况,需要看的时候重新设置为True即可
app.screen_updating=False

上面两个参数,先关闭的警告,让excel自动选择默认的,再关闭了屏幕自动更新,执行效率就提高了

以201房间的房租单为例,演示如何调整excel文件样式:

wb = app.books.open("F:/pandas/item_img/dist/result/201.xlsx")  # 打开文件
wb

wb = app.books.open("F:/pandas/item_img/dist/result/201.xlsx")  # 打开文件
wb

读取活动表格:

ws = wb.sheets.active
ws

ws = wb.sheets.active
ws

获取表格的数据范围:

last_column = ws.range('A1').end('right').get_address(0, 0)[0]  # 获取最后一列
last_row = ws.range('A1').end('down').row  # 获取最后一行
a_range = f'A1:{last_column}{last_row}'
a_range

last_column = ws.range('A1').end('right').get_address(0, 0)[0]  # 获取最后一列
last_row = ws.range('A1').end('down').row  # 获取最后一行
a_range = f'A1:{last_column}{last_row}'
a_range
'A1:P2'

上述代码获取到201.xlsx的数据范围是A1:P2,事实也是如此:



python对excel表格的截图_数据


image-20200614191505369

获得Range操作对象:

range_val = ws.range(a_range)
range_val

range_val = ws.range(a_range)
range_val

设置自动列宽和行高

range_val.autofit()

range_val.autofit()

设置边框

range_val.api.Borders(8).LineStyle = 1  # 上边框
range_val.api.Borders(9).LineStyle = 1  # 下边框
range_val.api.Borders(7).LineStyle = 1  # 左边框
range_val.api.Borders(10).LineStyle = 1  # 右边框
range_val.api.Borders(12).LineStyle = 1  # 内横边框
range_val.api.Borders(11).LineStyle = 1  # 内纵边框

range_val.api.Borders(8).LineStyle = 1  # 上边框
range_val.api.Borders(9).LineStyle = 1  # 下边框
range_val.api.Borders(7).LineStyle = 1  # 左边框
range_val.api.Borders(10).LineStyle = 1  # 右边框
range_val.api.Borders(12).LineStyle = 1  # 内横边框
range_val.api.Borders(11).LineStyle = 1  # 内纵边框

生成图片

复制图片:

range_val.api.CopyPicture()  # 复制图片区域
ws.api.Paste()  # 粘贴
pic = ws.pictures[0]  # 当前图片
pic.api.Copy()  # 复制图片

range_val.api.CopyPicture()  # 复制图片区域
ws.api.Paste()  # 粘贴
pic = ws.pictures[0]  # 当前图片
pic.api.Copy()  # 复制图片

现在使用Pillow获取剪贴板中的图片并保存起来,没有Pillow库,可以通过pip install Pillow安装

from PIL import ImageGrab

from PIL import ImageGrab
img = ImageGrab.grabclipboard()  # 获取剪贴板的图片数据
img

img = ImageGrab.grabclipboard()  # 获取剪贴板的图片数据
img



python对excel表格的截图_os如何读取图片_02


png

保存图片:

img.save("F:/pandas/item_img/dist/result/201.png")  # 保存图片

img.save("F:/pandas/item_img/dist/result/201.png")  # 保存图片

删除粘贴到excel应用中的图片:

pic.delete()

pic.delete()

保存设置好列宽和边框的excel表:

wb.save("F:/pandas/item_img/dist/result/201.xlsx")

wb.save("F:/pandas/item_img/dist/result/201.xlsx")

关闭表格文件:

wb.close()

wb.close()

退出后台excel应用:

app.quit()

app.quit()

完整代码实现

文件table_cut.py的代码如下:

import os
import sys

import pandas as pd
import xlwings as xw
from PIL import ImageGrab


def format_group_fields(group_field: str, columns: list):
if group_field is None:
return columns[0]
if group_field.isdigit():
return columns[int(group_field) - 1]
    group_fields = group_field.split("|")
    result = []
for group_field in group_fields:
if group_field in columns:
            result.append(group_field)
if len(result) == 0:
return columns[0]
else:
return result


def table_cut(data_file_path, group_field, head_line=1, sheet_name=1):
if data_file_path == "":
return f"请输入要处理的excel文件"
if not os.path.exists(data_file_path):
return f"{data_file_path}不存在,请输入正确的文件名"
    print("启动系统默认的Office Excel应用程序")
    app = xw.App(visible=False, add_book=False)  # 设置应用,关闭可视化,取消添加book
try:
# 默认值为true。将此属性设置为false可在代码运行时抑制提示和警报消息;当消息需要响应时,Excel将选择默认响应。
        app.display_alerts = False
# 关闭屏幕更新以加快脚本速度。 将无法看到脚本正在执行的操作,但它将运行得更快。 可在脚本结束时将screen_updating属性设置回True。
        app.screen_updating = False

        path = os.path.dirname(data_file_path)
        result_path = os.path.join(path, "result")
if not os.path.exists(result_path):
            os.mkdir(result_path)
if isinstance(sheet_name, int):
            sheet_name = sheet_name - 1
        df = pd.read_excel(data_file_path, header=head_line - 1, sheet_name=sheet_name)
        print(f"{data_file_path}读取完毕")

        group_fields = format_group_fields(group_field, df.columns)
        print("使用的分组字段为:", group_fields)
for field, df_g in df.groupby(group_fields):
if isinstance(field, tuple):
                field = '-'.join(field)
            print(field)
            excel_result_path = f"{result_path}/{field}.xlsx"
            df_g.to_excel(excel_result_path, index=False)
            print("生成excel文件:", excel_result_path)

            wb = app.books.open(excel_result_path)  # 打开文件
            print("打开该文件", end=",")
try:
                ws = wb.sheets.active
                last_column = ws.range('A1').end('right').get_address(0, 0)[0]  # 获取最后一列
                last_row = ws.range('A1').end('down').row  # 获取最后一行
                a_range = f'A1:{last_column}{last_row}'  # 生成表格的数据范围
                print(f"该excel文件活动范围是{a_range}", end=",")
                range_val = ws.range(a_range)

# 设置自动列宽和行高
                range_val.autofit()
                print("列宽和行高调整完毕")
# 设置边框
for i in range(7, 13):
                    range_val.api.Borders(i).LineStyle = 1
                print("边框设置完成", end=",")
# 生成图片
                range_val.api.CopyPicture()  # 复制图片区域
                ws.api.Paste()  # 粘贴
                pic = ws.pictures[0]  # 当前图片
                pic.api.Copy()  # 复制图片
                img = ImageGrab.grabclipboard()  # 获取剪贴板的图片数据
                img.save(f"{result_path}/{field}.png")  # 保存图片
                print("截图保存完成", end=",")

                pic.delete()
# 保存并关闭 Excel
                wb.save(excel_result_path)
                print("调整之后的excel文件已保存")
finally:
                wb.close()
finally:
        app.quit()


if __name__ == "__main__":
    msg = """需要输入的字段分别为:
            excel文件路径 - 必填项,被处理的excel文件
            分组字段 - 默认为第1个字段,同时要使用多个字段分组时可以使用|分割
            第几行作为表头 - 默认从第1行开始读取,表头不在第一行时应该指定该参数
            sheet名 - 默认为第1张表,对于存在多个sheet的excel文件,可以指定该参数
            例如:xxx.xlsx 房号 2 6月
            """
    print(msg)
    data_file_path, group_field, head_line, sheet_name = "", None, 1, 1
if len(sys.argv) > 1:
        data_file_path = sys.argv[1]
if len(sys.argv) > 2:
        group_field = sys.argv[2]
if len(sys.argv) > 3:
        head_line = int(sys.argv[3])
if len(sys.argv) > 4:
        sheet_name = sys.argv[4]
if sheet_name.isdigit():
            sheet_name = int(sheet_name)
    result = table_cut(data_file_path, group_field, head_line, sheet_name)
if result:
        print(result)

    input("程序已经运行结束,回车后确认")


import os
import sys

import pandas as pd
import xlwings as xw
from PIL import ImageGrab


def format_group_fields(group_field: str, columns: list):
if group_field is None:
return columns[0]
if group_field.isdigit():
return columns[int(group_field) - 1]
    group_fields = group_field.split("|")
    result = []
for group_field in group_fields:
if group_field in columns:
            result.append(group_field)
if len(result) == 0:
return columns[0]
else:
return result


def table_cut(data_file_path, group_field, head_line=1, sheet_name=1):
if data_file_path == "":
return f"请输入要处理的excel文件"
if not os.path.exists(data_file_path):
return f"{data_file_path}不存在,请输入正确的文件名"
    print("启动系统默认的Office Excel应用程序")
    app = xw.App(visible=False, add_book=False)  # 设置应用,关闭可视化,取消添加book
try:
# 默认值为true。将此属性设置为false可在代码运行时抑制提示和警报消息;当消息需要响应时,Excel将选择默认响应。
        app.display_alerts = False
# 关闭屏幕更新以加快脚本速度。 将无法看到脚本正在执行的操作,但它将运行得更快。 可在脚本结束时将screen_updating属性设置回True。
        app.screen_updating = False

        path = os.path.dirname(data_file_path)
        result_path = os.path.join(path, "result")
if not os.path.exists(result_path):
            os.mkdir(result_path)
if isinstance(sheet_name, int):
            sheet_name = sheet_name - 1
        df = pd.read_excel(data_file_path, header=head_line - 1, sheet_name=sheet_name)
        print(f"{data_file_path}读取完毕")

        group_fields = format_group_fields(group_field, df.columns)
        print("使用的分组字段为:", group_fields)
for field, df_g in df.groupby(group_fields):
if isinstance(field, tuple):
                field = '-'.join(field)
            print(field)
            excel_result_path = f"{result_path}/{field}.xlsx"
            df_g.to_excel(excel_result_path, index=False)
            print("生成excel文件:", excel_result_path)

            wb = app.books.open(excel_result_path)  # 打开文件
            print("打开该文件", end=",")
try:
                ws = wb.sheets.active
                last_column = ws.range('A1').end('right').get_address(0, 0)[0]  # 获取最后一列
                last_row = ws.range('A1').end('down').row  # 获取最后一行
                a_range = f'A1:{last_column}{last_row}'  # 生成表格的数据范围
                print(f"该excel文件活动范围是{a_range}", end=",")
                range_val = ws.range(a_range)

# 设置自动列宽和行高
                range_val.autofit()
                print("列宽和行高调整完毕")
# 设置边框
for i in range(7, 13):
                    range_val.api.Borders(i).LineStyle = 1
                print("边框设置完成", end=",")
# 生成图片
                range_val.api.CopyPicture()  # 复制图片区域
                ws.api.Paste()  # 粘贴
                pic = ws.pictures[0]  # 当前图片
                pic.api.Copy()  # 复制图片
                img = ImageGrab.grabclipboard()  # 获取剪贴板的图片数据
                img.save(f"{result_path}/{field}.png")  # 保存图片
                print("截图保存完成", end=",")

                pic.delete()
# 保存并关闭 Excel
                wb.save(excel_result_path)
                print("调整之后的excel文件已保存")
finally:
                wb.close()
finally:
        app.quit()


if __name__ == "__main__":
    msg = """需要输入的字段分别为:
            excel文件路径 - 必填项,被处理的excel文件
            分组字段 - 默认为第1个字段,同时要使用多个字段分组时可以使用|分割
            第几行作为表头 - 默认从第1行开始读取,表头不在第一行时应该指定该参数
            sheet名 - 默认为第1张表,对于存在多个sheet的excel文件,可以指定该参数
            例如:xxx.xlsx 房号 2 6月
            """
    print(msg)
    data_file_path, group_field, head_line, sheet_name = "", None, 1, 1
if len(sys.argv) > 1:
        data_file_path = sys.argv[1]
if len(sys.argv) > 2:
        group_field = sys.argv[2]
if len(sys.argv) > 3:
        head_line = int(sys.argv[3])
if len(sys.argv) > 4:
        sheet_name = sys.argv[4]
if sheet_name.isdigit():
            sheet_name = int(sheet_name)
    result = table_cut(data_file_path, group_field, head_line, sheet_name)
if result:
        print(result)

    input("程序已经运行结束,回车后确认")

打包成exe文件

打包成exe文件,可以供没有安装python的windows电脑使用

pyinstaller -F table_cut.py -i a.ico

pyinstaller -F table_cut.py -i a.ico

-F :指定打包为单个exe文件,而不是一个文件夹

-i :指定exe文件的图标

使用效果



python对excel表格的截图_os如何读取图片_03


image-20200614204636344