AOZHOU 价格提取 Skill
OCR 识别澳洲空运价格图片 → 24列标准输出 → 上传 WPS。
⚠️ 铁律:仅提取用户最新提交图片,禁止复制历史数据
- 禁止查看/引用/复制任何历史脚本中的 price/data 数组
- 禁止从历史输出文件直接读取数据
- 禁止硬编码历史图片路径作为源文件
- 每次提取必须从用户当前提交文件全新 OCR 识别
- 有就是有,没有就是没有:不得用历史数据填补缺失值
一、核心参数
| 参数 | 值 |
|------|-----|
| 起始港 | PVG |
| 价格编号 | aozhou_aozhou_001 |
| 来源 | aozhou |
| 引擎 | ocr_utils(跨平台统一 RapidOCR 封装,~/.workbuddy/skills/ocr_utils.py) |
| 输入格式 | PNG 图片 |
| 45KG | 有值(图片含45KG列) |
二、强制流程
1. 确认图片路径
2. ocr_utils.scan_to_log() 扫描 → 日志输出
3. 按 Y 坐标分组识别数据块
4. ocr_utils.find_price_at_xy() 逐行提取 → 比重推导
5. 生成 24 列 DataFrame
6. 保存 → 上传 WPS → 删除临时文件
2.1 OCR 统一导入(所有平台一致)
import sys, os
sys.path.insert(0, os.path.expanduser('~/.workbuddy/skills'))
from ocr_utils import scan_image, scan_to_log, find_price_at_xy, find_text_at_xy, check_env
check_env() # 启动时自检,确保引擎版本一致
三、目的地数据块配置 ⚠️ 最可能变化
3.1 生效日期(每个目的地不同)
| 目的地 | 生效日期 | 航司 | 频次 | |--------|---------|------|------| | AKL | 2026-04-24 | MU | Daily | | BNE | 2026-04-18 | MU | D24567 | | MEL | 2026-04-23 | MU/HO | Daily/D246 | | SYD | 2026-04-15 | MU/HO | Daily/D1357 |
3.2 备注(每个目的地不同)
| 目的地 | 备注 | |--------|------| | AKL | MUSTGO +5, 比重价格需要满300KG起 | | BNE | MUST GO +5, 比重价格需要满300KG起 | | MEL | 指定航司+2, 托书请备注有无电池, MUST GO运价+5 | | SYD | 指定航司+2, 托书请备注有无电池, MUST GO运价+5 |
四、OCR 坐标提取(核心)
4.1 价格列 X 坐标范围
| 重量段 | AKL X范围 | BNE X范围 | MEL X范围 | SYD X范围 | |--------|-----------|-----------|-----------|-----------| | 45KG | 320-340 | 335-345 | 330-345 | 330-345 | | 100KG | 460-480 | 460-475 | 460-475 | 460-475 | | 300KG | 555-565 | 550-560 | 550-560 | 645-660 | | 500KG | 650-660 | 645-655 | 645-655 | 720-730 | | 1000KG | 745-760 | 745-755 | 745-755 | 860-875 |
⚠️ 新图片 X 范围可能变化:需要对照图片重新标定。
4.2 数据行 Y 坐标(每个目的地)
AKL(4 行,Y=180-520):
209 平托 → 托盘, 238 平散 → 散货, 268 1:120以下散 → 散货
300 1:100以下散 → 散货, 331 1:300托盘 → 托盘, 360 1:300散货 → 散货
387 1:500托盘 → 托盘, 420 1:500散货 → 散货, 450 1:800托盘 → 托盘
480 1:800散货 → 散货, 511 1:1000散托 → 散货/托盘
BNE(3 行,Y=697-980):
742 平托 → 托盘, 778 平散 → 散货, 811 1:120以内泡散 → 散货
865 1:100以内泡散 → 散货, 917 1:300散托 → 散货/托盘
949 1:500散托 → 散货/托盘, 977 1:800散托 → 散货/托盘
MEL(2 行,Y=1227-1700):
1305 平托 → 托盘, 1374 平散 → 散货, 1442 1:100以内限散 → 散货
1506 1:250散 → 散货, 1572 1:300托 → 托盘, 1637 1:300散 → 散货
1698 1:500散+托 → 散货/托盘
SYD(3 行,Y=1954-2380):
2016 平托 → 托盘, 2061 平散 → 散货, 2107 1:100以内限散 → 散货
2140 1:250散 → 散货, 2199 1:300托 → 托盘, 2241 1:300散 → 散货
2287 1:400散+托 → 散货/托盘, 2333 1:500散+托 → 散货/托盘
2378 1:800散+托 → 散货/托盘
⚠️ 新图片 Y 坐标必须重新标定:用 OCR 日志对齐文本。
五、货型清理规则
def clean_cargo_type(cargo_str):
cargo_str = cargo_str.replace(':', ':').replace(' ', '')
# 平托/平散 → 1:1
if '平托' in cargo_str or '平散' in cargo_str:
return '1:1'
# 1:XXX格式 → 保留
match = re.search(r'1:(\d+)', cargo_str)
if match:
return f"1:{match.group(1)}"
return cargo_str
最终货型格式:1:1、1:100、1:120、1:250、1:300、1:400、1:500、1:800、1:1000
六、比重推导(calc_ratio_range)
与 Excel 数据源不同,AOZHOU 使用自定义推导(非 ratio_engine):
# 正常 1:XXX → start=XXX, end=下一个更大比的start-1
# "1:XXX以下/以内/限" → start=0, end=XXX
# 末档 → end=9999
关键规则:相同比重的多行(如 1:300 托盘 + 1:300 散货)共享同一结束比重。
七、散托类型判断
| 条件 | 散托类型 |
|------|---------|
| cargo_str 含「托」(且不是散货/托盘)| 托盘 |
| st_type 是「散货/托盘」| 散货/托盘 |
| 其他 | 散货 |
⚠️ 优先级:cargo_str 判断 > st_type 默认值。
八、价格提取(find_price_at_xy)
def find_price_at_xy(target_ys, x_min, x_max):
# Y 容差 ±5 像素
# X 必须在 x_min ~ x_max 范围内
# 匹配纯数字(含小数)
# 返回 float
- 支持
target_ys为 list(多行范围) - 正则:
^\d+(\.\d+)?$
九、常见坑
坑1:X 坐标偏移
新图片排版不同时,价格列 X 范围需要重新标定。对照 OCR 日志逐列验证。
坑2:Y 坐标漂移
OCR 对同一图片的 Y 坐标可能有 ±3px 偏差,abs(y - target_y) <= 5 容差处理。
坑3:1000KG 可能在下一行
AKL 1000KG 搜索范围扩大到了 [y-2, y+3]。
坑4:BNE 1000KG OCR 异常
OCR 可能误读价格,需检查 1000KG > 100KG 时用 100KG 值替换。
坑5:int() 转换丢失小数
价格直接用 int() 而非 float,注意检查是否有小数价格。
🔴 坑6:凭记忆看图片 → 复制历史数据(0608惨痛教训)
错误:AKL/BNE/MEL/SYD 四个已有航线的价格,实际输出的是旧批次数据,而非 0608 图片数据。
- AKL-MU 平托:应该 32/30/30/30/30,输出成了旧数据的 32/20/19/17/17
- SYD-MU 1:300托:应该 32/32/32,输出成了旧数据的 37/37/37
- BNE 列格式:新图片是标准5列(45/100/300/500/1000),旧脚本是非标准列(100/500/1000)
铁律:
- ❌ 禁止"扫一眼图片"就标记为熟悉航线,凭记忆填数据
- ✅ 必须逐行跑 RapidOCR,逐格对照 OCR 日志填入数据
- ✅ 即使是"已有航线",也必须基于当前图片 OCR 录入
- ✅ ZRH/ARN/CAI 新航线数据正确,恰恰证明了「新航线 = 强制看图片 = 不出错」
🔴 坑7:OCR Y坐标段归属串位(0608教训)
错误:OCR Y=2346 的 1:800散+托=24/24 属于 MEL 段,但被错误复制到 SYD 段。
SYD 段实际末行为 Y=3315 1:500散+托=29/29/29,图中无 1:800。
铁律:每条航线提取完成后,必须回看图片确认首行和末行的货型是否正确,防止 OCR 坐标串位。
坑8:MUSTGO +5 ≠ 散托增量(0608教训)
错误:将 MUSTGO +5 / MUST GO +5 当作散托增量(散托增量=5)。
MUSTGO 是优先货物附加费,不是托盘加价。
铁律:AOZHOU 图片中 没有散托增量(无「托盘+N元/kg」字样),散托增量列全部留空。
坑9:卡车转运段不提取(0608教训)
错误:提取了「BNE/ADL/PER 中转统配 JL/HO/TG/PR」和「SYD/MEL 中转统配 NH/JL/TG/5J/AI/PR」。 这些都是 卡车转运/地面运输价格,非空运报价,不应提取。
铁律:图片中含「中转」「统配」「卡车点」等字样的段落 → 跳过不提取。
坑10:OCR 未捕获货型标签时禁止猜测(0608教训)
错误:CK-AKL 首行 OCR 未捕获货型标签(Y=604 仅有数字 40/33/33/33/33),猜测填「散托」。 实际应为「平托→1:1」。
铁律:OCR 缺失货型标签时,对照图片人工确认,不能凭价格高低推测货型。
坑11:图片中斜杠(/)价格不得补填(0608教训)
错误:MEL 1:800 图片中 300KG 列为斜杠(/),脚本中填入了 24。 1:800 比率下 300KG 不在报价范围,应为空。
铁律:图片标注「/」的格子 → 输出留空,禁止用相邻列价格补填。
坑12:全段同价货物不拆分比率(0608教训)
错误:早期将「平托」一行拆分成多个比率段(1:1→1:100→1:200→...),产生重复数据。
铁律:平托/平散/单机 等全段同价货物 → 单行输出,范围 0→9999,货型 1:1。
#11 ZRH/ARN(MU)
| 项目 | 值 |
|------|-----|
| 源文件 | 用户提交 |
| 生效日期 | 从图片标题提取;0618 图片中 ZRH 为 2026-06-18,ARN 为 2026-06-22 |
| 价格编号 | aozhou_aozhou_001 |
| 航司 | MU |
| 提取范围 | 仅提取 ZRH/ARN 主运价区;下方 卡车点 转运费表不提取 |
| ZRH | 45KG 全空;频次 D247;备注 6.18首航, 不能锂电池/不分泡, MUSTGO +5, 不可锂电池 |
| ARN | 45KG 全空;频次 D146;备注 6.22首航, 不能锂电池, 5/5分泡, 大盘底泡托盘不接, MUST GO +5 |
| 行数 | ZRH 9 行、ARN 7 行;仅按图片主运价逐行输出,不展开卡车点 |
| 比重 | 平托/平散为 1:1(0-9999);1:100内、1:80内、1:120内 为 0-XXX;普通链式档按图片实际档位到下一档,末档到 9999 |
| 散托类型 | 按图片标签保留托盘/散货/散货/托盘;MUSTGO +5 只写备注,不写散托增量 |
十、脚本模板
脚本模板:/Users/hantao/WorkBuddy/oone/extract_aozhou.py
图片:用户提交(可通过命令行参数指定)
新图片时需更新:
- 所有目的地的 OCR 数据 → 手动录入 data 数组
- 生效日期(对应图片标题日期)
- 备注信息(MUSTGO 仅放备注列)
- 确认无卡车转运段数据
- 确认散托增量全部为空
- 逐像素核对 OCR 数据与图片一致
- 0615 这类长图可按主运价分段处理:AKL、BNE、ADL、MEL、SYD;中间的“中转统配/卡车点”整段跳过
- 目的港、备注、航司只取主运价区;不要把中转统配段混入主表
Scan to join WeChat group