- 为 build_studio_urls.py 和 lookup_groups.py 添加 PEP 723 依赖声明 - 修复 HTTP/2 伪头导致 requests 抛 InvalidHeader 的问题 - 添加 CDP 端口连通性预检查及启动指引
11 KiB
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(高优先)
- 导出按钮定位由
get_by_text改为get_by_label("导出当前视图")(保留文本定位兜底)。 decode_zipped_data改为 URL-safe base64 解码(补=+altchars=b"-_")。- (可选增强)保存后加
zipfile.testzip()校验,失败自动重试一次。
yt-studio-groupid-lookup(中优先)
- 捕获套件时自动过滤 HTTP/2 伪头(
:authority/:method/:path/:scheme)。 - (可选)运行前探测 Chrome 残留进程,给出更明确的"完全退出后重启"提示。
uv-env-setup(低优先)
- 补充"
uv命令不可用时改用python -m uv或直调.venv\Scripts\python.exe"的排查提示。
yt-studio-url-builder(低优先)
- SKILL.md 显式说明"清单只有群组名时,需先经 groupid-lookup 补 entity_id"。
八、待确认事项
以上优化点(尤其第三节、第五节的 3 处脚本缺陷修复)尚未改动任何技能脚本,仅记录在本文档。是否执行优化、执行到哪些范围(仅修复缺陷 / 同时做增强),请确认后再实施。