俄罗斯航线图片价格提取 Skill
从价格表图片(JPG/PNG)中提取俄罗斯及部分欧洲航线空运价格,输出 24 列标准 Excel。
⚠️ 铁律:仅提取用户最新提交文件,禁止复制历史数据
- 禁止查看/引用/复制任何历史脚本中的 price/data 数组
- 禁止从历史输出文件直接读取数据
- 禁止硬编码历史文件路径作为源文件
- 每次提取必须从用户当前提交文件全新读取
- 有就是有,没有就是没有:不得用历史数据填补缺失值
⚠️ 跨平台 OCR 铁律
必须使用 ~/.workbuddy/skills/ocr_utils.py 统一 OCR 模块,禁止内联 from rapidocr_onnxruntime import RapidOCR。
一、核心参数
| 参数 | 值 |
|------|-----|
| 起始港 | 动态(PEK/PVG/CAN/SZX/TAO,按图片内容识别) |
| 目的港 | SVO/LED/SVX/AMS(按图片内容识别) |
| 价格编号 | russia_img_{批次日期}_{图片序号}(如 russia_img_0916_3816;序号取源图文件名里的图片序号,无序号时 001)。一天多张价表(如 CA/CZ/HU 一张、MU 东航一张)必须各占一个编号,撞号会被 ERP 整表覆盖 |
| 来源 | RUSSIA(claw-lite 原生写卡器 cards.card_source():技能名含 russia → RUSSIA,兜底才是 IMAGE)。生产 0916 东航图实测输出 RUSSIA;旧文档写的 IMG 不存在于 claw-lite,别照抄 |
| 引擎 | OCR(ocr_utils.scan_image) |
| 输入格式 | JPG/PNG 图片 |
二、港口映射规则
| 图片原始文本 | 输出港口代码 | 说明 |
|-------------|-------------|------|
| 北京 / PKX / 大兴 | PEK | 北京统一输出 PEK,覆盖 PKX |
| 上海(未声明港口) | PVG | 默认浦东 |
| 广州(未声明港口) | CAN | 默认白云 |
| 青岛 / TAO | TAO | |
| 深圳 / SZX | SZX | |
| 西安 / XIY | XIY | 东航 67 号表出现(XIY-SV01) |
| 喀山 KZN | KZN | 东航 67 号表出现(PVG-KZN) |
| 莫斯科 SVO1(OCR: SV01) | SVO1 | SVO1/SVO2 是两个不同港口,不可合并 |
| 莫斯科 SVO2(OCR: SV02) | SVO2 | |
| 莫斯科 SVO(OCR: SVO,无数字)| SVO | 仅当 OCR 不带数字时使用 |
| 圣彼得堡 LED | LED | |
| 叶卡捷琳堡 SVX | SVX | |
| 阿姆斯特丹 AMS | AMS | |
表是举例,不是白名单:图上写 KZN / KJA / OVB / VVO 这类清单外三字码一律照抄, 禁止留空。下游若把港名做成白名单会把整块 origin/dest 吞成空,24 列校验随即判 「起始港/目的港不是三字码」,任务进入 16 轮重试死循环(0917 东航图实测)。
三、字段规则
货型转换
| 原始 | 输出 |
|------|------|
| 平 / 散件 / 平货 / 普货 | 1:1 |
| 1<XXX(OCR 误识别) | 1:XXX |
| 1:XXX | 1:XXX |
| >XXX | >XXX |
| <XXX | <XXX |
散托类型判断
- 备注含
托盘+N→ 散托类型 =散货,增量 = N(填入第24列) - 无增量 → 默认
散货/托盘 - 航司明确标注散件 →
散货
日期提取
| 原始格式 | 输出 |
|---------|------|
| 2026/7/17 | 2026-07-17 00:00:00 |
| 7.13-7.19 | 2026-07-13 00:00:00(取起始日期) |
| 2026/7/12(18) | 2026-07-12 00:00:00 |
| 图片标题含更新日期 | 以各航司块标注的生效日期为准 |
频次归一化
| 原始 | 输出 |
|------|------|
| Daily | Daily |
| 1234567 | Daily |
| 12467 | D12467 |
| D347 | D347 |
| 1357 | D1357 |
四、列 X 坐标范围(通用)
M: 255-315
N: 340-400
Q45: 415-490 (或 20KG以下: 415-495,映射到45KG)
Q100: 505-570
Q300: 590-655
Q500: 675-740
Q1000: 760-825
注意:不同航司块列结构可能不同:
- CA 块:M/N/20KG以下/Q100/Q300/Q1000(无 Q45/Q500)
- ⚠️ CA PEK-SVO2 的「20KG以下」列价格忽略,该块 45KG 列留空(仅当前技能规则)
- MU 块:Q100/Q300/Q500/Q1000(无 M/N/Q45)
- 其他:标准 M/N/Q45/Q100/Q300/Q500/Q1000
五、提取流程
1. 确认输入图片路径
2. scan_image(IMG) 获取 OCR items
3. 按航司块逐块提取:
a. 识别航司、起始港、目的港、生效日期、频次
b. 按 y 坐标定位每行比重
c. 比重升序排序 → 链式区间推导(每档 end=下一档 start,末档=9999)
d. 按 x 坐标范围提取各重量段价格
e. 解析备注中的散托增量
4. 24列 DataFrame → columns_24.COL_24
5. 校验 → 上传 WPS
六、链式区间推导
比重按以下顺序排序后推导区间:
1:1(最前)1:XXX(按 XXX 升序)>XXX(按 XXX 升序)
每档 end = 下一档 start,末档 end = 9999。
坑1:>N 型比重的 end 不是固定 9999,而是下一个 1:N 档的 N 值(如有)。
七、MU 三程联程特殊处理
MU 航线(如 PKX/SZX/CAN-PVG-AMS)需拆分为多条记录:
- 每个起始港单独一条记录
- 转运点填
PVG - MU 块可能无 M/N/Q45 列
八、24列输出
使用 workspace/shared/columns_24.py 的 COL_24 常量。
九、常见坑
| # | 坑 | 解决方案 |
|---|-----|---------|
| 1 | 1<130 OCR 误识别 | 统一转为 1:130 |
| 2 | >N 型比重 end 错误 | end 取下一个 1:N 档的 N,非 9999 |
| 3 | CA PEK-SVO2 块「20KG以下」列 | 忽略,45KG 留空(仅当前 Skill 规则,非通用) |
| 4 | MU 块无 M/N/Q45 | 留空,不填 0 |
| 5 | MU 三程联程 | 拆分为多条记录,填转运点 |
| 6 | 北京 PKX | 统一输出 PEK |
| 7 | 散托增量从备注提取 | 匹配 托盘+N,散托类型强制为散货 |
| 8 | SVO1/SVO2 是两个不同港口 | 保留数字不合并,OCR 中 SV01→SVO1、SV02→SVO2 |
| 9 | 同一张图里某块没印生效日 | 留空,禁止借用别块的日期。0916 图 上海SU-SV01 / 广州SU-SV01 两块就没印,黄金那两行 开始生效日期 为空。24 列校验器据此放行「同 (目的港,航司) 组内已有带日期的行」的空值(整列全空仍报错) |
| 10 | 目的港是 4 字符(SVO1/SVO2) | 下游严格校验器必须允许 ^[A-Z]{3}\d?$,否则整块被判非法丢掉(曾因此只出 3 行) |
| 11 | 长图切条后某块表头被切在段外 | 该残块(只有数据行、没有块头)由下游 cards._russia_drop_headerless_repeats 确定性丢弃,判据 = 同 (起始港,目的港) 已有带航司的正规块且残块档位是其档位子集(双条件,避免误杀源图本来没印航司的块)。禁止拿起始港冒充块头——PEK / PEK-SVO1 会被解析出 EK 假航司,残块就再也认不出来。同时不要主动跳过残块:把整段判成残块返 0 条会白跑一轮 |
| 12 | 清单外的新起始港/目的港(西安 XIY、喀山 KZN) | 照抄,禁止留空。上面那张表只是举例不是白名单;下游若把港名做成白名单会把整块 origin/dest 吞成空 → 24 列校验判「起始港/目的港不是三字码」→ 16 轮重试死循环(0917 东航图实测)。claw-lite 侧已按 ^[A-Z]{3}$ / ^[A-Z]{3}\d?$ 放行 |
| 13 | 东航 MU 表(列头 Flight|Route|Frequency|Density|Min|N|Q100|Q300|Q500|Q1000|Pallet|REMARKS) | 版式是左右两栏并排、一张图 3~5 块(0916 图左栏 PVG-SV01/PKX-SV01/XIY-SV01,右栏 PVG-LED/PVG-KZN),逐块都要读,禁止只读左栏。航司取 Flight 列前缀(MU591/MU5065 → MU);Q100/Q300/Q500/Q1000 → 100/300/500/1000KG;Min(最低收费)与 N 不属于五档,本表无 Q45 列 → 45KG 留空;Density 1: 100 / > 167 / > 300 / > 500 照抄去空格;Pallet 列 托盘+N → 该行散托增量,保舱+5/无保舱服务/重货仓位有限,单独确认 与该块 REMARKS 进备注;生效日取块右上 Effective from |
| 14 | 某档五个 Q 列全是 /(该档不报价) | 整行不提取(同「五档全空/单询不提取」)。该档仍参与链式推导,所以上一档的结束比重停在它的开始值,不要拉成 9999(0916 东航图 PVG-LED / PVG-KZN 的 >500 行) |
十、脚本模板
参考:workspace/0713/extract_new_img_0713.py
关键函数:
scan_image(IMG)→ OCR itemsfind_price(items, y_center, x_range)→ 在指定坐标找价格normalize_ratio(text)→ 货型标准化ratio_to_range(ratio, next_ratio)→ 比重转区间build_block(items, origin, dest, carrier, eff_date, freq, rows, col_def, ...)→ 构建航司块upload_excel_and_notify(OUT, name)→ 上传 WPS
坑18:同一图片二次提交必须全新识别(0824教训)⚠️ 铁律
背景:用户对同一张图片可能多次提交(如调价后重新发图)。文件名相同不代表内容相同——第二次提交可能是全新价格版本。
铁律:
- 禁止从历史批次/记忆复制任何价格数据,每次提交都按当前图片重新识别全部格子
- 同名图片二次提交时,逐格对比新旧版本:低比重/中档/高档均可能调价(如 +2/+1/-1)
- 模糊数字必须多参数补验(放大倍数 4x~10x、对比度增强),仍无法确认时人工裁决,不得猜
- 调价对比结果只记录差异格,输出以本次识别为准
Scan to join WeChat group