CloakBrowser 开发踩坑笔记
CloakBrowser 可以在启动浏览器时开启:
csharp
const context = await launchPersistentContext({
humanize: true,
userDataDir,
})
开启后,点击由 Humanize 层处理。Playwright 中一些合法的 Locator 写法,在 Humanize 模式下不能直接使用。
下面记录开发中出现过的错误代码、报错和修改方式。
1. 对列表元素使用 nth() 点击,触发 ElementNotReceivingEventsError
错误代码
csharp
const cards = page.locator('.job-card-box')
await cards.nth(5).click()
错误堆栈
ini
ElementNotReceivingEventsError
selector:
.job-card-box >> nth=5
pointer-events check failed
element is not receiving pointer events
Humanize 点击时拿到的是 .job-card-box >> nth=5,同时 .job-card-box 只是卡片容器,真正接收鼠标事件的是卡片里的可点击节点。
修改后的代码
csharp
const target = page.locator(
'.job-card-box:nth-child(6) .job-info',
)
await target.waitFor({
state: 'visible',
})
await target.scrollIntoViewIfNeeded()
await target.click()
把 Playwright 的 >> nth=5 改成浏览器原生 CSS,并把点击目标从卡片容器改成真正接收点击的 .job-info。
2. 保存动态页面的 DOM 节点后再点击,触发 ElementNotAttachedError
错误代码
scss
const card = await page
.locator('.job-card-box')
.nth(0)
.elementHandle()
await loadJobDetail()
await card?.click()
错误堆栈
arduino
ElementNotAttachedError
Element is not attached to the DOM
while performing humanized click
职位列表刷新后,React 删除了原来的 DOM 节点,又创建了外观相同的新节点,旧的 ElementHandle 仍然指向已经移除的节点。
修改后的代码
scss
const selector =
'.job-card-box:nth-child(1) .job-info'
await page
.locator(selector)
.waitFor({
state: 'visible',
})
await page
.locator(selector)
.click()
点击前重新执行 Locator 查询,让 Playwright 获取当前 DOM 中的节点,而不是继续使用已经失效的 ElementHandle。
3. 使用 .filter({ hasText }) 后继续点击,Humanize 无法处理 Locator 过滤链
错误代码
python
await page
.locator('.expect-item')
.filter({
hasText: 'Android(北京)',
})
.click()
错误堆栈
css
Error: unsupported locator operation in humanized mode
locator:
locator('.expect-item').filter({ hasText: 'Android(北京)' })
.filter() 属于 Playwright Locator 的过滤逻辑,不是浏览器原生选择器,Humanize 层不能完整处理这条 Locator 链。
修改后的代码
csharp
await page
.getByText('Android(北京)')
.click()
如果页面提供稳定属性:
csharp
await page
.locator(
'.expect-item[data-id="android-beijing"]',
)
.click()
把 Locator 过滤运算换成文字定位或原生 CSS 属性选择器,让 Humanize 只处理一个明确的目标。
4. 在 Humanize 中使用 :visible,选择器解析失败
错误代码
csharp
await page
.locator('.op-btn-chat:visible')
.click()
错误堆栈
makefile
Error: unsupported selector
selector:
.op-btn-chat:visible
:visible 是 Playwright 扩展的选择器语法,不属于浏览器标准 CSS,Humanize 不能按 Playwright 的 Selector Engine 解析它。
修改后的代码
csharp
const button =
page.locator('.op-btn-chat')
await button.waitFor({
state: 'visible',
})
await button.click()
选择器只保留原生 CSS,可见性判断改由 waitFor({ state: 'visible' }) 完成。
5. 使用 :nth-match() 定位列表元素,Humanize 无法解析选择器
错误代码
csharp
await page
.locator(
':nth-match(.job-card-box, 3)',
)
.click()
错误堆栈
ruby
Error: unsupported selector
selector:
:nth-match(.job-card-box, 3)
:nth-match() 是 Playwright 提供的扩展伪类,浏览器原生 CSS 不认识这段语法。
修改后的代码
csharp
await page
.locator(
'.job-card-box:nth-child(3) .job-info',
)
.click()
把 Playwright 扩展伪类换成浏览器能够直接解析的 :nth-child()。
6. 使用 >> 拼接选择器,Humanize 无法处理 Playwright Selector Engine 语法
错误代码
csharp
await page
.locator(
'.job-card-box >> .job-info',
)
.click()
错误堆栈
makefile
Error: unsupported selector syntax
selector:
.job-card-box >> .job-info
>> 是 Playwright 的选择器组合语法,不是 CSS 后代选择器,Humanize 不能直接把它交给浏览器解析。
修改后的代码
csharp
await page
.locator(
'.job-card-box .job-info',
)
.click()
用标准 CSS 的空格后代选择器替代 Playwright 的 >> 语法。
7. nth() 后继续调用 .locator(),Humanize 收到多层 Locator 链
错误代码
scss
await page
.locator('.job-card-box')
.nth(index)
.locator('.job-info')
.click()
错误堆栈
css
Error: failed to resolve humanized click target
locator:
locator('.job-card-box')
.nth(index)
.locator('.job-info')
目标经过 locator → nth → locator 多次计算,Humanize 需要重新解析 Playwright 的 Locator 链,目标解析在点击阶段失败。
修改后的代码
javascript
const selector =
`.job-card-box:nth-child(${index + 1}) .job-info`
await page
.locator(selector)
.click()
把多层 Locator 运算压成一个完整 CSS,让点击阶段只处理一个选择器。
8. 元素位于滚动区域边缘,Humanize 移动鼠标后目标位置发生变化
错误代码
csharp
await page
.locator('.job-card-box:nth-child(8) .job-info')
.click()
错误堆栈
arduino
Error: click target moved during humanized interaction
target position changed before pointer down
Humanize 在移动鼠标的过程中触发页面滚动,目标元素的位置随滚动变化,最终点击坐标和元素位置不再重合。
修改后的代码
csharp
const target = page.locator(
'.job-card-box:nth-child(8) .job-info',
)
await target.waitFor({
state: 'visible',
})
await target.scrollIntoViewIfNeeded()
await target.click()
先把目标滚进视口,再开始 Humanize 点击,减少鼠标移动期间的页面位置变化。
9. 按钮被浮层覆盖时直接点击,鼠标事件落到遮罩层
错误代码
csharp
await page
.locator('.op-btn-chat')
.click()
页面此时仍有:
css
<div class="loading-mask"></div>
错误堆栈
ini
Error: pointer intercepted
<div class="loading-mask">
intercepts pointer events
按钮已经出现在 DOM 中,但遮罩层仍盖在按钮上,Humanize 产生的真实鼠标事件被遮罩层接收。
修改后的代码
csharp
await page
.locator('.loading-mask')
.waitFor({
state: 'hidden',
})
await page
.locator('.op-btn-chat')
.click()
等待遮罩层退出点击区域后再执行鼠标操作,让按钮成为当前坐标下真正接收事件的元素。
10. frameLocator() 中直接执行 Humanize 点击,目标无法跨 Frame 解析
错误代码
csharp
await page
.frameLocator('#login-frame')
.locator('.submit')
.click()
错误堆栈
makefile
Error: humanized click target could not be resolved
FrameLocator:
#login-frame
Locator:
.submit
FrameLocator 属于 Playwright 的跨 Frame 定位抽象,Humanize 点击需要处理页面、iframe 和内部节点三层目标。
修改后的代码
javascript
const frame = page
.frames()
.find(item =>
item.url().includes('login'),
)
if (!frame) {
throw new Error('frame not found')
}
await frame
.locator('.submit')
.click()
先取得实际的 Frame 对象,再在 Frame 内执行普通 Locator,去掉 FrameLocator 这一层定位包装。