跨域 iframe 里的 CodeMirror 6 怎么自动化写入:四种输入方式实测
场景
要给一个博客后台批量写入 Markdown 正文,编辑器有两套模式:
- 富文本模式:TinyMCE,正文在
iframe.tox-edit-area__iframe里; - Markdown 模式:
iframe的src指向 CDN 域名,iframe 内部是 CodeMirror 6。
两套模式都不在主文档里,且 Markdown 的 iframe 是跨域的。常规的 page.fill() 在这类结构上直接失效——没有 textarea 可填,Playwright 会一直等到超时。
四种输入方式实测
1. locator.fill():失败。
CodeMirror 6 用 contenteditable 的自绘视图,页面上没有可见 textarea(有一个隐藏的用于输入法桥接)。fill 找不到可填元素。
2. 主文档 keyboard.type():失败。
焦点确实在 iframe 内部,但 Playwright 的键盘事件发往主 frame 的 activeElement,字符全部丢失,页面上一个字都不出现。
3. frame.click('.cm-content') + keyboard.type():成功。
先通过 frame 拿到元素并点击聚焦,再用 keyboard 逐字符输入。
4. CDP Input.insertText:成功,且适合长文本。
一次性把整篇正文塞进去,避免几千次按键事件。
# 跨域 iframe 不能用 URL 匹配来找,靠内容探测
cf = None
for f in page.frames:
try:
if await f.evaluate("()=>!!document.querySelector('.cm-content')"):
cf = f
break
except Exception:
pass
await cf.click(".cm-content") # 必须先聚焦
await page.keyboard.press("Control+A")
await page.keyboard.press("Delete") # 清空原有内容
sess = await page.context.new_cdp_session(page)
await sess.send("Input.insertText", {"text": body})
为什么 insertText 能过
CodeMirror 6 监听的是 beforeinput 与 input 事件,不是 keydown 序列。Input.insertText 走浏览器的文本输入通道,产生一次 insertText 类型的 beforeinput,编辑器会把它当成一次正常的粘贴处理,光标位置、撤销栈、字数统计都能正确更新。
keyboard.type 则是逐键模拟,两千多字的中文正文要发几千次按键事件,慢且容易在中途丢字符。
三个容易忽略的细节
- frame 的 url 可能是空字符串。 跨域 iframe 在
page.frames里,url有时是about:srcdoc或直接为空(还没加载完)。靠"xxx.com" in frame.url找 frame 会漏掉,只能靠evaluate探测内部 DOM。 - 必须先真实点击再 insertText。 不聚焦的话文本会插进主文档,而不是编辑器里,而且不会报错。
- 跨域 frame 的
evaluate是允许的。 Playwright 通过 CDP 执行脚本,不受同源策略限制;但document.querySelector只在当前 frame 内生效,不会穿透嵌套 iframe。
顺带一个坑:标签不能手动添加
后台的标签组件(tagator)在输入任意文本回车时会弹一个提示:不支持手动添加标签。它只接受推荐列表里的条目。
想加标签,正确路径是先点分类页签(云产品 / 解决方案 / 技术领域 / 平台功能),再点具体标签。用脚本给 input.value 赋值再按回车,只会触发那个提示,标签不会真的加上。
小结
跨域 iframe 加自绘编辑器的组合,别再想着 fill()。判断顺序应该是:能否 frame.evaluate 探到编辑器 → 用真实鼠标点击拿到焦点 → 短文本用 keyboard.type,长文本用 Input.insertText。这三步走通,绝大多数 Markdown 编辑器都能写进去。
- 点赞
- 收藏
- 关注作者
评论(0)