首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 ># 从每天 3 小时到 10分钟:用 Python + openpyxl 自动生成 Excel 工单的完整实践

# 从每天 3 小时到 10分钟:用 Python + openpyxl 自动生成 Excel 工单的完整实践

原创
作者头像
用户12732433
发布2026-09-03 09:27:39
发布2026-09-03 09:27:39
450
举报

#:一线业务场景里,最耗时间的往往不是"高大上"的算法,而是那些重复的数据搬运:从系统里导出一张表,手工拆字段、填模板、核对、打印。本文分享一个真实落地的方案——用 Python + openpyxl 把车间工单整理工作全自动化,每天节省约3 小时,且零额外成本运行。

一、背景:一个典型的"表格搬运"场景

我们公司的业务流程是这样的:采购系统(OA)里会不断产生加工打样订单,每条订单包含零件名称、数量、交期、申请人等信息。我的工作是:

  1. 从 OA 系统导出订单明细表(Excel);
  2. 按照车间要求的固定模板,整理成一张张可打印的 Excel 工单;
  3. 给每条记录分配一个内部单号(格式如 DY-2609-2100,年月 + 4 位流水号);
  4. 打印纸质工单下发给车间;
  5. 把成品清单归档到共享盘,并追加到年度总案表。

纯手工做,一单平均 5~10 分钟,遇到单数较多的时间,一天 20~30 个订单,半天时间就搭进去了。而且手工分配流水号特别容易出错——重号、跳号都出过事故。

这种活的特征是:规则固定、重复度高、但数据源格式不可控(OA 导出的表格偶尔多一列、少一列,日期格式还会变)。这正是 Python 自动化的甜点区。

二、方案选型:为什么是 openpyxl 而不是 pandas

一开始我也考虑过 pandas,但很快放弃了,原因是:

  • 模板保真:车间要求工单必须用固定模板(合并单元格、边框、字体、行高都有讲究)。pandas 写 Excel 本质是生成新表,样式控制很弱;openpyxl 可以直接打开模板文件、往指定单元格填值、另存为新文件,样式零丢失。
  • 图片插入:订单里要贴零件图,openpyxl 的 ws.add_image() 可以直接把本地图片钉到指定单元格。
  • 轻量:单个三方库,pip install openpyxl 就完事,内网机器也能装。

整体架构非常朴素:

代码语言:javascript
复制
```
OA 导出的明细 Excel
        │
        ▼
  打样订单整理.py
  ├─ 读取明细 → 解析/校验字段
  ├─ 打开模板 → 填充数据 + 插入零件图
  ├─ 读取单号进度.json → 分配流水号 → 回写进度
  ├─ 另存为工单 Excel(本地输出目录)
  └─ 归档到共享盘 Z:\ + 追加年度总案表(可失败,静默跳过)
        │
        ▼
  打印 → 下发车间
```

三、核心实现拆解

3.1 模板填充:加载模板,改值另存

不要用代码"画"表格,直接维护一个 .xlsx 模板文件,业务格式变了改模板就行,代码一行不用动:

代码语言:javascript
复制
```python
from openpyxl import load_workbook
from copy import copy

TEMPLATE = r"E:\AI\模板\订单铣样清单.xlsx"

def build_work_order(rows, out_path):
    wb = load_workbook(TEMPLATE)
    ws = wb.active

    # 填表头字段
    ws["B2"] = rows[0]["order_no"]      # 订单号
    ws["F2"] = rows[0]["owner"]         # 负责人
    ws["H2"] = rows[0]["date_str"]      # 日期

    # 填明细行:从模板里的"第一行明细"往下复制样式
    start = 6
    tpl_row = ws[start]
    for i, r in enumerate(rows):
        cur = start + i
        if cur > start:                 # 复制上一行样式到新行
            for col in range(1, 9):
                src, dst = ws.cell(cur-1, col), ws.cell(cur, col)
                dst._style = copy(src._style)
        ws.cell(cur, 1, i + 1)          # 序号
        ws.cell(cur, 2, r["part_name"]) # 零件名称
        ws.cell(cur, 3, r["qty"])       # 数量
        ws.cell(cur, 4, r["deadline"])  # 交期

    wb.save(out_path)
```

两个细节值得说:

  • 样式用"复制上一行"的方式扩行,比逐个设置 Border/Font 稳得多,模板怎么改代码都能跟上;
  • 日期列我统一格式化成 9月3日 这种口语化写法再填入,车间师傅看着舒服,这一步纯属用户体验,但落地时很加分。

3.2 流水号分配:用一个 JSON 文件做"断点续传"

流水号是最容易出错的地方,解法简单粗暴——用一个小 JSON 文件记录当前进度,每次分配后立即回写:

代码语言:javascript
复制
```python
import json, os

PROGRESS = r"E:\AI\内部单号进度.json"

def next_serial(count=1):
    """批量分配 count 个流水号,原子性回写进度"""
    with open(PROGRESS, encoding="utf-8") as f:
        prog = json.load(f)

    key = time.strftime("%y%m")            # 如 "2609"
    cur = prog.get(key, 1988) + 1
    nums = list(range(cur, cur + count))
    prog[key] = cur + count - 1

    tmp = PROGRESS + ".tmp"                # 先写临时文件再替换,防中途断电写坏
    with open(tmp, "w", encoding="utf-8") as f:
        json.dump(prog, f, ensure_ascii=False)
    os.replace(tmp, PROGRESS)
    return nums
```

要点有两个:

  1. 按年月分键26092610……),跨月自动从起始号重新计数,符合内部单号 DY-{年月}-{流水} 的规则;
  2. 先写临时文件再 os.replace(),替换操作是原子的,哪怕程序中途崩了,进度文件也不会写坏——这一招在所有"记录状态"的场景都适用。

3.3 容错设计:共享盘挂了怎么办

归档环节要写共享盘 Z:\ 并追加年度总案表。共享盘偶尔掉线/没权限,如果直接写,脚本一崩,前面生成的工单还在但流程中断,最烦人。

我的处理原则是:主产物(本地工单)必须成功,旁路产物(归档)允许失败

代码语言:javascript
复制
```python
def safe_archive(files):
    try:
        if not os.path.exists(r"Z:\CNCtudang"):
            raise OSError("共享盘不可用")
        month_dir = rf"Z:\CNCtudang\订单图档\{time.strftime('%Y.%m')}"
        os.makedirs(month_dir, exist_ok=True)
        for f in files:
            shutil.copy2(f, month_dir)
        append_master_table(files)     # 追加年度总案表
    except OSError as e:
        # 归档失败不报错、不中断,只记日志,等盘恢复后补归档
        log.warning(f"归档跳过:{e}")
```

这是全项目里我认为最"值钱"的一段设计——自动化工具想在一群不懂技术的人手里活下去,就必须比手工操作更不怕出错。宁可降级运行(本地产物照常生成),也不要让用户看到一堆红色报错然后弃用。

3.4 图片插入:从旧 Excel 里"抠图"

零件图没有独立文件,嵌在 OA 导出的旧 Excel 里。openpyxl 读不了嵌入图片,这里用了个冷门但好用的操作——xlsx 本质是 zip 包,xl/media/ 目录下就是所有嵌入图片

代码语言:javascript
复制
```python
import zipfile, shutil

def extract_images(xlsx_path, out_dir):
    os.makedirs(out_dir, exist_ok=True)
    paths = []
    with zipfile.ZipFile(xlsx_path) as z:
        for name in z.namelist():
            if name.startswith("xl/media/"):
                dst = os.path.join(out_dir, os.path.basename(name))
                with z.open(name) as src, open(dst, "wb") as f:
                    shutil.copyfileobj(src, f)
                paths.append(dst)
    return paths
```

拿到图片路径后,用 openpyxl.drawing.image.Image 加上 ws.add_image(img, "B8") 钉进工单即可。注意 add_image 需要 Pillow 支持(pip install pillow),且锚点单元格要提前留好行高。

3.5 部署:一个 bat 启动器干掉所有门槛

最后一步,让不会 Python 的同事也能用。把脚本做成免安装启动器:

代码语言:javascript
复制
```bat
@echo off
chcp 65001 >nul
cd /d "%~dp0"
"C:\Python313\python.exe" "打样订单整理.py" %*
pause
```

双击 bat → 弹出交互提示 → 选订单文件 → 自动出工单。没有环境变量、没有命令行、没有打包 exe 的体积和杀软误报问题。源码即程序,改需求时直接改 .py 文件,bat 永远不用动。

四、落地效果

指标

手工

自动化后

单张工单耗时

5~10 分钟

约 3 秒

30个订单整单耗时

约 3小时

约 10 分钟(含打印)

流水号错误

偶发重号/跳号

0(JSON 进度保障)

归档遗漏

忘记归档时有发生

自动执行,盘故障时降级跳过

更重要的是稳定性:上线一个多月,处理了几百条订单记录,期间经历了共享盘掉线、OA 导出格式微调、跨月重置流水号,脚本都平稳扛过来了。

五、几点经验总结

  1. 模板和代码分离。样式永远放在 xlsx 模板里,代码只负责填值。需求变了改模板,程序员的活变成业务的活。
  2. 状态文件用原子写。所有"记进度"的场景,先写 tmp 再 replace,成本一行代码,收益是永不出错。
  3. 主流程和旁路流程分级容错。本地产物必须成功,网络归档允许失败但要有日志可补。不要让 10% 概率的网络问题毁掉 100% 的用户体验。
  4. xlsx 就是 zip。读嵌入图片、批量改样式这类 openpyxl 做不到的事,直接解压处理往往柳暗花明。
  5. 最后一公里决定成败。一个 bat 双击启动,比任何精美的 GUI 都更贴近一线用户。

这类"表格搬运"自动化没有技术门槛,唯一的门槛是愿不愿意花一个下午把第一版写出来。希望这个案例能给同样被重复劳动困住的朋友一个起点——如果你手上也有一张每天都要手工整理的表,今天就可以动手了。


环境:Windows 11 + Python 3.13 + openpyxl 3.1 + Pillow。文中代码均可直接复用,欢迎交流。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、背景:一个典型的"表格搬运"场景
  • 二、方案选型:为什么是 openpyxl 而不是 pandas
  • 三、核心实现拆解
    • 3.1 模板填充:加载模板,改值另存
    • 3.2 流水号分配:用一个 JSON 文件做"断点续传"
    • 3.3 容错设计:共享盘挂了怎么办
    • 3.4 图片插入:从旧 Excel 里"抠图"
    • 3.5 部署:一个 bat 启动器干掉所有门槛
  • 四、落地效果
  • 五、几点经验总结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档