Files
cmshoppe/docs/tasks/T-632.md
T

120 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: T-632
title: ①采集短标题就绪识别与页面错误诊断修复
phase: 2
deps: [T-404b, T-630]
status: TODO
created: 2026-07-14
---
## 问题 / 背景
最新代码采集商品 `48363984966` 时,账号 Chrome/CDP 连通正常,程序打开的商品详情 tab 也已加载成功,但任务在约 63 秒后失败:
```text
step=wait_ready result=failed
detail=等待蝦皮商品编辑器就绪超时:此物流選項不支援較長備貨商品
```
运行事件只进入 `open_product -> wait_ready`,尚未执行 `read_title`、`read_cover` 或 `download_cover`。用户导出的商品图片区域 HTML 与实时只读 CDP 探查确认:
- 主图区域存在 `data-product-edit-field-unique-id="images"`,其中有 5 个可拖拽 `.shopee-image-manager__itembox`;5 张图片均已取得 `susercontent` CDN 地址。
- 该业务字段范围内存在 1 个主图上传 input;当前 `read_cover_src()` 也能正常取得第一张封面 URL。因此图片未加载和封面下载不是本次失败原因。
- 页面商品名称输入框位于 `data-product-edit-field-unique-id="name"` 下,真实 `value/modelvalue` 长度为 20。
- `app/editor.py` 当前 `TITLE_XPATH` 使用 `string-length(@modelvalue)>24` 猜测标题输入框;该商品命中数为 0,导致 `JS_READY` 即使图片和上传入口均存在也持续返回 `false`。
- 用 `data-product-edit-field-unique-id="name"` 作为标题业务字段定位后,实时页面准确命中 1 个标题输入框,标题/5张图片/上传入口三项就绪判断立即为 `true`。
错误末尾的“此物流選項不支援較長備貨商品”不是页面未加载原因。`_open_product_ready_error()` 当前在没有商品失效 toast 时,会把缓存中的最后一条任意 toast 直接拼到就绪超时后面;物流、备货或表单校验提示因此可能被误报为采集失败根因。
## 方案
### 1. 标题输入框改用业务字段定位
- 标题主定位改为 `data-product-edit-field-unique-id="name"` 范围内的文本输入框,例如:
```xpath
//*[@data-product-edit-field-unique-id='name']
//input[contains(concat(' ', normalize-space(@class), ' '), ' eds-input__input ')]
```
- 不再用标题字符数判断输入框身份;1~24 字、空值编辑态和长标题都必须识别到同一个商品名称字段。
- 不使用“页面第一个 `.eds-input__input`”或中文 `商品名稱` label 作为唯一定位,避免规格、价格、库存输入框误命中和站点语言变化。
- 为兼容尚未带 `data-product-edit-field-unique-id` 的旧页面,在主定位不存在时才回退当前已验证的旧 XPath;主定位命中时不得再通过长度启发式改选其他输入框。
- 抽取单一标题定位事实,让 `JS_READY`、`JS_TITLE_STATE`、`read_title()` 和 `change_title()` 共用;禁止采集、就绪判断和③更新各维护一份不同选择器。
- 若主定位命中多个可见标题输入框,返回明确诊断并停止,不猜测写入其中任意一个。
### 2. 主图区域限定到 `images` 业务字段
- 商品主图优先限定在 `data-product-edit-field-unique-id="images"` 业务字段内,再读取:
- `.shopee-image-manager`;
- 可拖拽且 `data-draggable="true"` 的主图 itembox;
- `.shopee-image-manager__upload input[type=file]`。
- 实时页面全局共有多个文件 input,但 `images` 业务字段内只有 1 个主图上传入口;就绪判断、读取封面、图片矩形、上传状态和点击上传块必须指向同一个主图 manager,不能由全局 `querySelector()` 分别命中不同图片组件。
- 对缺少业务字段属性的旧页面保留现有 itembox/upload 选择器回退;回退只能在业务字段根不存在时启用。
- 本节只收敛选择范围,不改变③更新封面的删除第一张、上传、等待 CDN、拖到第一位和点击更新流程。
### 3. 就绪判断返回可诊断快照
- 将当前只返回布尔值的 `JS_READY` 扩展为共享的就绪状态快照,至少包含:
- 商品名称主定位/回退定位命中数;
- 主图业务字段是否存在;
- 已加载主图数量及 CDN/blob 概况;
- 主图上传 input 是否存在;
- 当前 URL。
- `_wait_ready()` 仍按现有最多 60 秒预算轮询,不增加固定 sleep、不切换 Chrome 前台;成功条件仍为标题字段、至少一张商品主图和主图上传入口同时存在。
- 超时时按最后快照给出具体中文原因,例如:
```text
商品编辑器未就绪:未识别商品名称输入框;商品图片5张;主图上传入口正常
```
- 诊断快照写入现有 `run_log_events` 和本地脱敏日志,不保存标题全文、Cookie、token、上传文件内容或页面整份 HTML。
### 4. 区分页面错误与无关 toast
- 保持 T-404b 既有规则:明确商品失效、商品不存在、无权限或店铺不匹配类 toast,即使已经自动隐藏,也可作为 `open_product()` 的主要失败原因并驱动①列表显示“商品失效”。
- 非商品失效类 toast 不得无条件覆盖就绪快照。物流、较长备货、库存、价格、标题校验、图片重复等编辑表单提示不能被描述为“商品页加载失败”。
- 未分类 toast 只有在当前仍可见且与本次导航/页面打开直接相关时,才可作为附加“页面提示”显示;已经隐藏的普通 toast 只保留在脱敏诊断 payload,不拼成用户可见的主要错误原因。
- 若同时存在明确商品失效 toast 和选择器缺失,商品失效优先;否则优先显示缺失的就绪组件,并把普通 toast 标为可能无关的诊断信息。
### 5. 测试与真实页面验证
- 更新 editor 单元测试,覆盖:
- 20 字及 1~24 字短标题通过业务字段主定位并判定页面就绪;
- 长标题仍命中同一业务字段;
- 旧 DOM 缺少业务字段时旧 XPath 回退仍可用;
- 页面有大量规格/价格/库存 `.eds-input__input` 时不误选;
- 主定位命中多个候选时明确失败,不猜测;
- `read_title()` 与 `change_title()` 使用同一标题节点,并继续校验 `value == modelvalue == 新标题`;
- 主图业务字段内图片、上传 input 和上传状态来自同一个 manager;旧 DOM 回退不回归;
- 图片与上传入口正常、仅标题旧启发式不命中的页面,会报告“未识别商品名称输入框”;
- 隐藏的“此物流選項不支援較長備貨商品”不再成为加载失败主因;明确商品失效 toast 仍按 T-404b 上浮。
- 使用商品 `48363984966` 做只读 CDP 验证:新就绪快照须为标题1个、主图5张、主图上传入口1个;`read_title()` 非空、`read_cover_src()` 为 `susercontent`,①采集可进入 `read_title/read_cover/download_cover/db_write` 并成功关闭本轮自动创建 tab。
- 标题写入路径只在仓库指定测试商品或 mock 中验证,不在该运营商品上点击「更新」提交;测试商品失效导致实跑不可用时如实记录限制。
- 选择器变化后同步 `docs/04-architecture.md` 第七节与 `docs/api.md`,不得继续把标题长度启发式记作当前唯一已验证事实。
## 验收要点
- 标题不足25个字符的有效商品不再卡在 `wait_ready`;页面图片已加载时能正常采集标题和第一张封面。
- 标题识别基于 `name` 业务字段,不会误选规格、价格、库存、物流或货号输入框;旧页面仍有受控回退。
- 商品名称、主图 itembox 和上传入口分别来自明确业务字段,且所有读写/就绪脚本复用同一定位事实。
- 就绪超时能明确指出标题、主图或上传入口哪一项缺失,不再只显示泛化超时。
- 物流/备货等无关且已隐藏 toast 不再伪装成商品页面加载失败;商品失效/无权限 toast 捕获不回归。
- 不改变①后台采集、不切前台、只关闭程序自动创建 tab的策略;不改变③封面上传、拖拽、确认和线上提交边界。
- 自动验证:
- `py -3.10 -m unittest tests.test_editor_login`
- `py -3.10 -m unittest tests.test_gui`
- `py -3.10 -m unittest discover -s tests`
- `python -m ruff check app tests main.py`
- `py -3.10 -m compileall app main.py`
- `git diff --check`
## 边界(不改什么)
- 不通过降低为 `document.readyState`、只检查 URL 或取消关键字段检查来掩盖问题。
- 不通过固定等待更久、把60秒改得更大或切换 Chrome 前台解决选择器错误。
- 不修改登录检测、账号 Chrome 启动、只读 tab 生命周期或①采集并发策略。
- 不修改③删除第一张封面、上传、拖拽第一位、更新确认框和成功后关闭 tab 的业务顺序,不点击线上「更新」做本任务验收。
- 不修改 SQLite/Excel schema、AI/cmhub、提示词或 GUI 布局。
- 不提交用户导出的 `48363984966.html`、运营 Excel、`data/` 日志、账号登录态或其他业务数据。