Playwright Frame 类完全指南:掌握页面 iframe 树、定位、求值与导航的核心 API
在 Playwright 中,Frame(框架)是页面 DOM 树的组织单元:一个页面由主框架(main frame)与若干子框架(child frame,通常由 <iframe> 产生)构成。无论是处理第三方广告 iframe、富文本编辑器内嵌页,还是多层嵌套的业务系统,你都需要直接面向 Frame 编程。本指南基于 Playwright 官方 API 文档 docs/src/api/class-frame.md 展开,并结合仓库内 客户端实现 深入剖析其底层原理。读完本文,你将掌握框架树的遍历、iframe 内定位、DOM 求值、表单操作、状态等待与导航同步等一整套实战方案。
理解 Frame:页面中的框架树与生命周期
主框架与子框架的树形结构
Page 在任意时刻都会通过两个方法暴露当前框架树:
- [
method: Page.mainFrame]——返回页面的主框架,它是顶层文档所在的Frame对象; - [
method: Frame.childFrames]——返回当前框架的所有子框架数组。
在 客户端实现 中,Frame 类内部维护了 _parentFrame、_childFrames(Set<Frame>)等字段,并持有 _url、_name、_detached 状态,用来精确刻画框架树。相应地,Page 在 page.ts 中通过 _onFrameAttached / _onFrameDetached 维护帧集合,保证父子关系与主框架映射的一致。
三个决定 Frame 生命周期的事件
框架对象的生命周期由发布在 Page 对象上的三个事件控制:
| 事件 | 触发时机 | 备注 |
|---|---|---|
[event: Page.frameAttached] |
框架被挂载到页面时 | 一个 Frame 只能被挂载一次 |
[event: Page.frameNavigated] |
框架提交导航到不同 URL 时 | 提交即生效 |
[event: Page.frameDetached] |
框架从页面分离时 | 一个 Frame 只能被分离一次 |
完整示例:递归转储框架树
下面的四语言示例演示了从主框架出发递归打印全部框架 URL 的方法,是理解框架树最直接的起点:
const { firefox } = require('playwright'); // 或 'chromium' / 'webkit'
(async () => {
const browser = await firefox.launch();
const page = await browser.newPage();
await page.goto('https://www.google.com/chrome/browser/canary.html');
dumpFrameTree(page.mainFrame(), '');
await browser.close();
function dumpFrameTree(frame, indent) {
console.log(indent + frame.url());
for (const child of frame.childFrames())
dumpFrameTree(child, indent + ' ');
}
})();
from playwright.sync_api import sync_playwright, Playwright
def run(playwright: Playwright):
firefox = playwright.firefox
browser = firefox.launch()
page = browser.new_page()
page.goto("https://www.theverge.com")
dump_frame_tree(page.main_frame, "")
browser.close()
def dump_frame_tree(frame, indent):
print(indent + frame.name + '@' + frame.url)
for child in frame.child_frames:
dump_frame_tree(child, indent + " ")
with sync_playwright() as playwright:
run(playwright)
import asyncio
from playwright.async_api import async_playwright, Playwright
async def run(playwright: Playwright):
firefox = playwright.firefox
browser = await firefox.launch()
page = await browser.new_page()
await page.goto("https://www.theverge.com")
dump_frame_tree(page.main_frame, "")
await browser.close()
def dump_frame_tree(frame, indent):
print(indent + frame.name + '@' + frame.url)
for child in frame.child_frames:
dump_frame_tree(child, indent + " ")
async def main():
async with async_playwright() as playwright:
await run(playwright)
asyncio.run(main())
import com.microsoft.playwright.*;
public class Example {
public static void main(String[] args) {
try (Playwright playwright = Playwright.create()) {
BrowserType firefox = playwright.firefox();
Browser browser = firefox.launch();
Page page = browser.newPage();
page.navigate("https://www.google.com/chrome/browser/canary.html");
dumpFrameTree(page.mainFrame(), "");
browser.close();
}
}
static void dumpFrameTree(Frame frame, String indent) {
System.out.println(indent + frame.url());
for (Frame child : frame.childFrames()) {
dumpFrameTree(child, indent + " ");
}
}
}
using Microsoft.Playwright;
using System;
using System.Threading.Tasks;
class FrameExamples
{
public static async Task Main()
{
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Firefox.LaunchAsync();
var page = await browser.NewPageAsync();
await page.GotoAsync("https://www.bing.com");
DumpFrameTree(page.MainFrame, string.Empty);
}
private static void DumpFrameTree(IFrame frame, string indent)
{
Console.WriteLine($"{indent}{frame.Url}");
foreach (var child in frame.ChildFrames)
DumpFrameTree(child, indent + " ");
}
}
在 JS 与 Python 示例中可以看到,Python 的 main_frame 同时携带 name 与 url 两个属性,便于在打印时区分框架。
关于 iframe 的正确打开方式
官方文档反复强调一个现代定位原则:当页面操作与 iframe 相关时,优先使用 page.frameLocator(),它无需手动获取 Frame 对象即可完成“进入 iframe 再定位”的链路。真正的 Frame 对象用于以下场景:
- 需要遍历框架树、或对某个具体框架执行整段 JS 求值;
- 需要监听框架的挂载/分离/导航事件;
- 需要把
FrameLocator之外的、要求精确到某个已存在的Frame实例的操作完成。
Frame 上查找与定位元素:从 Locator 到 FrameLocator
frameLocator:一步进入 iframe 定位
[method: Frame.frameLocator](自 v1.17 起)返回 FrameLocator,用于在 iframe 内部定位元素。它有两种调用方式:
带选择器——限定在匹配的 iframe 内查找:
const locator = frame.frameLocator('#my-iframe').getByText('Submit');
await locator.click();
locator = frame.frame_locator("#my-iframe").get_by_text("Submit")
locator.click()
Locator locator = frame.frameLocator("#my-iframe").getByText("Submit");
locator.click();
不带选择器——搜索从当前 frame 或其中任意 iframe 开始,无需先逐一定位 iframe:
const locator = frame.frameLocator().getByRole('button');
await locator.click();
var locator = frame.FrameLocator().GetByRole(AriaRole.Button);
await locator.ClickAsync();
值得注意的边界行为是:当 frameLocator() 不带选择器时,搜索起始范围扩大,但其余定位器解析仍限定在单个 frame 内;若最终匹配跨越了多个 frame,会抛出错误。选择器参数为空时,客户端在 frame.ts 中实际使用了 kAnyFrameSelector(任意 frame 选择器),这正是“当前 frame 或其中任意 iframe 均可”语义的来源。
locator 与 getBy* 系列
[method: Frame.locator](自 v1.14 起)基于选择器创建根定位器,支持 has、hasText、hasNot(v1.33)、hasNotText(v1.33)等过滤选项;getByRole / getByText / getByLabel / getByPlaceholder / getByAltText / getByTitle / getByTestId(均自 v1.27 起)返回按语义角色与可访问性文本定位的 Locator。从实现上看,getByText 等调用 locatorUtils.ts 中的选择器生成函数把语义条件编译为底层引擎选择器,并由 locator.ts 提供自动等待能力。
更多细节可参见 locators 指南。
传统 DOM 查询 API(已不推荐)
Playwright 建议用 Locator 取代如下方法,因其具备自动等待与可重试性:
- [
method: Frame.querySelector](JS 别名$)——匹配单个元素,找不到返回null;使用ElementHandle被明确 caution 警告(参见 frame.ts 的委托实现); - [
method: Frame.querySelectorAll](JS 别名$$)——匹配全部元素,无匹配返回空数组; - [
method: Frame.$eval](即evalOnSelector)——找到元素后把它作为第一个参数传给表达式;若元素不存在则抛错; - [
method: Frame.$$eval](即evalOnSelectorAll)——把匹配的元素数组传给表达式。
例如用 $$eval 判断 div 数量是否不小于阈值:
const divsCounts = await frame.$$eval('div', (divs, min) => divs.length >= min, 10);
divs_counts = frame.eval_on_selector_all("div", "(divs, min) => divs.length >= min", 10)
boolean divsCounts = (boolean) page.evalOnSelectorAll("div", "(divs, min) => divs.length >= min", 10);
这些求值方法同样支持向表达式额外传参 arg(EvaluationArgument 类型),参看 frame.ts 中 serializeArgument 的处理。
Frame 内执行 JS:evaluate 与 evaluateHandle
evaluate:返回值可序列化
[method: Frame.evaluate] 在 frame 上下文中执行函数或字符串表达式,并返回其求值结果:
- 函数返回
Promise时,Playwright 会等待其 resolve 后返回结果; - 返回非
Serializable值时返回undefined;除 JSON 外还额外支持-0、NaN、Infinity、-Infinity。
函数形式并传参:
const result = await frame.evaluate(([x, y]) => {
return Promise.resolve(x * y);
}, [7, 8]);
console.log(result); // 打印 56
result = frame.evaluate("([x, y]) => Promise.resolve(x * y)", [7, 8])
print(result) # 打印 56
字符串表达式形式:
console.log(await frame.evaluate('1 + 2')); // 打印 3
x = 10
print(frame.evaluate(f"1 + {x}")) # 打印 11
传入 ElementHandle 作为参数(需注意用完 dispose):
const bodyHandle = await frame.evaluateHandle('document.body');
const html = await frame.evaluate(([body, suffix]) =>
body.innerHTML + suffix, [bodyHandle, 'hello'],
);
await bodyHandle.dispose();
从 实现代码 可以看到,evaluate 通过通道调用 evaluateExpression,并以 isFunction 区分函数与表达式;参数经 serializeArgument 序列化,结果经 parseResult 还原。
evaluateHandle:保留对象引用
[method: Frame.evaluateHandle] 与 evaluate 的唯一区别是返回 JSHandle(而非序列化值),因此可以拿到底层 DOM 对象/函数/window 的实时引用,用于后续继续传入其他求值调用:
// window 对象的 handle
const aWindowHandle = await frame.evaluateHandle(() => Promise.resolve(window));
// 字符串形式也可
const aHandle = await frame.evaluateHandle('document'); // document 的 handle
// JSHandle 亦可作为参数传给 evaluateHandle
const resultHandle = await frame.evaluateHandle(([body, suffix]) =>
body.innerHTML + suffix, [aHandle, 'hello']);
console.log(await resultHandle.jsonValue());
await resultHandle.dispose();
a_window_handle = await frame.evaluate_handle("Promise.resolve(window)")
a_handle = await frame.evaluate_handle("document") # document 的 handle
print(await a_handle.json_value())
await a_handle.dispose()
对应实现见 frame.ts,返回的 JSHandle 应通过 dispose()/DisposeAsync() 释放,避免句柄泄漏。
注入标签与读取内容:addScriptTag / addStyleTag 与文本提取
注入脚本与样式
[method: Frame.addScriptTag] 向 frame 注入 <script> 标签,等脚本 onload 触发或内容注入完成后返回对应 ElementHandle;支持三类来源与一个类型参数:
| 选项 | 类型 | 说明 |
|---|---|---|
url |
string | 待加载脚本的 URL |
path |
path | 本地 JS 文件路径,相对路径按当前工作目录解析 |
content |
string | 要注入的原始 JS 文本 |
type |
string | 脚本类型,传 'module' 可加载 ES6 模块 |
底层实现值得注意:当传入 path 时,客户端会先用 fs.promises.readFile 读取文件,再通过 addSourceUrlToScript 把 //# sourceURL 追加到内容中(见 frame.ts),这样后续调试时能定位到原始文件。
[method: Frame.addStyleTag] 同理注入样式:传 url 生成 <link rel="stylesheet">,传 content 生成 <style type="text/css">;若传 path,读取 CSS 后会追加 /*# sourceURL=... */ 注释(见 frame.ts)。
内容与标题读取
- [
method: Frame.content]——返回含 doctype 的整段 HTML; - [
method: Frame.setContent]——用 HTML 覆盖 frame 文档,底层调用document.write(),因此继承其全部特性与行为,并可配合timeout、waitUntil选项; - [
method: Frame.title]——返回页面标题。
针对“选中的元素”读取文本与 HTML,有 textContent、innerText、innerHTML、getAttribute(name)、inputValue 五个常用方法(返回可能为 null,例如元素不存在时 textContent/getAttribute 返回 null)。注意 inputValue 只适用于 <input>、<textarea>、<select>,若元素位于带关联 control 的 <label> 内则读取关联控件值。
表单交互与操作类 API
Frame 提供了一整套以 CSS 选择器为入参的传统操作 API。官方已将这些方法标注为 discouraged(建议改用 Locator 同名方法),但在理解旧代码、快速脚本或框架测试中仍然常用,其语义与 Locator 版本一致:先找元素(无则等待挂载)→ 通过可操作性(actionability)检查(除非设置 force,且检查期间元素被分离则整体重试)→ 滚动进入视口 → 通过 Page.mouse 执行点击 → 验证结果。若在 timeout 内未完成,抛出 TimeoutError;传 timeout: 0 表示关闭等待。
| 方法 | 核心语义 | 特有参数 |
|---|---|---|
click(selector) |
单击元素,等待由点击触发的导航完成 | button/clickCount/delay/modifiers/position/trial |
dblclick(selector) |
双击,注意会派发两次 click 与一次 dblclick |
同上 |
check(selector) / uncheck(selector) |
勾选/取消勾选复选框或单选钮(已处于目标状态则直接返回) | 通用可操作性参数 |
setChecked(selector, checked) |
根据布尔值选择调用 check/uncheck | checked |
tap(selector) |
通过 Page.touchscreen 轻点元素 |
需 context hasTouch: true |
hover(selector) |
悬停到元素中心或指定位置 | modifiers |
dragAndDrop(source, target) |
从源拖放到目标 | sourcePosition/targetPosition/steps(v1.57) |
focus(selector) |
聚焦元素(无匹配则等待出现) | — |
fill(selector, value) |
等待 → 聚焦 → 填充 → 触发 input 事件;传空串可清空 |
value |
type(selector, text) |
逐字符派发 keydown/keypress+input/keyup(已弃用,建议 fill/pressSequentially) |
delay 默认 0 |
press(selector, key) |
对元素按键 | key/delay |
selectOption(selector, values) |
选择 <select> 选项,返回成功选择的 value 数组,完成后触发 change+input |
支持 value/label/index 或 SelectOption |
setInputFiles(selector, files) |
为 <input type=file> 设置本地路径或文件载荷 |
相对路径按 cwd 解析,传空数组清空 |
dispatchEvent(selector, type, eventInit) |
同步派发指定 DOM 事件,不受可见性影响 | 见下文 |
键位与修饰符的细节
press 的 key 可以是 keyboardEvent.key 值或单个字符。常用键示例:F1-F12、Digit0-Digit9、KeyA-KeyZ、Backquote、Minus、Equal、Backslash、Backspace、Tab、Delete、Escape、ArrowDown、End、Enter、Home、Insert、PageDown、PageUp、ArrowRight、ArrowUp 等。
修饰键支持 Shift、Control、Alt、Meta、ShiftLeft、ControlOrMeta(ControlOrMeta 在 Windows/Linux 解析为 Control,macOS 上解析为 Meta)。组合快捷键如 "Control+o"、"Control+Shift+T" 亦受支持,此时修饰键保持按下直到后续键按下。
dispatchEvent:绕过可操作性派发事件
[method: Frame.dispatchEvent] 不关心元素可见性,直接构造事件并派发,等价于 element.click() 等。事件默认 composed、cancelable 且会冒泡。支持的事件类型包括 DeviceMotionEvent、DeviceOrientationEvent、DragEvent、Event、FocusEvent、KeyboardEvent、MouseEvent、PointerEvent、TouchEvent、WheelEvent,初始化属性以 eventInit 传入。
await frame.dispatchEvent('button#submit', 'click');
也可以把 JSHandle 作为属性值传入,以携带实时对象(例如拖拽所需的 DataTransfer,注意它只能在 Chromium 与 Firefox 中创建):
const dataTransfer = await frame.evaluateHandle(() => new DataTransfer());
await frame.dispatchEvent('#source', 'dragstart', { dataTransfer });
data_transfer = frame.evaluate_handle("new DataTransfer()")
frame.dispatch_event("#source", "dragstart", { "dataTransfer": data_transfer })
状态断言类方法
isChecked(selector)——元素是否为勾选态(非复选框/单选钮则抛错);isDisabled(selector)/isEnabled(selector)——判断禁用/可用;isEditable(selector)——判断是否可编辑;isHidden(selector)/isVisible(selector)——判断可见性;选择器无匹配元素分别视为“隐藏”与“不可见”;这两个方法的timeout参数已被废弃(不等待、立即返回),且从 frame.ts 可见其调用使用kNoTimeout。
导航、加载状态与等待 API
goto:导航并获取响应
[method: Frame.goto](Java 别名为 navigate)让 frame 导航到指定 URL,返回主资源响应(多次重定向时以最后一次重定向的响应为准)。
以下情况会抛错:SSL 错误(如自签名证书)、目标 URL 非法、超过 timeout、服务器无响应/不可达、主资源加载失败。但只要返回合法 HTTP 状态码(含 404、500)就不会抛错,状态码可通过 Response.status() 获取。
两个值得记住的特例(成功但返回 null):导航到 about:blank;或带不同 hash 导航到同一 URL。另外,Headless 模式不支持导航到 PDF 文档(受上游 Chromium issue 限制)。
参数速览:
| 选项 | 默认 | 说明 |
|---|---|---|
url |
— | 目标 URL,需包含 scheme(如 https://) |
waitUntil |
load |
等待的加载状态,可选 load/domcontentloaded/networkidle/commit |
timeout |
依上下文导航超时 | 超过则抛错;0 表示禁用 |
referer |
— | Referer 请求头,优先于 Page.setExtraHTTPHeaders 设置的 referer |
在 frame.ts 中,goto 通过 verifyLoadState 校验 waitUntil 取值,合法集合为 load|domcontentloaded|networkidle|commit(见同文件 verifyLoadState)。
waitForLoadState / waitForURL:可靠的导航同步
- [
method: Frame.waitForLoadState]——等待 frame 达到某个加载状态(默认load)。调用时必须已经提交导航;若当前文档已达到目标状态则立即返回。多数场景下 Playwright 会在动作前自动等待,因此该方法通常不必要。 - [
method: Frame.waitForURL](v1.11)——等待 frame 导航到给定 URL,支持 glob 模式(**/target.html)与正则:
await frame.click('a.delayed-navigation'); // 点击间接触发导航
await frame.waitForURL('**/target.html');
frame.click("a.delayed-navigation")
frame.wait_for_url("**/target.html")
waitForNavigation:已弃用的竞态来源
[method: Frame.waitForNavigation](Java 的导航回调、Python 的 expect_navigation、C# 的 RunAndWaitForNavigation)已弃用,原因是它“天生有竞态”——必须先注册等待再触发导航。官方推荐改用 waitForURL。仍会见到它出现在旧测试中:
const navigationPromise = page.waitForNavigation(); // 先启动等待(注意此处无 await)
await page.getByText('Navigate after timeout').click();
await navigationPromise;
with frame.expect_navigation():
frame.click("a.delayed-navigation") # 点击会间接触发导航
# 导航完成后此处才继续
注意:使用 History API 改变 URL 也被视为一次导航;纯 hash 变化的导航会以 null 结果 resolve。其等待核心是 frame.ts 中基于 Waiter 的 _setupNavigationWaiter——它会监听页面关闭、崩溃与“导航所在 frame 被分离”等拒绝条件。
waitForSelector / waitForFunction / waitForTimeout
- [
method: Frame.waitForSelector]——等待选择器满足state选项(attached/detached/visible/hidden)。若方法被调用时已满足条件立即返回;等待hidden/detached时返回null;超过timeout抛错。该方法跨导航有效,典型用法是连续页面中循环等待首张图片:
for (const currentURL of ['https://google.com', 'https://bbc.com']) {
await page.goto(currentURL);
const element = await page.mainFrame().waitForSelector('img');
console.log('Loaded image: ' + await element.getAttribute('src'));
}
官方提示:Playwright 动作前会自动等待元素就绪,使用 Locator 与 web-first 断言可以让代码完全摆脱 waitForSelector。此外,该方法不接受 Puppeteer 风格的 visibility/waitFor 参数——传了会直接抛错提示改用 state(见 frame.ts)。
- [
method: Frame.waitForFunction]——在表达式返回真值时立即 resolve,并返回该值。观察视口变化的经典示例:
const watchDog = page.mainFrame().waitForFunction('window.innerWidth < 100');
await page.setViewportSize({ width: 50, height: 50 });
await watchDog;
向谓词传参(如轮询检测某选择器出现):
const selector = '.foo';
await frame.waitForFunction(selector => !!document.querySelector(selector), selector);
其 polling 选项可传数字毫秒或字符串 'raf'——实现中 frame.ts 会拒绝 'raf' 以外的字符串轮询模式。
- [
method: Frame.waitForTimeout]——仅用于调试,生产测试使用固定时间等待本质上是 flaky 的,应改为等待网络事件、选择器可见等信号。
Frame 元信息与关系 API
| 方法 | 返回 | 说明 |
|---|---|---|
url() |
string | 当前 frame 的 URL |
name() |
string | frame 的 name 属性;若为空则返回 id 属性。该值在 frame 创建时计算一次,之后属性变更不会更新 |
page() |
Page | 包含该 frame 的页面对象 |
parentFrame() |
Frame | null | 父框架;主框架与已分离框架返回 null |
childFrames() |
Frame[] | 子框架数组 |
isDetached() |
boolean | 框架是否已被分离 |
frameElement() |
ElementHandle | 对应此 frame 的 frame/iframe 元素句柄 |
frameElement() 是 [method: ElementHandle.contentFrame] 的逆运算——返回的句柄属于父 frame;若调用前 frame 已分离则抛错。可做一致性校验:
const frameElement = await frame.frameElement();
const contentFrame = await frameElement.contentFrame();
console.log(frame === contentFrame); // true
frame_element = frame.frame_element()
content_frame = frame_element.content_frame()
assert frame == content_frame
源码视角:Frame 与 Page 的分工
从仓库源码可以确认几个关键实现事实,帮助你判断何时用 Page、何时直接拿 Frame:
-
Page 的大量方法只是主框架的薄封装:在 page.ts 中,
page.$、page.click、page.fill、page.evaluate、page.locator、page.goto等均直接转发到this._mainFrame的对应方法(见 page.ts 区间的方法委托)。因此Page操作默认面向主框架;操作子框架内容必须显式取到对应Frame。 -
客户端以 ChannelOwner 模式双向同步框架状态:
Frame继承自ChannelOwner,构造时通过 initializer 还原parentFrame、name、url、loadStates,并在构造体内订阅loadstate与navigated两个通道事件,用于推进_loadStates、更新 URL、向上冒泡FrameNavigated(见 frame.ts)。 -
超时分为“动作超时”与“导航超时”两套体系:
_timeout读取TimeoutSettings.timeout,_navigationTimeout读取navigationTimeout(frame.ts),这解释了为何goto、waitForNavigation等导航类方法文档上标注的是navigation-timeout语义。 -
弃用倾向的代码证据:
Frame.setChecked在客户端只是把checked布尔值路由到check/uncheck(frame.ts);大量方法类型注释与文档标记为 discouraged,指向同一能力集的 Locator 化实现。
选择器、选项与编程语言命名对照速查
本节汇总各 API 的公共参数与跨语言命名差异,便于快速对照查阅:
公共选项(出现在大部分交互/断言方法上):
| 选项 | 说明 |
|---|---|
selector |
定位元素的引擎选择器 |
strict(v1.14+) |
严格模式下选择器必须唯一命中,否则抛错 |
timeout |
动作总超时(毫秒),0 禁用;JS 另有带 signal 的取消变体 |
force |
跳过可操作性检查直接执行 |
noWaitAfter |
动作后不等待可能触发的导航 |
trial(v1.11+) |
只执行可操作性检查不真正操作 |
position |
元素内点击/悬停的偏移坐标(相对元素左上角) |
modifiers |
按下期间的修饰键组合 |
signal(AbortSignal) |
JS 语言中用于取消正在进行的等待 |
语言命名对照:
| 语义 | JS | Python | Java | C# |
|---|---|---|---|---|
| 框架 | page.mainFrame() |
page.main_frame |
page.mainFrame() |
page.MainFrame |
| 单元素求值 | frame.$eval |
frame.eval_on_selector |
frame.evalOnSelector |
frame.EvalOnSelectorAsync |
| 多元素求值 | frame.$$eval |
frame.eval_on_selector_all |
frame.evalOnSelectorAll |
frame.EvalOnSelectorAllAsync |
| 导航 | frame.goto |
frame.goto |
frame.navigate |
frame.GotoAsync |
| 等待导航 | frame.waitForNavigation |
frame.expect_navigation() |
回调式 | RunAndWaitForNavigationAsync |
| 元素文本类型 | frame.type |
frame.type |
frame.type |
frame.TypeAsync |
selectOption 的 Python 特有参数:element(ElementHandle)、index、value、label,其余语言可用 SelectOption/SelectOptionValue 结构体按 label、value、index 匹配。
实操小结:五步把 iframe 场景跑起来
综合以上内容,一个典型的 iframe 测试可归纳为如下路径(以多语言能力通用):
- 找到目标 frame——拿到
Page后要么用page.frames()遍历并按url()/name()匹配,要么直接针对已知的 iframe 用frameLocator('#id'); - 进入定位——通过
FrameLocator或frame.locator()建立 web-first 定位器,优先用getByRole/getByText等语义定位; - 执行操作——用
locator.click()/fill()等动作(或对旧脚本保留的frame.click/frame.fill),依赖 Playwright 动作前自动等待; - 求值与断言——
frame.evaluate/evaluateHandle处理页面内逻辑,web-first 断言检查最终状态; - 等待与清理——需要精确同步时使用
waitForURL/waitForFunction/waitForLoadState;测试结束记得关闭浏览器并释放JSHandle/ElementHandle。
关联阅读:locators 文档、actionability 可操作性说明、以及同一系列中 class-frame-locator、class-page 等 API 参考;测试层面可查看 oopif.spec.ts(OOPIF 跨进程 iframe 覆盖)与 page-frame 相关测试 加深对框架树的验证方式的理解。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00