From 2f6b9f7af4bc1a64e947ab03c5f217e9b9f6d7dd Mon Sep 17 00:00:00 2001 From: chengma Date: Tue, 14 Jul 2026 16:14:24 +0800 Subject: [PATCH] docs(tasks): add short-title collection readiness fix --- docs/tasks/T-632.md | 119 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 docs/tasks/T-632.md diff --git a/docs/tasks/T-632.md b/docs/tasks/T-632.md new file mode 100644 index 0000000..64af2ca --- /dev/null +++ b/docs/tasks/T-632.md @@ -0,0 +1,119 @@ +--- +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/` 日志、账号登录态或其他业务数据。