核心总结
- 如果页面有多个相似文本,要正确告诉cc, 点击哪个位置, 比如问题1, 多个"导出"文本,导致cc点击错误的地方
- 如果某个元素是悬停鼠标后出现新内容, 因为出现新内容要重新渲染DOM, 要告诉CC,避免多次尝试,浪费时间
问题1: 弹窗有多个相似文本
● 问题分析与解决过程总结
失败原因分析
┌──────────┬────────────────────────────────────┬──────┬───────────────────────────────────────────────────┐
│ 尝试次数 │ 点击方式 │ 结果 │ 原因分析 │
├──────────┼────────────────────────────────────┼──────┼───────────────────────────────────────────────────┤
│ 1 │ locator.click() │ ❌ │ 元素在弹窗中,Playwright 的默认可见性检查可能误判 │
├──────────┼────────────────────────────────────┼──────┼───────────────────────────────────────────────────┤
│ 2 │ locator.evaluate(el => el.click()) │ ❌ │ el.click() 不是有效的选择器语法 │
├──────────┼────────────────────────────────────┼──────┼───────────────────────────────────────────────────┤
│ 3 │ page.mouse.click() 直接点击 │ ❌ │ 点击位置正确,但可能点击速度太快,页面没反应过来 │
├──────────┼────────────────────────────────────┼──────┼───────────────────────────────────────────────────┤
│ 4 │ 监听 page.on('download') 事件 │ ❌ │ 点击没有真正触发,所以监听不到下载事件 │
├──────────┼────────────────────────────────────┼──────┼───────────────────────────────────────────────────┤
│ 5 │ 使用 CDP 监听网络请求 │ ❌ │ 点击没有生效,没有下载相关的网络请求 │
└──────────┴────────────────────────────────────┴──────┴───────────────────────────────────────────────────┘
关键发现
弹窗结构分析后才发现:
┌─────────────────────────────────────┐
│ 导出 Word 设置 │ ← 标题
├─────────────────────────────────────┤
│ 内容范围 │
│ ○ 导出正文及评论 ● 仅正文 │ ← "仅正文"是默认选项
├─────────────────────────────────────┤
│ [取消] [导出] │ ← 导出按钮在右下角
└─────────────────────────────────────┘
最终成功的三个关键因素
1. 正确的元素定位
- 之前用 text=导出 会匹配到多个元素(标题、按钮等)
- 后来用 button:has-text("导出") 精确定位到按钮元素
2. 完整的操作流程
- 需要按顺序点击:菜单 → 悬停"下载为" → Word → 导出
- 每一步之间需要适当的等待时间(1-5 秒)
3. 使用鼠标模拟真实点击
const exportBox = await exportButton.boundingBox();
await page.mouse.click(
exportBox.x + exportBox.width / 2,
exportBox.y + exportBox.height / 2
);
- 比 locator.click() 更接近真实用户行为
- 绕过了 Playwright 的可见性检查问题
为什么之前截图显示弹窗没消失
从日志中看到,脚本点击了多个包含"导出"文本的元素:
点击按钮:导出 Word 设置 at (280, 252.5) ← 标题,不是按钮
点击按钮:导出正文及评论 at (400, 349.5) ← 单选框,不是按钮
点击按钮:导出 at (896, 411.5) ← 这才是真正的导出按钮!
之前点击的是标题或其他元素,真正的导出按钮坐标是 (896, 411.5),只有点击这个位置才触发下载。
核心教训
┌──────────────────────┬──────────────────────────────────────────────┐
│ 问题 │ 教训 │
├──────────────────────┼──────────────────────────────────────────────┤
│ 多个元素包含相同文本 │ 需要用更精确的选择器(如 button:has-text()) │
├──────────────────────┼──────────────────────────────────────────────┤
│ 弹窗中的元素 │ 需要获取 boundingBox() 后用鼠标点击 │
├──────────────────────┼──────────────────────────────────────────────┤
│ 下载不触发 │ 可能是点击了错误的元素,需要分析完整 DOM │
├──────────────────────┼──────────────────────────────────────────────┤
│ 等待时间不足 │ 每步操作后需要适当 waitForTimeout() │
└──────────────────────┴──────────────────────────────────────────────┘
一句话总结: 之前失败是因为点击了弹窗标题而不是真正的导出按钮,后来通过分析 DOM 结构找到正确的按钮元素并用鼠标模拟点击才成功。
问题2: 部分元素靠鼠标悬停,而不是点击
问题根源:悬停菜单(Hover Menu)的特殊性
这个网站的"下载为"菜单是一个悬停触发的子菜单,不是点击后弹出的对话框。
失败过程回顾
尝试 1: 直接查找 text=下载为
结果:❌ 元素存在但不可见(子菜单未展开)
尝试 2: 使用选择器 [class*="dropdown"]
结果:❌ 子菜单还没渲染出来
尝试 3: 查找 text=Word
结果:❌ Word 选项在子菜单中,子菜单没展开
关键发现
菜单结构:
右上角菜单按钮 [⋮]
↓ 点击后展开
┌─────────────────────┐
│ 下载为 ───────────►│ ← 悬停后才显示子菜单
│ 打印 │
│ 另存为版本 │
└─────────────────────┘
↓ 悬停后展开
┌──────────────┐
│ Word │ ← 这个元素最初不在 DOM 中!
│ PDF │
└──────────────┘
为什么找不到
┌────────────┬─────────────────────────────────────────────────────────────────────────┐
│ 原因 │ 说明 │
├────────────┼─────────────────────────────────────────────────────────────────────────┤
│ 动态渲染 │ 子菜单在悬停前可能根本不在 DOM 中 │
├────────────┼─────────────────────────────────────────────────────────────────────────┤
│ 可见性问题 │ 即使元素在 DOM 中,display: none 或 visibility: hidden 也会导致查找失败 │
├────────────┼─────────────────────────────────────────────────────────────────────────┤
│ 时机问题 │ 悬停后需要等待 1-2 秒子菜单才完全渲染 │
└────────────┴─────────────────────────────────────────────────────────────────────────┘
最终成功的解决方案
await page.locator('[data-icon="MoreOutlined"]').first().click();
await page.waitForTimeout(1000);
const dlBox = await page.locator('text=下载为').first().boundingBox();
await page.mouse.move(dlBox.x + dlBox.width / 2, dlBox.y + dlBox.height / 2);
await page.waitForTimeout(2000);
const wordOption = page.locator('text=Word').first();
await wordOption.click();
核心教训
┌─────────────────────────────────────────────────────────┐
│ 悬停菜单的操作顺序: │
│ │
│ 1. 先触发父菜单(点击) │
│ 2. 悬停在目标项上(mouse.move) │
│ 3. 等待子菜单渲染(waitForTimeout) │
│ 4. 最后点击子菜单项 │
│ │
│ 缺少任何一步都会导致失败! │
└─────────────────────────────────────────────────────────┘
对比两种菜单
┌────────────────┬────────────────────────┬──────────┐
│ 菜单类型 │ 特点 │ 查找难度 │
├────────────────┼────────────────────────┼──────────┤
│ 点击弹出对话框 │ 点击后整个对话框出现 │ 简单 │
├────────────────┼────────────────────────┼──────────┤
│ 悬停子菜单 │ 需要悬停触发,动态渲染 │ 困难 │
└────────────────┴────────────────────────┴──────────┘
一句话总结: "下载为"是悬停菜单,需要先悬停等待子菜单渲染完成后才能找到"Word"选项,之前失败是因为没有正确的悬停操作和等待时间。