- 为 build_studio_urls.py 和 lookup_groups.py 添加 PEP 723 依赖声明 - 修复 HTTP/2 伪头导致 requests 抛 InvalidHeader 的问题 - 添加 CDP 端口连通性预检查及启动指引
152 lines
11 KiB
Markdown
152 lines
11 KiB
Markdown
# StudioSkillsSet 四技能使用问题复盘与优化建议
|
||
|
||
> 记录日期:2026-08-26
|
||
> 涉及技能:`uv-env-setup`、`yt-studio-groupid-lookup`、`yt-studio-url-builder`、`youtube-studio-csv-download`
|
||
> 数据链路:群组名 →(groupid-lookup)→ groupId →(url-builder)→ explore URL →(csv-download)→ CSV zip
|
||
|
||
---
|
||
|
||
## 一、结论速览
|
||
|
||
| 技能 | 是否遇到问题 | 问题是否已解决 | 是否需要优化 | 优化紧急度 |
|
||
|---|---|---|---|---|
|
||
| uv-env-setup | 是(命令不可用) | 是 | 是(轻) | 低 |
|
||
| yt-studio-groupid-lookup | 是(端口/登录/401/伪头) | 是 | 是 | 中 |
|
||
| yt-studio-url-builder | 否(但存在隐性前置依赖) | 是 | 是(轻) | 低 |
|
||
| youtube-studio-csv-download | 是(登录/按钮定位/base64 解码) | 是 | **是** | **高** |
|
||
|
||
整体结论:4 个技能功能均跑通,最终产物(6 份 CSV zip)全部有效。其中 `youtube-studio-csv-download` 暴露了 **2 处真实脚本缺陷**(导出按钮定位、`zippedData` 解码),是本次最需要回填到技能脚本的修复点。
|
||
|
||
---
|
||
|
||
## 二、uv-env-setup(环境准备)
|
||
|
||
### 问题 1:`uv` 命令不被识别
|
||
|
||
- **现象**:执行 `uv --version`、`uv sync` 报 `The term 'uv' is not recognized`。
|
||
- **为什么会出现**:`uv` 刚安装,当前 PowerShell 会话的 PATH 未刷新;或者安装方式(如 `pip install uv`)没有把可执行文件加入 PATH。
|
||
- **解决方法**:改用 `python -m uv` 调用;或在后续会话中直接调用项目已有的 `.venv\Scripts\python.exe` 绕过 uv 代理。
|
||
- **如何避免**:安装后重开终端;或脚本执行统一改为"优先 `.venv\Scripts\python.exe` 直调,回退 `uv run`"。
|
||
|
||
### 问题 2:已有 `.venv` 但缺 `pyproject.toml`
|
||
|
||
- **现象**:项目已存在 Python 3.12.6 的 `.venv`(内含 pandas/openpyxl/playwright),但没有 `pyproject.toml`。
|
||
- **为什么会出现**:原环境是手动 `pip install` 搭出来的,不是 uv 管理。
|
||
- **解决方法**:新建 `pyproject.toml` 声明依赖作为唯一事实来源,`uv sync` 复用现有 Python 生成 `uv.lock`。
|
||
- **如何避免**:首次迁移到 uv 时先补 `pyproject.toml`,再 `uv sync`。
|
||
|
||
**是否已解决**:是。环境最终就绪,三条自检(`import pandas/openpyxl/playwright`、`--selftest`、`--help`)通过。
|
||
|
||
---
|
||
|
||
## 三、yt-studio-groupid-lookup(群组名 → groupId)
|
||
|
||
### 问题 1:Chrome 调试端口 9222 连不上
|
||
|
||
- **现象**:`--connect http://localhost:9222` 连不上,报"无法连接到远程服务器"。
|
||
- **为什么会出现**:Chrome 未以 `--remote-debugging-port=9222` 启动。
|
||
- **解决方法**:用带调试端口的命令重启 Chrome。
|
||
- **如何避免**:运行前先 `Invoke-WebRequest http://localhost:9222/json/version` 探测端口是否在线。
|
||
|
||
### 问题 2:Chrome 进程未完全退出,调试端口参数被忽略
|
||
|
||
- **现象**:已带参数重启 Chrome,但 9222 仍不通。
|
||
- **为什么会出现**:后台仍有 Chrome 残留进程,新启动实例的 `--remote-debugging-port` 被已运行实例"吞掉"(Chrome 单实例复用机制)。
|
||
- **解决方法**:完全退出所有 Chrome 进程后再启动调试实例,或者改用独立 `--user-data-dir` 隔离启动。
|
||
- **如何避免**:启动前检测残留进程;或固定使用独立 profile 目录 + 调试端口。
|
||
|
||
### 问题 3:回放请求全量 401
|
||
|
||
- **现象**:`search_groups` 回放返回 `HTTP401`。
|
||
- **为什么会出现**:套件过期(鉴权头是逐请求计算的),或捕获时鉴权头不全。
|
||
- **解决方法**:重新在线捕获套件,更新 `bundles.json`。
|
||
- **如何避免**:套件一次性用完即弃;长期复用前先重新捕获校验。
|
||
|
||
### 问题 4:HTTP/2 伪头导致 `InvalidHeader` 报错
|
||
|
||
- **现象**:`requests` 抛 `InvalidHeader: Invalid leading whitespace...`。
|
||
- **为什么会出现**:捕获到的请求头里残留了 HTTP/2 伪头 `:authority`、`:method`、`:path`、`:scheme`,这些带冒号前缀的伪头不能被 `requests` 直接作为普通请求头发送。
|
||
- **解决方法**:从 `bundles.json` 中手动删除这 4 个伪头字段。
|
||
- **如何避免**:脚本捕获时自动过滤伪头,而不是留到下游报错后人工处理。
|
||
|
||
**是否已解决**:是。3 个群组名全部解析出 groupId 并标注归属所有者(产物 `群组ID结果-需求输入-测试.xlsx`)。
|
||
|
||
---
|
||
|
||
## 四、yt-studio-url-builder(生成 explore URL)
|
||
|
||
- **现象**:本身运行无报错,从需求清单成功生成 6 条 URL。
|
||
- **隐性前置依赖(潜在问题)**:URL 中 `entity_id` 必须是群组的 groupId,清单里只有群组名、没有 ID 时会缺 `entity_id` 而失败。本次实际流程正是"先 groupid-lookup 填 ID,再 url-builder 生成 URL"。
|
||
- **如何避免**:在 SKILL.md 中显式注明"清单若只有群组名,需先经 groupid-lookup 补 entity_id"。
|
||
|
||
**是否已解决**:是。产物 `studio_urls_需求输入-测试.csv` 含 6 条有效 URL。
|
||
|
||
---
|
||
|
||
## 五、youtube-studio-csv-download(下载 CSV)
|
||
|
||
这是问题最集中的技能,存在 2 处需要回填的脚本缺陷。
|
||
|
||
### 问题 1:登录态失效,跳转 Google 登录页
|
||
|
||
- **现象**:用已保存的 `cdp-profile`(Chrome)和 `edge-yts-debug-profile`(Edge)启动,页面都停在 `accounts.google.com` 登录页。
|
||
- **为什么会出现**:两个 profile 里保存的 YouTube Studio 登录会话已过期。注意:Edge 配置目录的 Cookies 文件里仍能看到 SAPISID/SID/__Secure-3PSID 等 cookie,但**cookie 存在不等于会话仍有效**,Google 仍要求重新登录。
|
||
- **解决方法**:由用户在浏览器中手动完成一次登录,之后复用该已登录会话。
|
||
- **如何避免**:下载前先探测页面是否跳登录页(脚本已有该判断,但只在跳转后提示);长期任务建议在开始前确认一遍登录态。
|
||
|
||
### 问题 2:导出按钮定位失败(脚本缺陷 1)
|
||
|
||
- **现象**:`page.get_by_text("导出当前视图")` 点击超时 30s,日志 `waiting for get_by_text("导出当前视图")`。
|
||
- **为什么会出现**:当前 YouTube Studio「高级模式」页的导出入口是右上角的**下载图标按钮**,它只有 `aria-label="导出当前视图"`,**没有可见文本**。脚本用 `get_by_text`(文本匹配)自然匹配不到。
|
||
- **解决方法**:改用 `page.get_by_label("导出当前视图")`(或 `get_by_role("button", name=...)`、`[aria-label=...]`),已验证三种方式都能命中 `count=1`。
|
||
- **如何避免**:对"图标类按钮"优先用 `aria-label` 定位,而非可见文本。
|
||
|
||
### 问题 3:`zippedData` 解码失败 / 生成的 zip 损坏(脚本缺陷 2,核心)
|
||
|
||
- **现象**:拦截到 `csv_export` 响应后,`base64.b64decode(zippedData)` 报 `Incorrect padding`;即使解码未抛错,写出的 zip 文件用 `zipfile` 打开报 `BadZipFile: Bad magic number for central directory`(文件尾部无 `PK\x05\x06` 结束记录)。
|
||
- **为什么会出现**:响应中 `zippedData` 字段是 **URL-safe base64**(用 `-`、`_` 替代 `+`、`/`)且**去掉了 `=` 填充**。脚本用标准 `base64.b64decode` 解码:遇到 `-`/`_` 或非 4 对齐长度时,要么抛 padding 错,要么静默解出损坏字节流,导致 zip 不完整。
|
||
- **排查过程**:诊断脚本发现响应 `status=200`、`content-encoding: br`(Brotli),但 Playwright 已自动解压、`resp.json()` 能正常解析,因此排除了"响应被压缩截断"的猜想,最终锁定是 base64 变体问题。
|
||
- **解决方法**:解码前补 `=` 填充(`z += "=" * (-len(z) % 4)`),并用 `base64.b64decode(z, altchars=b"-_")` 指定 URL-safe 字符表。
|
||
- **如何避免**:对来自前端接口的 base64 字段,先确认是否为 URL-safe + 无填充变体再解码;解码后增加 `zipfile.testzip()` 完整性校验。
|
||
|
||
**是否已解决**:是。修正后 6/6 条全部下载成功,每个 zip 含 `表格数据.csv` / `图表数据.csv` / `总计.csv`,抽查验证美国筛选、按频道明细、按日收益均正确。
|
||
|
||
---
|
||
|
||
## 六、执行过程与原 skill 的差异对比
|
||
|
||
| 环节 | 原 skill 脚本行为 | 本次实际执行行为 | 差异原因 | 如何回归/解决 |
|
||
|---|---|---|---|---|
|
||
| csv-download:导出按钮 | `get_by_text("导出当前视图")` | `get_by_label("导出当前视图")` | 前端已改为图标按钮,无可见文本,原定位失效 | 回填脚本:优先 `get_by_label`,可保留 `get_by_text` 兜底 |
|
||
| csv-download:zip 解码 | `base64.b64decode(zipped)` | 补 `=` 填充 + `altchars=b"-_"` | 接口返回 URL-safe、无填充 base64 | 回填脚本:改用 URL-safe 解码 + 完整性校验 |
|
||
| csv-download:下载目录 | 写死 `D:\Downloads` | 传 `--download-dir` 指向工作区 `YT导出` | 沙箱外 `D:\Downloads` 用户不可见 | 脚本已支持 `--download-dir`,无需改;仅执行时传参 |
|
||
| csv-download:批量下载 | `run()` 单 URL、每次新建会话 | 外层驱动循环复用一次浏览器连接逐条下载 + zip 校验 + 失败重试 | 清单有 6 条 URL,单次调用不够 | 可选:给脚本增加批量能力;或保持单次语义、由调用方写循环 |
|
||
| groupid-lookup:伪头 | 捕获后原样落 `bundles.json` | 人工删除 4 个 HTTP/2 伪头 | 脚本未在捕获阶段过滤伪头 | 建议:捕获时自动过滤伪头 |
|
||
| url-builder | 直接读清单生成 URL | 先经 groupid-lookup 补 entity_id 再生成 | 清单只有群组名,缺 groupId | 无需改脚本;在 SKILL.md 注明前置依赖即可 |
|
||
|
||
> 差异分为两类:**脚本缺陷**(按钮定位、base64 解码、伪头过滤)应回填到技能脚本;**环境/规模差异**(下载目录、批量循环、前置补 ID)属于执行时的合理适配,脚本本身逻辑无需破坏性改动。
|
||
|
||
---
|
||
|
||
## 七、四技能优化清单(建议)
|
||
|
||
### youtube-studio-csv-download(高优先)
|
||
1. 导出按钮定位由 `get_by_text` 改为 `get_by_label("导出当前视图")`(保留文本定位兜底)。
|
||
2. `decode_zipped_data` 改为 URL-safe base64 解码(补 `=` + `altchars=b"-_"`)。
|
||
3. (可选增强)保存后加 `zipfile.testzip()` 校验,失败自动重试一次。
|
||
|
||
### yt-studio-groupid-lookup(中优先)
|
||
4. 捕获套件时自动过滤 HTTP/2 伪头(`:authority` / `:method` / `:path` / `:scheme`)。
|
||
5. (可选)运行前探测 Chrome 残留进程,给出更明确的"完全退出后重启"提示。
|
||
|
||
### uv-env-setup(低优先)
|
||
6. 补充"`uv` 命令不可用时改用 `python -m uv` 或直调 `.venv\Scripts\python.exe`"的排查提示。
|
||
|
||
### yt-studio-url-builder(低优先)
|
||
7. SKILL.md 显式说明"清单只有群组名时,需先经 groupid-lookup 补 entity_id"。
|
||
|
||
---
|
||
|
||
## 八、待确认事项
|
||
|
||
以上优化点(尤其第三节、第五节的 3 处脚本缺陷修复)尚未改动任何技能脚本,仅记录在本文档。是否执行优化、执行到哪些范围(仅修复缺陷 / 同时做增强),请确认后再实施。 |