Playwright主要接口-Browser, BrowserContext, Page及Locator

Playwright 是一个强大的自动化测试和网页抓取工具,支持 Python 的同步和异步 API。它提供了 BrowserBrowserContextPage 三个核心对象,用于控制浏览器、隔离上下文和操作页面。以下是对这三个对象的详细介绍,包括其主要接口、使用场景和示例代码,结合你之前的提问背景(如 locator、get_by_text、get_by_role 等),确保内容清晰、全面且实用。


1. Browser 对象

概述

  • 定义: Browser 是 Playwright 的顶级对象,表示一个浏览器实例(如 Chromium、Firefox 或 WebKit)。
  • 用途: 用于启动浏览器、管理浏览器级别的设置(如 headless 模式、代理)和创建 BrowserContext
  • 创建方式: 通过 playwright.<browser_type>.launch() 创建,例如 playwright.chromium.launch()

主要接口

  1. 启动和关闭浏览器:
  • launch(**kwargs): 启动浏览器实例。
    • 参数: headless (布尔,是否无头模式)、slow_mo (慢速执行,单位毫秒)、proxy 等。
  • close(): 关闭浏览器,释放资源。
  1. 创建上下文:
  • new_context(**kwargs): 创建一个新的浏览器上下文(类似于浏览器的独立会话)。
    • 参数: viewport (视口大小)、user_agentlocalepermissions 等。
  1. 创建页面:
  • new_page(**kwargs): 直接创建一个新页面(隐式创建一个上下文)。
  1. 浏览器信息:
  • version: 获取浏览器版本。
  • browser_type: 获取浏览器类型(chromium、firefox、webkit)。

使用场景

  • 初始化浏览器(headless 或 headed 模式)。
  • 配置全局浏览器设置(如代理、下载路径)。
  • 管理多个独立会话(通过 BrowserContext)。

示例代码

from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        # 启动 Chromium 浏览器(无头模式)
        browser = await p.chromium.launch(headless=False, slow_mo=100)
        print(f"Browser version: {browser.version}")  # 打印浏览器版本

        # 创建新上下文
        context = await browser.new_context(
            viewport={"width": 1280, "height": 720},
            user_agent="MyCustomAgent"
        )

        # 创建页面
        page = await context.new_page()
        await page.goto("https://example.com")
        print(f"Page title: {await page.title()}")

        # 关闭浏览器
        await browser.close()

import asyncio
asyncio.run(main())

输出示例:

Browser version: 123.0.6312.122
Page title: Example Domain

2. BrowserContext 对象

概述

  • 定义: BrowserContext 表示一个独立的浏览器会话,类似于浏览器的隐身窗口。每个上下文有自己的 cookies、缓存、localStorage 和页面。
  • 用途: 用于隔离测试用例、模拟不同用户会话或设备配置(如不同的视口、语言)。
  • 创建方式: 通过 browser.new_context() 或隐式通过 browser.new_page() 创建。

主要接口

  1. 创建和管理页面:
  • new_page(): 在当前上下文中创建新页面。
  • pages: 获取上下文中的所有页面列表。
  • close(): 关闭上下文及其所有页面。
  1. 网络和权限控制:
  • set_default_timeout(timeout): 设置默认操作超时(毫秒)。
  • grant_permissions(permissions, origin): 授予权限(如 geolocation、notifications)。
  • clear_cookies(): 清除上下文中的 cookies。
  • add_cookies(cookies): 添加自定义 cookies。
  • route(url, handler): 拦截和修改网络请求。
  1. 事件监听:
  • on("page"): 监听新页面(如弹出窗口)。
  • on("request"): 监听网络请求。
  • on("response"): 监听网络响应。

使用场景

  • 模拟不同用户的登录会话(不同 cookies)。
  • 测试不同设备配置(视口、user-agent)。
  • 拦截和模拟网络请求(mock API 响应)。
  • 管理弹出窗口或多标签页。

示例代码

from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=False)

        # 创建上下文,模拟移动设备
        context = await browser.new_context(
            viewport={"width": 375, "height": 667},  # iPhone 6 尺寸
            is_mobile=True,
            user_agent="Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X)"
        )

        # 设置默认超时
        context.set_default_timeout(5000)

        # 拦截网络请求
        async def handle_route(route):
            await route.fulfill(status=200, body="Mocked response")
        await context.route("**/api/**", handle_route)

        # 创建页面
        page = await context.new_page()
        await page.goto("https://example.com")

        # 监听新页面(如弹出窗口)
        async def handle_popup(popup):
            print(f"New page opened: {await popup.title()}")
        context.on("page", handle_popup)

        # 模拟点击可能触发弹出窗口的元素
        # await page.click("a[target='_blank']")

        # 打印上下文中的页面数量
        print(f"Open pages: {len(context.pages)}")

        # 关闭上下文
        await context.close()
        await browser.close()

import asyncio
asyncio.run(main())

输出示例:

Open pages: 1

3. Page 对象

概述

  • 定义: Page 表示单个浏览器页面(标签页),是最常用的对象,用于与网页内容交互。
  • 用途: 执行导航、元素查找、用户操作(如点击、输入)、截图、录制视频等。
  • 创建方式: 通过 context.new_page()browser.new_page() 创建。

主要接口

  1. 导航:
  • goto(url, **kwargs): 导航到指定 URL。
    • 参数: timeout, wait_until (domcontentloaded, load, networkidle)。
  • reload(): 刷新页面。
  • go_back(), go_forward(): 页面历史导航。
  1. 元素查找(结合你之前的 locator 问题):
  • locator(selector): 使用 CSS 或 XPath 选择器查找元素。
  • get_by_role(role, **kwargs): 按 ARIA 角色查找(见你之前的 get_by_role 提问)。
  • get_by_text(text, exact=False): 按文本内容查找(见你之前的 get_by_text 问题)。
  • get_by_label(label): 按表单标签查找。
  • get_by_placeholder(placeholder): 按占位符查找。
  • get_by_test_id(test_id): 按 data-testid 属性查找。
  1. 用户交互:
  • click(selector): 点击元素。
  • fill(selector, value): 填充表单输入。
  • check(selector), uncheck(selector): 操作复选框。
  • type(selector, value): 模拟逐字输入。
  • press(selector, key): 模拟按键(如 Enter)。
  1. 内容操作:
  • content(): 获取页面 HTML 内容。
  • set_content(html): 设置页面 HTML(见你之前的 set_content 示例)。
  • text_content(selector): 获取元素文本内容。
  • inner_text(selector): 获取元素可见文本。
  • evaluate(expression, arg): 在页面执行 JavaScript。
  1. 页面信息:
  • title(): 获取页面标题。
  • url: 获取当前 URL。
  • screenshot(path): 截取页面截图。
  • pdf(path): 生成页面 PDF(仅 Chromium)。
  1. 事件监听:
  • on("load"): 页面加载完成。
  • on("console"): 监听控制台消息。
  • on("dialog"): 处理弹窗(如 alert、confirm)。
  1. 等待机制:
  • wait_for_selector(selector): 等待元素出现。
  • wait_for_timeout(timeout): 等待指定时间。
  • wait_for_load_state(state): 等待页面加载状态。

使用场景

  • 导航和页面操作(如点击按钮、填写表单)。
  • 元素查找和验证(结合 get_by_text, get_by_role)。
  • 截图、录制视频或生成 PDF。
  • 执行 JavaScript 或处理动态内容。

示例代码(结合你之前的 locator 问题)

以下示例整合了你之前的提问(get_by_text, get_by_role, 属性和子节点打印):

from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        # Browser: 启动浏览器
        browser = await p.chromium.launch(headless=False)

        # BrowserContext: 创建上下文,模拟特定设备
        context = await browser.new_context(
            viewport={"width": 1280, "height": 720}
        )

        # Page: 创建页面并设置内容
        page = await context.new_page()
        html = """
            <div class="first" role="region">Hello <span>world</span></div>
            <button role="button" aria-label="Submit">Click me <span>world</span></button>
        """
        await page.set_content(html)

        # 使用 get_by_text 查找(参考你之前的提问)
        locator_text = page.get_by_text("world", exact=True)
        text = await locator_text.text_content()
        print("get_by_text result:", text)  # 输出: world

        # 使用 get_by_role 查找按钮(参考你之前的 get_by_role 提问)
        locator_role = page.get_by_role("button", name="Submit")
        button_text = await locator_role.text_content()
        print("get_by_role button text:", button_text)  # 输出: Click me world

        # 打印按钮的属性(参考你第一个问题)
        attributes = await locator_role.evaluate(
            """element => {
                const attrs = {};
                for (const attr of element.attributes) {
                    attrs[attr.name] = attr.value;
                }
                return attrs;
            }"""
        )
        print("Button attributes:", attributes)

        # 打印按钮的子节点(参考你第二个问题)
        children = await locator_role.evaluate(
            """element => {
                const childrenArray = [];
                for (const child of element.children) {
                    childrenArray.push({
                        tagName: child.tagName,
                        textContent: child.textContent.trim(),
                        className: child.className || 'none'
                    });
                }
                return childrenArray;
            }"""
        )
        print("Button children:")
        for i, child in enumerate(children):
            print(f"Child {i + 1}: Tag = {child['tagName']}, Text = {child['textContent']}, Class = {child['className']}")

        # 截图页面
        await page.screenshot(path="screenshot.png")

        # 关闭上下文和浏览器
        await context.close()
        await browser.close()

import asyncio
asyncio.run(main())

输出示例:

get_by_text result: world
get_by_role button text: Click me world
Button attributes: {'role': 'button', 'aria-label': 'Submit'}
Button children:
Child 1: Tag = SPAN, Text = world, Class = none

结合你之前的问题

  1. get_by_text 问题(world\n Hello\n):
  • 如之前分析,get_by_text("world") 可能选中父 <div>,导致包含额外文本。示例中通过 exact=True 或更具体的 locator(如 span:text("world"))解决。
  1. get_by_role:
  • 示例中使用 get_by_role("button", name="Submit") 展示了如何定位具有明确 ARIA 角色的元素,比 get_by_text 更可靠。
  1. 属性和子节点:
  • 使用 evaluate() 提取属性和子节点,延续你之前的逻辑,确保一致性。

关键注意事项

  • 异步 vs 同步:
  • 示例使用异步 API(async_playwright),因为你之前使用了 await。同步 API 类似,只需替换为 sync_playwright 并移除 awaitasync 关键字。
  • 同步示例:
    python from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch(headless=False) page = browser.new_page() page.goto("https://example.com") print(page.title()) browser.close()
  • 超时和等待:
  • 使用 set_default_timeoutwait_for_selector 确保操作健壮性,避免元素未加载的错误。
  • 示例:await page.wait_for_selector("div.first", timeout=5000)
  • 错误处理:
  • 检查 locator 是否找到元素:
    python if await locator.count() > 0: text = await locator.text_content() else: print("No elements found")
  • Playwright 版本:
  • 确保使用最新版本(pip install --upgrade playwright)以获取最新的 locator 支持(如 get_by_role 的增强功能)。
  • 调试:
  • 使用 page.screenshot()page.on("console", lambda msg: print(msg.text)) 调试页面状态。
  • 检查 locator 选中的元素:
    python tag = await locator.evaluate("el => el.tagName") print(f"Selected tag: {tag}")

在 Playwright 的 Python API 中,Locator 类是用于定位和操作网页元素的核心工具。它提供了一种灵活且强大的方式来选择 DOM 元素,并支持多种交互操作(如点击、获取文本、提取属性等)。结合你之前的提问(关于 get_by_textget_by_role、打印属性和子节点等),我将详细介绍 Locator 类的功能、主要接口、使用场景,并提供针对性示例,展示如何解决你之前遇到的问题(如 get_by_text 输出意外换行符)以及如何与 BrowserBrowserContextPage 配合使用。


4. Locator 类

  • 定义: Locator 表示页面上的一组元素(可以是一个或多个),通过选择器(如 CSS、XPath)或 Playwright 特有的定位方法(如 get_by_roleget_by_text)创建。
  • 用途: 用于定位元素、执行交互(如点击、输入)、获取元素属性或内容,以及处理动态内容。
  • 创建方式:
  • 通过 Page 对象的定位方法创建,例如 page.locator(selector)page.get_by_role(role)page.get_by_text(text)
  • 通过 Locator 对象的进一步筛选,例如 locator.locator(selector)
  • 特点:
  • 惰性求值: Locator 不会立即查询 DOM,而是等到执行操作(如 click()text_content())时才解析。
  • 动态更新: 支持处理动态加载的元素,自动重新查询 DOM。
  • 链式定位: 支持嵌套定位(如 locator.locator("span"))。
  • 多元素支持: 可以处理匹配多个元素的场景(如 locator.nth(0)locator.all())。

1. 主要接口

以下是 Locator 类的主要接口,分为几类:定位、交互、内容提取、状态检查和高级操作。

1.1 定位相关

  • locator(selector, **kwargs): 在当前 Locator 的范围内进一步筛选子元素。
  • 参数: selector(CSS 或 XPath)、has_text(筛选包含指定文本的元素)、has(筛选包含特定子元素的元素)。
  • 示例: locator.locator("span") 选择当前 locator 中的 <span> 元素。
  • nth(index): 选择匹配的第 n 个元素(从 0 开始)。
  • 示例: locator.nth(0) 获取第一个匹配元素。
  • first: 选择第一个匹配元素(等价于 nth(0))。
  • last: 选择最后一个匹配元素。
  • filter(**kwargs): 根据条件(如 has_texthas)过滤 locator。
  • 示例: locator.filter(has_text="world") 筛选包含 “world” 的元素。
  • all(): 返回所有匹配元素的 Locator 列表(异步 API 返回 List[Locator])。
  • count(): 返回匹配元素的数量。

1.2 交互操作

  • click(**kwargs): 点击元素。
  • 参数: timeoutbutton(left、right、middle)、modifiers(如 Alt、Shift)。
  • dblclick(**kwargs): 双击元素。
  • fill(value, **kwargs): 填充输入框。
  • check(**kwargs): 勾选复选框或单选按钮。
  • uncheck(**kwargs): 取消勾选。
  • type(value, **kwargs): 模拟逐字输入。
  • press(key, **kwargs): 模拟按键(如 “Enter”、”Tab”)。
  • hover(**kwargs): 悬停在元素上。
  • drag_to(target, **kwargs): 拖动元素到目标位置。

1.3 内容和属性提取

  • text_content(**kwargs): 获取元素的文本内容(包括所有子节点,规范化换行符)。
  • inner_text(**kwargs): 获取元素的可见文本(不包括隐藏元素)。
  • inner_html(**kwargs): 获取元素的 HTML 内容。
  • get_attribute(name, **kwargs): 获取指定属性的值(如 classhref)。
  • input_value(**kwargs): 获取输入框的值(如 <input><textarea>)。
  • evaluate(expression, arg=None, **kwargs): 在元素上执行 JavaScript 表达式,返回结果。
  • 示例: locator.evaluate("el => el.tagName") 获取元素标签名。
  • evaluate_all(expression, arg=None, **kwargs): 对所有匹配元素执行 JavaScript,返回结果列表。

1.4 状态检查

  • is_visible(**kwargs): 检查元素是否可见。
  • is_hidden(**kwargs): 检查元素是否隐藏。
  • is_enabled(**kwargs): 检查元素是否启用(非 disabled)。
  • is_disabled(**kwargs): 检查元素是否禁用。
  • is_checked(**kwargs): 检查复选框是否选中。
  • is_editable(**kwargs): 检查元素是否可编辑(如输入框)。

1.5 等待机制

  • wait_for(**kwargs): 等待 locator 匹配的元素满足特定状态(如 visiblehidden)。
  • 参数: state(”attached”、”detached”、”visible”、”hidden”)、timeout
  • wait_for_timeout(timeout): 等待指定时间(毫秒)。

1.6 定位器特有方法

  • get_by_role(role, **kwargs): 在当前 locator 中按 ARIA 角色定位子元素。
  • get_by_text(text, exact=False): 在当前 locator 中按文本定位子元素。
  • get_by_label(label, exact=False): 按表单标签定位。
  • get_by_placeholder(placeholder, exact=False): 按占位符定位。
  • get_by_test_id(test_id): 按 data-testid 属性定位。

2. 使用场景

  • 元素定位: 使用 CSS、XPath 或 Playwright 的高级定位方法(如 get_by_roleget_by_text)选择元素。
  • 动态内容: 处理 AJAX 加载的元素,Locator 会自动重试直到元素出现。
  • 交互测试: 模拟用户操作(如点击、输入、拖放)。
  • 数据提取: 获取元素文本、属性或执行 JavaScript 提取复杂数据。
  • 多元素处理: 处理列表或表格中的多个匹配元素。
  • 结合你之前的问题:
  • 打印属性: 使用 evaluate 提取所有属性(如你的第一个提问)。
  • 打印子节点: 使用 evaluate 获取子元素信息(如你的第二个提问)。
  • 解决 get_by_text 换行问题: 使用更精确的定位或 exact=True 避免父元素干扰(如你的第三个提问)。
  • 使用 get_by_role: 结合 ARIA 角色定位(如你的第四提问)。

3. 示例代码

以下示例整合了你的提问背景(get_by_textget_by_role、打印属性和子节点),并展示了 Locator 类的主要接口,涵盖定位、交互、内容提取和状态检查。

示例 1: 基本定位和交互

from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=False)
        context = await browser.new_context()
        page = await context.new_page()

        # 设置页面内容(基于你之前的 HTML)
        html = """
            <div class="first" role="region">Hello <span>world</span></div>
            <button role="button" aria-label="Submit">Click me <span>world</span></button>
            <input type="text" placeholder="Enter text">
        """
        await page.set_content(html)

        # Locator: 使用 CSS 选择器定位 div.first
        locator = page.locator("div.first")
        print("Div text:", await locator.text_content())  # 输出: Hello world

        # Locator: 使用 get_by_role 定位按钮
        button_locator = page.get_by_role("button", name="Submit")
        await button_locator.click()  # 模拟点击
        print("Button clicked, is enabled?", await button_locator.is_enabled())  # 输出: True

        # Locator: 使用 get_by_text 定位 span(解决你之前的换行问题)
        span_locator = page.locator("span:text('world')")
        print("Span text:", await span_locator.text_content())  # 输出: world

        # Locator: 输入框交互
        input_locator = page.get_by_placeholder("Enter text")
        await input_locator.fill("Test input")
        print("Input value:", await input_locator.input_value())  # 输出: Test input

        await context.close()
        await browser.close()

import asyncio
asyncio.run(main())

输出示例:

Div text: Hello world
Button clicked, is enabled? True
Span text: world
Input value: Test input

示例 2: 打印属性和子节点(结合你之前的提问)

from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=False)
        context = await browser.new_context()
        page = await context.new_page()

        html = """
            <div class="first" role="region" data-test="example">Hello <span>world</span></div>
            <button role="button" aria-label="Submit">Click me <span>world</span></button>
        """
        await page.set_content(html)

        # Locator: 定位 div.first
        locator = page.locator("div.first")

        # 打印属性(参考你第一个问题)
        attributes = await locator.evaluate(
            """element => {
                const attrs = {};
                for (const attr of element.attributes) {
                    attrs[attr.name] = attr.value;
                }
                return attrs;
            }"""
        )
        print("Div attributes:", attributes)  # 输出: {'class': 'first', 'role': 'region', 'data-test': 'example'}

        # 打印子节点(参考你第二个问题)
        children = await locator.evaluate(
            """element => {
                const childrenArray = [];
                for (const child of element.children) {
                    childrenArray.push({
                        tagName: child.tagName,
                        textContent: child.textContent.trim(),
                        className: child.className || 'none'
                    });
                }
                return childrenArray;
            }"""
        )
        print("Div children:")
        for i, child in enumerate(children):
            print(f"Child {i + 1}: Tag = {child['tagName']}, Text = {child['textContent']}, Class = {child['className']}")

        await context.close()
        await browser.close()

import asyncio
asyncio.run(main())

输出示例:

Div attributes: {'class': 'first', 'role': 'region', 'data-test': 'example'}
Div children:
Child 1: Tag = SPAN, Text = world, Class = none

示例 3: 解决 get_by_text 换行问题(参考你第三个提问)

你的 get_by_text("world") 问题导致输出 world\n Hello\n,因为它选中了父 <div>。以下是使用 Locator 精确匹配 <span> 的示例:

from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=False)
        context = await browser.new_context()
        page = await context.new_page()

        html = """
            <div class="first" role="region">Hello <span>world</span></div>
            <div class="second">Hello</div>
        """
        await page.set_content(html)

        # 使用 get_by_text 精确匹配 span
        locator = page.locator("span:text-is('world')")  # 使用 text-is 确保精确匹配
        text = await locator.text_content()
        print("Span text:", text)  # 输出: world

        # 验证选中的元素
        tag = await locator.evaluate("el => el.tagName")
        print("Selected tag:", tag)  # 输出: SPAN

        await context.close()
        await browser.close()

import asyncio
asyncio.run(main())

输出示例:

Span text: world
Selected tag: SPAN

示例 4: 使用 get_by_role 和链式定位(参考你第四提问)

from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=False)
        context = await browser.new_context()
        page = await context.new_page()

        html = """
            <div class="first" role="region">Hello <span>world</span></div>
            <button role="button" aria-label="Submit">Click me <span>world</span></button>
        """
        await page.set_content(html)

        # 使用 get_by_role 定位按钮
        button_locator = page.get_by_role("button", name="Submit")

        # 链式定位:获取按钮内的 span
        span_locator = button_locator.locator("span:text('world')")
        text = await span_locator.text_content()
        print("Button span text:", text)  # 输出: world

        # 检查按钮是否可见
        print("Button is visible?", await button_locator.is_visible())  # 输出: True

        # 获取按钮的 aria-label 属性
        aria_label = await button_locator.get_attribute("aria-label")
        print("Button aria-label:", aria_label)  # 输出: Submit

        await context.close()
        await browser.close()

import asyncio
asyncio.run(main())

输出示例:

Button span text: world
Button is visible? True
Button aria-label: Submit

4. 结合 Browser、BrowserContext 和 Page

Locator 通常在 Page 对象上创建,并与 BrowserBrowserContext 配合使用。以下是典型工作流程:

  1. Browser: 启动浏览器(playwright.chromium.launch())。
  2. BrowserContext: 创建隔离会话(browser.new_context()),设置视口、cookies 等。
  3. Page: 创建页面(context.new_page()),进行导航(page.goto())。
  4. Locator: 在页面上定位元素(page.locator()page.get_by_role() 等),执行交互或提取数据。

示例:完整工作流程

from playwright.async_api import async_playwright

async def main():
    async with async_playwright() as p:
        # Browser: 启动 Chromium
        browser = await p.chromium.launch(headless=False)

        # BrowserContext: 创建上下文,设置视口
        context = await browser.new_context(viewport={"width": 1280, "height": 720})

        # Page: 创建页面并导航
        page = await context.new_page()
        await page.goto("https://example.com")

        # Locator: 定位所有 <a> 标签
        link_locator = page.locator("a")
        count = await link_locator.count()
        print(f"Found {count} links")

        # 遍历所有链接并打印 href
        for i in range(count):
            href = await link_locator.nth(i).get_attribute("href")
            text = await link_locator.nth(i).text_content()
            print(f"Link {i + 1}: Text = {text}, Href = {href}")

        # 截图
        await page.screenshot(path="example.png")

        await context.close()
        await browser.close()

import asyncio
asyncio.run(main())

输出示例:

Found 1 links
Link 1: Text = More information..., Href = https://www.iana.org/domains/example

5. 解决你之前的问题

以下是如何使用 Locator 类解决你之前的问题:

  1. 打印所有属性(第一个问题):
  • 使用 locator.evaluate() 获取元素属性:
    python attributes = await locator.evaluate( """element => { const attrs = {}; for (const attr of element.attributes) { attrs[attr.name] = attr.value; } return attrs; }""" )
  1. 打印子节点(第二个问题):
  • 使用 locator.evaluate() 获取子元素信息:
    python children = await locator.evaluate( """element => { const childrenArray = []; for (const child of element.children) { childrenArray.push({ tagName: child.tagName, textContent: child.textContent.trim(), className: child.className || 'none' }); } return childrenArray; }""" )
  1. get_by_text 换行问题(第三个问题):
  • 使用 page.locator("span:text-is('world')")page.get_by_text("world", exact=True) 精确匹配 <span>,避免选中父元素。
  1. get_by_role(第四问题):
  • 使用 page.get_by_role("button", name="Submit") 定位具有明确 ARIA 角色的元素,结合 locator 方法进一步筛选子元素。

6. 注意事项

  • 异步 vs 同步:
  • 示例使用异步 API(async_playwright),因为你之前的代码使用了 await。同步 API 类似,只需使用 sync_playwright 并移除 awaitasync
  • 同步示例:
    python from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch(headless=False) page = browser.new_page() page.set_content('<div>Hello <span>world</span></div>') locator = page.locator("span:text('world')") print(locator.text_content()) # 输出: world browser.close()
  • 精确定位:
  • 使用 exact=True:text-is() 避免 get_by_text 选中父元素。
  • 使用 locator.filter(has_text="world")locator.locator("span") 精确筛选。
  • 等待机制:
  • 对动态内容,使用 await locator.wait_for(state="visible") 确保元素可用。
  • 错误处理:
  • 检查元素是否存在:
    python if await locator.count() > 0: text = await locator.text_content() else: print("No elements found")
  • 调试:
  • 验证选中的元素:
    python tag = await locator.evaluate("el => el.tagName") print(f"Selected tag: {tag}")
  • 使用 page.screenshot()locator.highlight()(仅同步 API)调试定位。
  • Playwright 版本:
  • 确保使用最新版本(pip install --upgrade playwright)以支持最新的 locator 功能(如 :has():text-is())。

7. 总结

  • Browser: 管理浏览器实例,适合全局配置和上下文创建。
  • BrowserContext: 隔离会话,模拟不同用户或设备,管理 cookies 和网络请求。
  • Page: 最常用的对象,用于页面导航、元素查找和用户交互,结合 get_by_roleget_by_text 等实现你之前的 locator 需求。
  • Locator: 提供定位、交互、内容提取和状态检查的强大接口,支持 CSS、XPath 和 Playwright 特有的定位方法(如 get_by_roleget_by_text)。
  • Locator与 Browser、Context、Page 的关系: LocatorPage 上创建,依赖 BrowserContext 的会话隔离和 Browser 的全局配置。
  • 提取属性和子节点(使用 evaluate)。
  • 结合 get_by_role 和链式定位实现复杂需求。
  • 示例: 提供了多个示例,覆盖定位、交互、属性和子节点提取,适配你的 HTML 和提问背景。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注