# 用 WorkBuddy + pyautogui 实现绿源ERP电池发货单全流程自动化
> **场景**:电池车制造企业的PMC仓库每天需要打印50-80张电池发货单,手工操作繁琐易错。本文记录了如何用 WorkBuddy AI 助手 + Python pyautogui 实现从ERP打印到Excel表格自动生成的完整自动化方案。
---
## 一、背景与痛点
我是一名电池车制造企业PMC仓库的工作人员,日常工作流程如下:
1. 登录绿源ERP系统(Web端,通过夸克浏览器访问)
2. 从「整单发货管理」页面查询当天发货单
3. 逐个打开发货单明细,打印A4电池单
4. 根据打印出来的单据,手工制作电池发货明细Excel表格
**痛点**:
- 每天打印50-80张单子,重复操作大量时间
- 手工做表容易出错(型号、数量、分组计算)
- 数据一致性难以保证(ERP数据 vs Excel表格)
**目标**:错误率为0,效率最大化。
---
## 二、方案选型:为什么用 pyautogui 而不是 Selenium?
| 方案 | 优点 | 缺点 |
|------|------|------|
| **pyautogui(最终选择)** | 不需要浏览器驱动,模拟真实操作,零安全风险 | 依赖固定坐标,屏幕分辨率变化需调整 |
| Selenium/Playwright | 代码更稳定,不依赖坐标 | 需要安装驱动,可能触发ERP安全告警 |
| 直接调ERP后端API | 最高效 | 需要IT部门配合,有安全风险 |
选择 pyautogui 的原因:
- **零安全风险**:跟正常人工操作完全一样,不会触发任何安全告警
- **无需额外依赖**:不需要安装浏览器驱动、不需要获取API权限
- **AI辅助开发**:WorkBuddy 可以实时截图分析、调整坐标、调试脚本,开发效率极高
---
## 三、环境搭建
### 3.1 需要安装的Python库
```bash
pip install pyautogui pyperclip Pillow openpyxl
```
### 3.2 依赖说明
- `pyautogui` — 模拟鼠标键盘操作
- `pyperclip` — 剪贴板读写
- `Pillow` — 屏幕截图和像素检测
- `openpyxl` — 读写Excel文件
---
## 四、核心功能一:自动打印发货单
### 4.1 工作流程
```
输入单号 → 查询 → 双击结果行 → A4电池打印 → 打印预览
→ 扫描工具栏定位"打印"按钮 → 点击 → 打印对话框
→ 智能页码处理 → 确定打印 → 关闭预览 → 关闭Tab → 下一个
```
### 4.2 坐标适配(关键技巧)
所有坐标基于 1920×1080 参考分辨率,运行时自动缩放到实际屏幕:
```python
REF_W = 1920
REF_H = 1080
def sc(coord):
"""将参考坐标转换为实际屏幕坐标"""
sw, sh = pyautogui.size()
x = int(coord[0] * sw / REF_W)
y = int(coord[1] * sh / REF_H)
return x, y
```
### 4.3 坐标获取方法(踩坑经验)
**不要用像素扫描或估算坐标!** 正确做法:
1. AI助手运行 `pyautogui.position()` 读取鼠标当前位置
2. 用户手动把鼠标移到目标位置
3. AI读取坐标并写入脚本
这样获取的坐标100%准确。
### 4.4 打印预览按钮定位(亮点功能)
打印预览的工具栏按钮位置不固定,需要实时扫描定位:
```python
def find_toolbar_buttons():
"""在打印预览工具栏上实时定位"打印"和"关闭"按钮"""
full = pyautogui.screenshot()
toolbar = full.crop((0, 25, sw, 85)) # 裁剪工具栏区域
# 自动检测背景色
pixels = list(toolbar.getdata())
color_counts = Counter(pixels)
bg_color = color_counts.most_common(1)[0][0]
# 按列统计非背景像素数,找出按钮块
col_data = []
for x in range(w):
non_bg = sum(1 for y in range(h)
if not is_similar(pixels[y*w+x], bg_color))
col_data.append(non_bg)
# 根据非背景像素的连续分布,识别按钮位置
# "关闭"按钮在最右边(宽度15~35像素)
# "打印"按钮在"关闭"按钮左边紧邻
```
**核心思路**:工具栏是纯色背景+文字按钮,通过列方向非背景像素数的变化,就能准确定位每个按钮的边界。
### 4.5 多页文档只打印最后一页(智能页码处理)
电池发货单可能是多页的,但实际只需要最后一页的数据。解决方案:
```python
# 1. 点击"页码范围(G)"单选按钮
pyautogui.click(pr_x, pr_y)
# 2. 截取"从(F)"输入框像素判断是否可编辑
pixel = screen.getpixel(sc(COORD_FROM_F))
# 3. 灰色 = 单页文档,直接打印
# 4. 白色 = 多页文档,读取"到(T)"页码,把"从(F)"改为相同值
if not is_gray and to_num > 1:
# 只打印最后一页
paste_text(str(to_num))
```
---
## 五、核心功能二:自动采集数据
### 5.1 数据采集时机
在打开发货单明细页、打印之前,先采集页面数据:
```
打开明细页 → 点击表格区域 → Ctrl+A全选 → Ctrl+C复制 → 保存到文件 → 继续打印
```
### 5.2 为什么要点击表格再全选?
ERP页面的Ctrl+A默认全选的是整个页面(包括导航栏、按钮等),而不是表格内容。**必须先点击表格区域让焦点到表格上**,Ctrl+A才能选中表格数据。
### 5.3 数据解析(核心难点)
ERP复制出来的文本格式像这样(Ctrl+A → Ctrl+C的结果):
```
发货单号
(空行)
ZCFH26032732
经销商:
(空行)
吉长春宽城区于宝伟(专)
制单时间
(空行)
2026/5/25 10:30:00
换配电池
1
ZCYDD260100001
...
标配电池编码 105000123 每组节数 4 标配配发数量 10
电池延迟发货 否
换配电池编码 105000244 每组节数 4 换配配发数量 44
```
**关键发现**:
1. 标签和值之间**隔了一个空行**,需要跳过空行取值
2. 用 `"否"`(电池延迟发货)作为标配和换配的分界线
3. 分界线后面的空行数量**不固定**,不同单号不一样
### 5.4 解析代码(跳过空行法)
```python
def get_next_value(keyword, start=0):
"""找到含keyword的行,跳过空行,返回下一个非空值"""
for i in range(start, len(lines)):
if keyword in lines[i]:
for j in range(i + 1, min(i + 5, len(lines))):
val = lines[j].strip()
if val:
return val
break
return ""
# 提取换配电池数据:用"否"作为分界线
for j in range(rs, end):
if lines[j].strip() == '否':
delay_idx = j
break
# 跳过空行,取换配编码、每组节数、配发数量
non_empty = []
for j in range(delay_idx + 1, min(delay_idx + 20, end)):
s = lines[j].strip()
if s:
non_empty.append(s)
swap_code = non_empty[0] # 换配电池编码
swap_per_group = non_empty[1] # 每组节数
swap_qty = non_empty[2] # 换配配发数量
```
---
## 六、核心功能三:自动生成Excel表格
### 6.1 电池型号与分组规则
| 电池类型 | 电压 | 每组节数 | 示例 |
|---------|------|---------|------|
| 铅酸电池 | 48V | ÷4 | 44节 ÷ 4 = 11组 |
| 铅酸电池 | 60V | ÷5 | 50节 ÷ 5 = 10组 |
| 铅酸电池 | 72V | ÷6 | 72节 ÷ 6 = 12组 |
| **锂电池** | **任意** | **÷1** | 30节 ÷ 1 = 30组 |
> **踩坑**:锂电池的分组除数是1,不是按电压算!通过电池编码映射表中的 `type` 字段判断。
### 6.2 电池编码映射表
维护了一个JSON映射文件 `battery_code_map.json`(135条记录),结构如下:
```json
{
"105000244": {
"model": "铅酸电池48V20AH",
"factory": "天能",
"type": "铅酸电池"
},
"1050010000H": {
"model": "锂电池48V30AH",
"factory": "星恒",
"type": "锂电池"
}
}
```
### 6.3 Excel表格公式
| 列 | 公式 | 说明 |
|----|------|------|
| J(实际发货组) | `=I{n}/每组节数` | 实际发货(节)÷每组节数 |
| M(应欠节) | `=I{n}-G{n}` | 实际发货-应发数量 |
| N(应欠组) | `=M{n}/每组节数` | 应欠节÷每组节数 |
| O(现欠发节) | `=Q{n}+M{n}` | 上次累计欠发+本次应欠 |
---
## 七、从OCR方案到数据采集方案的演进
### 7.0 第一版:手工做表
纯手工,50张单子做一天。
### 7.1 第二版:OCR拍照识别
打印出来后拍照 → AI识别单据上的型号和数量 → 生成Excel。
**问题**:
- OCR识别率不稳定,数字容易粘连
- 数据顺序可能被AI打乱
- 需要人工逐条核对
### 7.2 第三版:直接从ERP采集(最终方案)
打印时同步采集ERP页面数据 → 解析文本 → 生成Excel。
**优势**:
- **错误率为0**:直接从ERP原始数据提取,不经过OCR
- **数据顺序不变**:原始文本解析,严格保持原始顺序
- **效率翻倍**:打印和采集同时完成,不再需要额外拍照步骤
---
## 八、踩坑记录与解决方案
### 坑1:Ctrl+A没有选中表格内容
**原因**:焦点不在表格上,全选的是整个页面。
**解决**:先点击表格区域(坐标550,380),再Ctrl+A。
### 坑2:不同单号的空行数量不一样
**原因**:ERP复制出来的文本中,标签和值之间的空行数量不固定。
**解决**:用"跳过空行取非空值"的方法,不依赖固定偏移量。
### 坑3:锂电池分组计算错误
**原因**:锂电池48V30AH,按48V÷4=10.5组,但实际应该是30÷1=30组。
**解决**:通过编码映射表的 `type` 字段判断,锂电池统一÷1。
### 坑4:双击发货单号会打开详情页
**原因**:从列表页复制单号时,双击单元格会触发跳转。
**解决**:只能用鼠标拖选方式复制,不能双击。
### 坑5:单元格行高不统一
**原因**:ERP表格的行高不是固定的,有的行高有的行矮。
**解决**:整列拖选后用正则解析,不逐行按固定行高操作。
---
## 九、项目文件结构
```
Claw/
├── print_erp.py # 主打印脚本(v12.0),含数据采集
├── gen_excel_standalone.py # 独立Excel生成脚本
├── battery_code_map.json # 电池编码映射表(135条)
├── clipboard_data/ # 采集数据存储目录
│ ├── ZCFH26032732.txt
│ ├── ZCFH26032740.txt
│ └── ...
├── test_query.py # 日期筛选查询测试
└── copy_order_list.py # 复制发货单号列表测试
```
---
## 十、效果对比
| 指标 | 自动化前 | 自动化后 |
|------|---------|---------|
| 打印50张单子 | ~2小时(手工) | ~20分钟(脚本) |
| 做Excel表格 | ~3小时(手工) | ~1分钟(自动生成) |
| 数据准确率 | ~95%(手工易错) | **100%**(原始数据直采) |
| 多页打印 | 手动选择页码 | 自动只打最后一页 |
---
## 十一、后续规划
1. **记账后数据核对**:装车后手工修改Excel实发数量 → ERP记账后导出数据 → AI自动核对两边是否一致
2. **整单发货管理页面自动查询**:自动填入日期、查询、读取单号列表
3. **全流程打通**:查询 → 读取单号 → 逐个打印+采集 → 生成Excel → 记账核对,一键完成
---
## 总结
这个项目的核心经验:
1. **AI辅助开发效率极高**:坐标定位、脚本调试、问题排查都可以让AI实时参与
2. **pyautogui + AI 是强大的组合**:AI负责"看"(截图分析)和"想"(逻辑处理),pyautogui负责"做"(鼠标键盘操作)
3. **先验证再编码**:每个坐标都通过用户手动验证,每个功能都经过实测确认
4. **从失败中迭代**:OCR方案失败后快速转向数据采集方案,最终实现了错误率为0的目标
希望这篇文章对有类似需求的朋友有所帮助!
---
*使用工具:WorkBuddy AI助手 + Python 3.13 + pyautogui + openpyxl*
*标签:#WorkBuddy #自动化 #ERP #pyautogui #办公效率*原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。