真正的勇士,敢于直面银行卡上的余额,敢于正视磅秤上的数字。

表格数据是最常见的数据类型,Excel是日常办公中最常接触的文件。

上一章提过Python可以通过openpyxl模块处理Excel文件。

其实Python处理Excel的三方模块主要有这几个:

  1. xlrdxlwtxlutils,支持读写Excel文件。
  2. openpyxl,支持读写和修改OpenXML格式的Excel文件。
  3. xlswriter,支持写文件,以及更多Excel功能特性。
  4. pandas,底层依赖xlrdxlwt读写Excel文件,处理数据神器。
  5. pywin32,调用Win32 API操作文件,只适用于Windows。
  6. xlwings,可理解为pywin32的多平台加强版,有商业版支持。

简单做个对比分析:

  • 第1类中3个模块,属于同一作者的开源项目,目前已停更,不建议直接使用。
  • pywin32xlwings基于原生Excel软件提供服务,需提前安装软件,读写性能和功能最佳,xlwings还有商业版本支持。
  • pandas侧重数据分析,Excel文件读写只是其数据输入和输出部分。
  • openpyxl不需要依赖Excel功能,直接读写OpenXML内容,兼容性好。
  • xlswriter侧重写Excel文件,支持VBA等更多特性。

建议的使用场景:

  1. 如果企业内大量使用Excel,并用了很多如VBA宏的特性,建议用xlwings。一方面功能全,另一方面可以采买商业付费版支持。
  2. 对于普通Excel文件自动处理,建议用openpyxl,大部分情况下够用。
  3. GB级以下的数据分析,建议用pandas,后续会单独出一个数据分析系列来介绍它。

本文重点介绍openpyxl的使用,它足够应付大部分人日常的表格自动化处理场景。

日常处理Excel文件,最常见的应用场景有3类:

  • 读写Excel文件
  • 合并、分割文档数据
  • 报表自动化,批量生成图表

模块安装:pip install openpyxl

基本使用

Excel表格文件中主要元素有这几个:

  • sheet(工作表):一个Excel文件可以有多个sheet。
  • row/col(行和列),iter_rows()iter_cols()可以返回所有行和列。
  • cell(单元格):和Excel一样用sheet['A1']表示,注意行列都以1开始。
  • formulae(公式):可以为单元格设置计算公式,和Excel概念一致。
  • charts(图表):比如饼图、柱状图等。
  • style(样式):支持自定义样式,以及条件格式化等。
  • image(图片),支持pillow的图像处理。

打开Excel文件

  1. import pathlib
  2. import datetime
  3. from openpyxl import load_workbook
  4. path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/004excel')
  5. in_path = path.joinpath('input.xlsx')
  6. wb = load_workbook(in_path, read_only=True)
  7. for name in wb.sheetnames:
  8. ws = wb[name]
  9. print(f'{name} 表有 {ws.max_row-ws.min_row+1} 行, {ws.max_column-ws.min_column+1} 列.')

读取Excel文件内的数据

  1. import pathlib
  2. import datetime
  3. from openpyxl import load_workbook
  4. path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/004excel')
  5. in_path = path.joinpath('input.xlsx')
  6. wb = load_workbook(in_path)
  7. ws = wb[wb.sheetnames[0]] # 第一个工作表
  8. # 操作单列
  9. for cell in ws["A"]:
  10. print(cell.value)
  11. # 操作单行
  12. for cell in ws["1"]:
  13. print(cell.value, end='\t')
  14. # 操作多列
  15. for column in ws['A:C']:
  16. for cell in column:
  17. print(cell.value)
  18. # 操作多行
  19. for row in ws['1:3']:
  20. for cell in row:
  21. print(cell.value, end='\t')
  22. print()
  23. # 指定范围
  24. for row in ws['A1:C3']:
  25. for cell in row:
  26. print(cell.value)
  27. # 所有行
  28. for row in ws.iter_rows():
  29. for cell in row:
  30. print(cell.value)
  31. # 所有列
  32. for column in ws.iter_cols():
  33. for cell in column:
  34. print(cell.value)

创建Excel文件

  1. import pathlib
  2. import datetime
  3. from openpyxl import Workbook
  4. path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/004excel')
  5. out_path = path.joinpath('004excel_basic.xlsx')
  6. sheet_path = path.joinpath('004excel_sheet.xlsx')
  7. wb = Workbook()
  8. ws = wb.active # 找当前Sheet
  9. ws['A1'] = 42
  10. ws.append([1, 2, 3]) # 添加行
  11. ws['A2'] = datetime.datetime.now()
  12. wb.save(out_path)
  13. # 创建第二个sheet
  14. wb.create_sheet('sheet2')
  15. ws = wb['sheet2']
  16. ws.title = '第二个工作表'
  17. ws.sheet_properties.tabColor = "FF0000"
  18. # 增加一整行数据
  19. ws.append([1, 2, 3, datetime.datetime.now()])
  20. ws.cell(row=1, column=3, value=10)
  21. # 获取cell方式一
  22. d1 = ws.cell(row=1, column=4)
  23. # 获取cell方式二
  24. d2 = ws['D1']
  25. print(d1==d2)
  26. print(type(d2.value))
  27. wb.save(sheet_path)

设置单元格样式

单元格样式可以用NumberFormat(数据格式)、Alignment(对齐)、Font(字体)、Border(边框)、PatternFill(填充)、Protection(保护)等来设置。

也可以通过NamedStyle自定义样式,重复应用到单元格。

此外,通过row_dimensionscolumn_dimensions可以设置整行/整列的样式。

  1. import pathlib
  2. import datetime
  3. from openpyxl import Workbook
  4. from openpyxl.styles import numbers, Font, GradientFill, PatternFill, Border, Side, Alignment, Protection
  5. from openpyxl.styles import NamedStyle
  6. path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/004excel')
  7. out_path = path.joinpath('004excel_style.xlsx')
  8. wb = Workbook()
  9. ws = wb.active
  10. ws.cell(row=1, column=1, value='微软雅黑').font = Font(name='微软雅黑', size=18, bold=True, color='123456')
  11. ws.cell(row=2, column=1, value='居中对齐').alignment = Alignment(horizontal='center', vertical='center')
  12. ws.cell(row=3, column=1, value='填充渐变色').fill = GradientFill(stop=['FF0000', '0000FF'])
  13. ws.cell(row=4, column=1, value='设置边线').border = Border(left=Side(border_style='thin', color='123456'), right= Side(border_style='thick', color='FF0000'))
  14. ws.cell(row=5, column=1, value='受保护的').protection = Protection(locked=True, hidden=True)
  15. ws.cell(row=6, column=1, value=0.88).number_format =numbers.FORMAT_PERCENTAGE
  16. ws.column_dimensions['A'].width = 80 # 修改列宽
  17. # 设置整行
  18. row = ws.row_dimensions[4]
  19. row.font = Font(name='宋体', size=30, italic=True, color='FF0000')
  20. # 设置整列
  21. column = ws.column_dimensions["B"]
  22. column.fill = PatternFill("solid", fgColor="1874CD")
  23. # 自定义样式
  24. my_style = NamedStyle(name='my_style',
  25. font=Font(name='宋体',size=12, color='FF000000'),
  26. fill=PatternFill(fill_type='solid', fgColor="FFDAB9"),
  27. border=Border(top=Side(border_style='thin', color='FF000000')),
  28. alignment=Alignment(horizontal='left',vertical='center'))
  29. ws['B4'] = '自定义样式'
  30. ws['B4'].style=my_style
  31. wb.save(out_path)

合并单元格

  • 合并单元格:merge_cells()
  • 解除合并:unmerge_cells()

注意:对于没有合并过单元格的位置调用unmerge_cells()会出错;此外合并的单元格顺序是从左上角到右下角。

  1. import pathlib
  2. import datetime
  3. from openpyxl import Workbook
  4. from openpyxl.styles import numbers, Font, GradientFill, Border, Side, Alignment, Protection
  5. path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/004excel')
  6. out_path = path.joinpath('004excel_cell_merge.xlsx')
  7. wb = Workbook()
  8. ws = wb.active
  9. # 合并,必须是从左上角,到右下角的顺序
  10. ws.merge_cells('A3:D5')
  11. ws.merge_cells('A7:D10')
  12. # 合并后以起点为代表读写值,其他的cell都被设置为只读
  13. ws['A3']='test'
  14. # 解除合并
  15. ws.unmerge_cells('A7:D10')
  16. # 也可以用起止行列数字来选取合并区域
  17. ws.merge_cells(start_row=12,start_column=11,end_row=15,end_column=14)
  18. ws['K12']='合并过'
  19. ws.unmerge_cells(start_row=12,start_column=11,end_row=15,end_column=14)
  20. wb.save(out_path)

图表和公式应用

openpyxl支持的图表都在openpyxl.chart包中,比如直方图、饼图、折线图等。

可以直接把单元格的值设置为公式,在Excel软件中可以获得结果,但openpyxl并不支持公式的计算。

如果生成的xlsx文件被Excel软件打开过,这时公式的计算结果会缓存在文件中,这时用openpyxl打开文件时候,增加一个data_only=True参数,也可以读取到计算结果。

  1. import pathlib
  2. import datetime
  3. from openpyxl import Workbook
  4. from openpyxl.chart import BarChart, PieChart, LineChart, Reference
  5. path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/004excel')
  6. out_path = path.joinpath('004excel_graph.xlsx')
  7. wb = Workbook()
  8. ws = wb.active
  9. # 装载数据
  10. data = [
  11. ('月份', '产品A销量', '产品B销量'),
  12. (1, 100, 200),
  13. (2, 106, 100),
  14. (3, 120, 200),
  15. (4, 180, 100),
  16. (5, 200, 300),
  17. (6, 280, 400),
  18. (7, 400, 200),
  19. (8, 500, 800),
  20. (9, 600, 1000),
  21. (10, 800, 300),
  22. ]
  23. for d in data:
  24. ws.append(d)
  25. # 用Python做些统计
  26. ws['A12'] = '汇总累加'
  27. ws['B12'] = sum([d[1] for i, d in enumerate(data) if i > 0])
  28. ws['C12'] = sum([d[2] for i, d in enumerate(data) if i > 0])
  29. # 用公式统计
  30. ws['A13'] = '公式累加'
  31. ws['B13'] = '=SUM(B2:B11)'
  32. ws['C13'] = '=SUM(C2:C11)'
  33. # 增加一列总销量
  34. ws['D1'] = '当月总销量'
  35. for i in range(2, 13):
  36. ws[f'D{i}'] = ws[f'C{i}'].value + ws[f'B{i}'].value
  37. # 列状图
  38. ct_bar= BarChart()
  39. ct_bar.type = "col" # 列状图
  40. ct_bar.style = 10
  41. ct_bar.title = "销量柱状图"
  42. ct_bar.y_axis.title = '销量'
  43. ct_bar.x_axis.title = '月份'
  44. d_ref = Reference(ws, min_col=2, min_row=1, max_row=11, max_col=3)
  45. series = Reference(ws, min_col=1, min_row=2, max_row=11)
  46. ct_bar.add_data(d_ref, titles_from_data=True)
  47. ct_bar.set_categories(series)
  48. ws.add_chart(ct_bar, 'A15')
  49. # 饼图
  50. ct_pie = PieChart()
  51. ct_pie.title = "产品销量占比"
  52. d_ref = Reference(ws, min_col=2, min_row=12, max_col=3)
  53. series = Reference(ws, min_col=2, min_row=1, max_col=3)
  54. ct_pie.add_data(d_ref, titles_from_data=True)
  55. ct_pie.set_categories(series)
  56. ct_pie.splitType='val'
  57. ws.add_chart(ct_pie, 'I15')
  58. # 一个图两个轴
  59. ct_bar = BarChart()
  60. d_ref = Reference(ws, min_col=2, min_row=1, max_row=11, max_col=3)
  61. ct_bar.add_data(d_ref, titles_from_data=True)
  62. ct_bar.x_axis.title = '月份'
  63. ct_bar.y_axis.title = '产品AB销量'
  64. ct_bar.y_axis.majorGridlines = None
  65. ct_bar.title = '销量分析'
  66. ct_line = LineChart()
  67. d_ref = Reference(ws, min_col=4, min_row=1, max_row=11)
  68. ct_line.add_data(d_ref, titles_from_data=True)
  69. ct_line.y_axis.axId = 200 # 不为空即可
  70. ct_line.y_axis.title = '总销量'
  71. # 让线条和第一图的最大值相交
  72. ct_line.y_axis.crosses = 'max'
  73. ct_bar += ct_line # 只支持+=赋值,不能直接+
  74. ws.add_chart(ct_bar, 'A31')
  75. wb.save(out_path)

插入图像

openpyxl的图像处理基于PIL.Image

PIL.Image的宽和高单位默认使用Pixel(像素),如果需要映射到Excel中的位置,可以用pixels_to_EMU()转换。

  1. import pathlib
  2. from openpyxl import Workbook, load_workbook
  3. from openpyxl.drawing.image import Image
  4. from openpyxl.drawing.spreadsheet_drawing import AbsoluteAnchor
  5. from openpyxl.drawing.xdr import XDRPoint2D, XDRPositiveSize2D
  6. from openpyxl.utils.units import pixels_to_EMU as p2e
  7. path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/004excel')
  8. out_path = path.joinpath('004excel_image.xlsx')
  9. img_path = path.joinpath('image.jpg')
  10. wb = Workbook()
  11. ws = wb.active
  12. img = Image(img_path)
  13. img.width /= 2
  14. img.height /= 2
  15. h, w = img.height, img.width
  16. # 绝对定位图片
  17. position = XDRPoint2D(p2e(800), p2e(800))
  18. size = XDRPositiveSize2D(p2e(h), p2e(w))
  19. img.anchor = AbsoluteAnchor(pos=position, ext=size)
  20. ws.add_image(img)
  21. # 单元格定位
  22. img = Image(img_path)
  23. size = (90, 90)
  24. img.width, img.height = size
  25. ws.row_dimensions[3].height=90
  26. ws.column_dimensions['B'].width=90
  27. ws.add_image(img, 'B3')
  28. wb.save(out_path)

总结

本文主要介绍了使用openpyxl模块处理Excel文件,包括基本的Excel文件读写、单元格样式处理、图表生成和图像结合等。

用Excel应对日常工作中的小规模数据足以。

比如实践应用中有一个“多门店加盟品牌数据统计”的案例,其中正好用到了上面的各项知识点。

4、Excel 文件处理 - 图1

案例说明:

  • Pythonopenpyxl应用实践为主。
  • 提供解决问题思路,实战中可直接应用。
  • 后续“数据分析专题”会介绍Pandas等数据分析工具,一个实战应用更灵活的模块。

加入学习群

4、Excel 文件处理 - 图2