框架介绍和安装

Playwright是一个开源的自动化库,由Microsoft开发,用于自动化基于Chromium,Firefox,和WebKit的浏览器。它支持多种语言,包括Python,并能在Windows,Linux,和macos上运行。
支持同步和异步执行。

playwright 与 selenium 区别

  • selenium

    WebDriver 的底层实现是通过调用浏览器驱动程序与浏览器交互来实现的。具体来说,下面是 WebDriver 底层实现的一般步骤:

    1. 用户编写测试脚本或代码,调用 Selenium 提供的 API。
    2. 测试脚本通过 Selenium 提供的 WebDriver API(如 find_element_by_xpath()click()send_keys() 等)发送命令。
    3. WebDriver 接收到命令后,根据命令的内容,调用相应的浏览器驱动程序。
    4. 浏览器驱动程序将命令转换为对应浏览器支持的实际操作,如打开网页、定位元素、模拟用户输入等。
    5. 浏览器驱动程序通过浏览器提供的接口来与浏览器进行通信,在浏览器中执行相应的操作。
    6. 浏览器执行完操作后,将结果返回给 WebDriver。
    7. WebDriver 将结果返回给测试脚本,测试脚本根据返回的结果进行进一步操作或断言。

    流程: python代码调用selenium - webdriver - 浏览器驱动控制浏览器 - 信息返回给webdriver - 返回给python代码

  • playwright

    Playwright 的底层实现是通过对不同浏览器的原生 API 进行封装和封装,并通过自定义协议与浏览器通信来实现。playwright是直接操作浏览器,所以playwright会比selenium快
    !!!playwright自带有驱动,本机可以不用下载浏览器

    1. 启动浏览器实例:
      • 首先,通过 Playwright 提供的 API 创建并启动所需浏览器实例,例如 Chromium、Firefox 或 WebKit。
    2. 发送指令与浏览器交互:
      • 使用 Playwright 提供的方法,向浏览器发送指令以执行测试脚本中的操作,例如打开链接、定位元素、模拟用户行为等。
    3. 执行测试脚本:
      • 通过浏览器实例执行测试脚本,执行操作并获取结果,例如页面元素状态、网络请求信息等。
    4. 关闭浏览器实例:
      • 当测试脚本执行完成后,通过 Playwright 提供的 API 关闭浏览器实例,释放资源。

环境安装

1
$ pip install playwright

playwright自带浏览器驱动安装

1
2
3
4
5
6
7
8
9
10
# 安装 firefox、webkit、chromium
playwright install

# 单独安装
# firefox
playwright install firefox
# webkit
playwright install webkit
# chromium
playwright install chromium

playwright 使用本地浏览器运行

1
2
3
4
5
6
7
8
9
10
from playwright.sync_api import sync_playwright
_chrome_path = r"C:\Program Files\Google\Chrome\Application\chrome.exe"
_msdege_path = r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
p = sync_playwright().start()
# 启动本地Chrome浏览器实例,如果需要使用无头模式,需传递 args=['--headless']
# browser = p.chromium.launch(headless=False, executable_path=_chrome_path, args=['--headless'])
browser = p.chromium.launch(headless=False, executable_path=_chrome_path)
page = browser.new_page()
baidu = page.goto("https://www.baidu.com")
page.pause()

脚本录制功能

启用录制工具命令
1
$ playwright codegen

使用内置支持浏览器类型

1
2
3
4
5
6
7
8
9
# 使用firefox
with sync_playwright() as p:
firefox_browser = p.firefox
# 使用webkit
with sync_playwright() as p:
webkit_browser = p.webkit
# 使用chromium
with sync_playwright() as p:
chromium_browser = p.chromium

使用方法 - 本地执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# 同步api
# 创建一个playwright上下文管理器
from playwright.sync_api import sync_playwright
'''上下文方式'''
with sync_playwright() as p:
# 启动浏览器并启动
browser = p.chromium.launch(headless=False)
# 创建浏览器的上下文
context = browser.new_context()
# 创建一个页面
page = context.new_page()
# or 使用browser创建
page = browser.new_page()
# 打开页面
page.goto("https://www.baidu.com")
browser.close()

# 提供异步api
from playwright.async_api import async_playwright
import asyncio

async def main():
async with sync_playwright() as p:
# 启动浏览器
browser = p.chromium.launch(headless=False)
# 创建浏览器的上下文
context = browser.new_context()
# 创建一个页面
page = context.new_page()
# 打开页面
page.goto("https://www.baidu.com")
browser.close()

asyncio.run(main())


'''不用上下文方式的同步写法'''
p = sync_playwright().start()
'''
sync_playwright()方法返回一个 Playwright 对象,表示同步 API 的入口。
start()方法用于启动 Playwright,并返回一个 Playwright 对象,该对象可以用于启动不同类型的浏览器。
'''
# 启动浏览器
browser = p.chromium.launch(headless=False)
'''
p.chromium 表示使用 Chromium 浏览器。
launch(headless=False) 方法启动一个 Chromium 浏览器实例,其中 headless=False 表示以有界面的方式启动浏览器,如果要以无界面方式启动,可以将 headless=True。
返回一个 Browser 对象,表示一个浏览器实例。
'''
# 创建浏览器的上下文
context = browser.new_context()
'''
browser.new_context() 方法在创建一个新的浏览器上下文,上下文包含页面集合、网络请求拦截器等。一个浏览器可以包含多个上下文。
返回一个 BrowserContext 对象,表示浏览器上下文。
'''
# 创建一个页面
page = context.new_page()
'''
context.new_page() 方法在当前上下文中创建一个新的页面。
返回一个 Page 对象,表示新创建的页面。可以在这个页面中进行网页操作和页面交互。
'''
# 打开页面
page.goto("https://www.baidu.com")
# 需要自行终止程序
# 用于关闭单个页面,释放页面占用的资源
page.close()
# 用于关闭浏览器上下文,关闭上下文将关闭其中的所有页面并释放上下文占用的资源。
context.close()
# 用于关闭整个浏览器实例,关闭浏览器将关闭包含的所有上下文和页面,并释放浏览器占用的资源。
browser.close()


远程执行

connect(options): 在远程计算机上连接浏览器实例

在远程计算机上安装Playwright,并通过终端命令 npx playwright serve --port=8585 启动Playwright Server。
在本地计算机上可以通过以下方式连接到远程浏览器实例

1
2
3
4
5
6
7
8
9
10
11
12
import playwright.sync_api as p  

# 创建浏览器对象并连接到远程浏览器实例
browser = p.chromium.connect({
"wsEndpoint": "ws://[远程计算机IP]:8585",
})
# 创建一个新的页面
page = browser.new_page()
# 在新页面加载并展示百度网站
page.goto("https://www.baidu.com")
# 关闭浏览器
browser.close()

设置整个浏览器对象操作设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def launch(
self,
*,
executable_path: typing.Optional[typing.Union[str, pathlib.Path]] = None,
channel: typing.Optional[str] = None,
args: typing.Optional[typing.Sequence[str]] = None,
ignore_default_args: typing.Optional[
typing.Union[bool, typing.Sequence[str]]
] = None,
handle_sigint: typing.Optional[bool] = None,
handle_sigterm: typing.Optional[bool] = None,
handle_sighup: typing.Optional[bool] = None,
timeout: typing.Optional[float] = None,
env: typing.Optional[typing.Dict[str, typing.Union[str, float, bool]]] = None,
headless: typing.Optional[bool] = None,
devtools: typing.Optional[bool] = None,
proxy: typing.Optional[ProxySettings] = None,
downloads_path: typing.Optional[typing.Union[str, pathlib.Path]] = None,
slow_mo: typing.Optional[float] = None,
traces_dir: typing.Optional[typing.Union[str, pathlib.Path]] = None,
chromium_sandbox: typing.Optional[bool] = None,
firefox_user_prefs: typing.Optional[
typing.Dict[str, typing.Union[str, float, bool]]
] = None
) -> "Browser":

可以设置浏览器特定操作,可以在browser = p.chromium.launch()方法中传递特定参数

  • headless:设置是否以无头浏览器运行,默认为True。若是本地浏览器,可能还需要设置实际参数进行操作
  • show_mo:模拟用户操作的延迟时间,单位ms,当前浏览器全局生效,默认为0
  • args:传递浏览器实例的其他命令行参数列表
  • proxy:设置代理
  • executable_path:启动本地浏览器时路径
  • downloads_path:设置下载路径

等待 - 且注意事项

由于Playwright底层是基于异步实现的,使用time.sleep()会导致代码阻塞,而Playwright的操作可能会在时间等待期间进行异步操作,这样就会导致程序出现错误或不可预测的行为。会导致出错

强制等待 - page.wait_for_timeout(1000) - 单位:ms

如果要使用强制等待可以使用这个代替

page.wait_for_selector(selector, options?)
  • 参数

    • options - [“attached”, “detached”, “hidden”, “visible”] 默认visible
  • 等待指定选择器匹配的元素出现在页面上

1
2
3
4
5
6
7
8
9
10
11
12
13
page.wait_for_selector('#myElement')
'''
可传入对象:
timeout:超时
state:状态
'attached': 元素已附加到 DOM 中。
'detached': 元素已从 DOM 中移除。
'visible': 元素在页面上可见(即没有设置 display: none,visibility: hidden,或者其尺寸为 0)。
'hidden': 元素在页面上不可见。
strict:
'''
# 等待元素消失
page.wait_for_selector('#myElement', state='hidden')
wait_for_function(page_function)

等待js函数返回ture

1
page.wait_for_function('document.querySelector("#myElement").textContent === "Hello"')
wait_for_event(target, event)

等待指定事件在目标对象上触发。

1
page.wait_for_event('domcontentloaded') 
wait_for_navigation()

等待页面导航完成(例如页面加载或页面跳转)

1
page.wait_for_navigation(state, timeout) 
page.wait_for_load_state("load")

page对象的方法,用于等待页面加载到指定状态

  • 参数
    • state
      • domcontentloaded - 等到加载DOMContentLoaded事件
      • load - 等到加载load事件
      • networkidle - 等到500 ms没有网络请求交互
    • timeout
wait_for_request(url_or_predicate)

等待一个网络请求触发,并匹配给定的 URL 或谓词函数。

1
page.wait_for_request('https://example.com/data.json')  
wait_for_response(url_or_predicate)

等待一个网络响应返回,并匹配给定的 URL 或谓词函数。

1
page.wait_for_response('https://example.com/data.json')  
wait_for_url()

等待页面跳转某个url

1
page.wait_for_url('https://www.baidu.com')
locator().waitFor()
1
2
3
4
5
6
7
8
9
10
11
# 等待元素消失
page.locator('#my-id').waitFor({ state: 'detached' })
# 等待元素隐藏
page.locator('#my-id').waitFor({ state: 'hidden' })
# 等待元素显示
page.locator('#my-id').waitFor({ state: 'visible' })
# 等待元素可编辑
page.locator('#my-id').waitFor({ state: 'editable' })
# 等待元素被选中
page.locator('#my-id').waitFor({ state: 'checked' })

Browser(浏览器对象)

1
2
3
4
5
from playwright.sync_api import sync_playwright

p = sync_playwright().start()
# 生成浏览器对象并执行
browser = p.chromium.launch(headless=False)
创建浏览器上下文 - browser.new_context()
1
2
3
# 创建上下文
# 创建一个新的浏览器上下文。它不会与其他浏览器上下文共享 Cookie/缓存。
context = browser.new_context()
获取所有浏览器上下文对象 - browser.contexts
1
2
# 获取浏览器所有上下文对象,列表形式展示
contexts = browser.contexts
创建页面实例 - browser.new_page()
1
2
# 创建页面,这里会弹出页面窗口
page = browser.new_page()
获取浏览器所属的浏览器类型 - browser.browser_type
1
2
3
# 获取浏览器上下文对象类型 - BrowserType 对象
bType = browser.browser_type
# 也可以通过 p.chromium、p.firefox、p.webkit 等方式来获取对应的 BrowserType 对象。
  • 额外方法

    • 方法:

      • launch(options): 通过指定的选项启动一个指定类型的浏览器实例。

        1
        2
        # 根据 BrowserType 对象 启动一个新的想通浏览器实例  
        new_browser = chromium_browser_type.launch(headless=False)
      • connect(options): 连接到一个远程浏览器实例,可以在不同的计算机上控制同一个浏览器实例。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        # 获取 Chromium 浏览器类型对象  
        chromium_browser_type = p.chromium
        # 连接到一个远程 Chromium 浏览器实例
        browser = chromium_browser_type.connect({
        "wsEndpoint": "ws://[远程计算机IP]:8585",
        })
        # 创建一个新的页面
        page = browser.new_page()
        # 在新页面加载并展示百度网站
        page.goto("https://www.baidu.com")
        # 关闭浏览器
        browser.close()
判断浏览器对象是否连接 - browser.is_connected()
1
2
3
4
5
6
7
# 用于检查浏览器实例是否连接到 Playwright 服务器
# 检查浏览器实例是否连接到 Playwright 服务器
connected = browser.is_connected()
if connected:
print("Browser is connected to Playwright server.")
else:
print("Browser is not connected to Playwright server.")
客户端调试协议(CDP)会话 - browser.new_browser_cdp_session()

用于创建一个与浏览器实例关联的新的浏览器客户端调试协议(CDP)会话, 返回CDPsession实例

CDP(Chrome DevTools Protocol)会话在 Playwright 中的作用主要体现在以下几个方面:

  1. 执行高级的浏览器操作:通过 CDP 会话,您可以执行一些高级的浏览器操作,比如模拟用户输入、网络请求拦截、执行 JavaScript 等操作。这些高级操作通常是通过 CDP 提供的接口实现的,可以实现更灵活和定制化的功能。
  2. 调试浏览器:CDP 会话可以用于调试浏览器,包括检查 DOM 结构、网络请求、性能指标等。您可以通过 CDP 会话与浏览器进行交互,查看浏览器的内部状态。
  3. 监控浏览器性能:通过 CDP 会话,您可以监控浏览器的性能指标,如内存占用、CPU 使用率、页面加载时间等。这对于性能优化和调试非常有帮助。
1
2
3
4
5
6
7
8
9
10
11
import playwright.sync_api as p  
# 启动一个本地 Chrome 浏览器实例
browser = p.chromium.launch()
# 创建一个与浏览器实例关联的新的浏览器 CDP 会话
browser_cdp_session = browser.new_browser_cdp_session()
# 通过 CDP 会话执行一些高级的浏览器操作
browser_cdp_session.send('Runtime.evaluate', {'expression': 'console.log("Hello from CDP")'})
# 关闭 CDP 会话
browser_cdp_session.close()
# 关闭浏览器
browser.close()
事件监听 - browser.on()

绑定监听器,给浏览器对象操作绑定一个函数,当操作执行时会被监听到,触发该函数

1
2
3
4
5
6
7
8
9
# 定义事件处理函数  
def on_broswer_closed():
print("Browser is closed.")
# 启动一个本地 Chrome 浏览器实例
browser = p.chromium.launch()
# 注册 close 事件监听器
browser.on("disconnected", on_broswer_closed)
# 关闭浏览器
browser.close()
一次性事件监听 - browser.once()

用于为浏览器实例注册一次性事件监听器。只会被处罚一次,并在执行后自动移除,不会重复执行。

1
2
3
4
5
6
7
8
9
10
11
# 定义事件处理函数  
def on_browser_closed():
print("Browser is closed.")
# 启动一个本地 Chrome 浏览器实例
browser = p.chromium.launch()
# 注册 close 事件的一次性监听器
browser.once("disconnected", on_browser_closed)
# 关闭浏览器
browser.close()
# 再次触发 close 事件,但不会再次执行监听器中的代码
browser.close()
移除监听 - browser.remove_listener()

用于移除先前使用browser.on() 方法添加的事件监听器。

1
2
3
4
5
6
# 添加 close 事件监听器  
browser.on("disconnected", on_browser_closed)
# 关闭浏览器时触发 close 事件
browser.close() # 输出: Browser is closed.
# 移除 close 事件监听器
browser.remove_listener("disconnected", on_browser_closed)
开启性能跟踪 - browser.start_tracing(path, screenshots) & browser.stop_tracing()

用于开始对浏览器实例进行性能追踪。如网络请求、JavaScript 执行时间、内存占用等信息,以便分析和优化页面性能。

  • 参数
    • path="trace.json": 指定保存性能追踪数据的文件路径。在这个示例中,性能追踪数据将被保存在名为 trace.json 的文件中。
    • screenshots=True: 如果将此参数设置为 True,则在性能追踪过程中还会捕获页面截图。这对于分析页面加载过程中的视觉表现非常有用,可以帮助进一步了解页面的性能情况。
1
2
3
4
5
6
7
8
9
10
11
# 启动一个本地 Chrome 浏览器实例  
browser = p.chromium.launch()
# 开始对浏览器进行性能追踪
browser.start_tracing(path="trace.json", screenshots=True)
# 访问页面并执行一些操作
page = browser.new_page()
page.goto("https://www.example.com")
# 在这里执行其他操作...
# 停止性能追踪
# 调用 trace.stop() 方法来停止性能追踪,此时会生成一个包含追踪数据的 JSON 文件 trace.json。
browser.stop_tracing()
获取当前浏览器版本号 - browser.version

获取当前浏览器版本

1
2
brVerion = browser.version
print(brVerion) # 129.0.6668.29
关闭 - browser.close()
1
2
# 退出上下文
context = browser.new_context()

context(浏览器上下文)

浏览器上下文提供了一种操作多个独立浏览器会话的方法。

1
2
3
4
5
6
7
from playwright.sync_api import sync_playwright

p = sync_playwright().start()
# 生成浏览器对象并执行
browser = p.chromium.launch(headless=False)
# 创建浏览器的上下文
context = browser.new_context()

将 Cookie 添加到此浏览器上下文。此上下文中的所有页面都将安装这些 Cookie。

1
2
3
4
5
6
7
8
9
10
11
12
# 启动一个本地 Chromium 浏览器实例  
browser = p.chromium.launch()
# 创建浏览器的上下文
context = browser.new_context()
# 定义要添加的 cookie 数据,name,value,url(domain) 是必须项
cookie = {
'name': 'session',
'value': '123456',
'url': 'https://www.example.com' # 设置Cookie的URL
}
# 向上下文中添加 cookie
context.add_cookies([cookie])
获取cookie - context.cookies()

此方法将返回所有 Cookie。如果指定了 URL,则仅返回影响这些 URL 的 Cookie(cookie受跨域影响)

1
2
3
# 获取cookie
browser_context.cookies()
# [{'name': 'session', 'value': '123456', 'domain': 'www.example.com', 'path': '/', 'expires': -1, 'httpOnly': False, 'secure': True, 'sameSite': 'Lax'}]
清除cookie记录 - context.clear_cookies
1
context.clear_cookies()
浏览器授权 - context.grant_permissions()

向浏览器上下文授予指定的权限。如果指定,则仅向给定来源授予相应的权限,可以模拟浏览器环境中向页面添加例如摄像头、麦克风、通知等权限。

  • 环境光传感器 - ambient-light-sensor
  • 相机 - camera
  • 地理位置 - geolocation
  • 陀螺 - gyroscope
  • 麦克风 - microphone
  • 通知 - notifications
  • 存储访问 - storage-access
1
2
3
4
5
6
7
8
9
10
11
# 启动一个浏览器实例  
browser = p.chromium.launch()
# 创建一个新页面
page = browser.new_page()
# 向页面授予摄像头和麦克风权限
page.grant_permissions(['camera', 'microphone'])
# 访问一个需要摄像头和麦克风权限的网站
page.goto('https://www.example.com')
# 进行其他操作...
# 关闭浏览器
browser.close()
清除权限 - context.clear_permissions
1
context.clear_permissions()
关闭浏览器上下文context.close(str)

关闭浏览器上下文。属于该浏览器上下文的所有页面(page)都将关闭。

  • 参数

  • str(可选) - 要报告给被上下文关闭中断的操作的原因 (添加于:v1.40)

  • context.add_init_script()

    用于向浏览器上下文(Context)中添加自定义的初始化脚本(init script)。这个初始化脚本会在每次新页面被打开前执行,可以用来注入自定义的 JavaScript 代码,从而在页面加载前执行特定操作或修改页面行为。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # 创建初始化脚本,用于在每次新页面加载前输出一条信息  
    init_script = """
    console.log('Custom init script executed!');
    """
    # 启动一个本地 Chromium 浏览器实例
    browser = p.chromium.launch()
    # 创建浏览器的上下文
    context = browser.new_context()
    # 向上下文中添加初始化脚本
    context.add_init_script(init_script)
    # 在上下文中创建新页面
    page = context.new_page()
    # 在新页面中访问网站
    page.goto("https://example.com")
    # 等待页面加载完成
    page.wait_for_load_state("load")
监听等待特定事件触发 - expect_event()

用于在页面监听并等待特定事件的方法。通过 expect_event 方法,您可以设置条件来监听页面中触发的特定事件,并在事件发生时进行断言验证或执行其他操作。

等待给定的 event 触发 - wait_for_event()
context.new_cdp_session()

返回新创建的会话

创建一个页面 - context.new_page()
1
2
3
browser = p.chromium.launch(headless=False)
context = browser.new_context()
page = context.new_page() # 默认访问about:blank 返回一个页面对象page
用于浏览器上下文捕获网络请求的功能 - context.route()

启用路由后,与 URL 模式匹配的每个请求都将暂停

1
2
3
browser = p.chromium.launch(headless=False)
context = browser.new_context()
context.route() # 默认访问about:blank 返回一个页面对象page
清除router捕获功能 - unroute()
清除使用 browser_context.route()和 browser_context.route_from_har()创建的所有路由规则 - unroute_all()
从 HAR 文件创建虚拟的路由 - route_from_har

pass

统一设置超时时间 - set_default_navigation_timeout(timeout)
  • 超时生效的方法

    • page.go_back()

    • page.go_forward()

    • page.goto()

    • page.reload()

    • page.set_content()

    • page.expect_navigation()

1
2
# 设置默认的页面导航超时时间为10秒(10000毫秒)  
context.set_default_navigation_timeout(10000)
设置所有超时时间 - set_default_timeout(timeout)
1
context.set_default_timeout(timeout)
设置请求头 - set_extra_http_headers()

设置该上下文请求时发送的,如果相同参数,将使用页面特定的标头值而不是浏览器上下文标头值。

1
context.set_extra_http_headers(headers)
设置地理位置 - set_geolocation()
1
2
3
context.set_geolocation({"latitude": 59.95, "longitude": 30.31667})
# latitude float 纬度,介于 -90 和 90 之间。
# longitude float 经度,介于 -180 和 180 之间。
模拟网络离线状态 - set_offline()
1
context.set_offline(offline)
获取浏览器缓存数据 - storage_state()
1
2
context.storage_state(path)
# path 文件保存路径,可选参数。如果未提供路径,则仍将返回存储状态,但不会保存到磁盘。
获取上下文中所有现有的后台页面 - background_pages

用于获取当前浏览器上下文(Context)关联的所有后台页面(background pages)。后台页面是指在后台运行的页面,通常是一些扩展或插件创建的页面,独立于当前活动页面,可以在后台执行一些任务或逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 启动一个本地 Chromium 浏览器实例  
browser = p.chromium.launch()
# 创建浏览器的上下文
context = browser.new_context()
# 在上下文中创建新页面
page = context.new_page()
# 在新页面中打开一个后台页面,模拟一个插件或扩展操作
page.evaluate('window.open("about:blank", "_background")')
# 获取当前上下文关联的所有后台页面
background_pages = context.background_pages
# 输出后台页面数量和相关信息
print(f"Total background pages: {len(background_pages)}")
for bg_page in background_pages:
print(f"Background page ID: {bg_page.id}")
获取上下文所有打开的页面 - pages
1
context.pages
发起请求 - request

使用此 API 发出的请求将使用上下文 Cookie。

context相关事件

image-20241007165739866

on(“backgroundpage”)

适用于 Chromium 浏览器的持久上下文

在上下文中创建新的后台页面时发出

1
2
3
background_page = context.wait_for_event("backgroundpage")
# or
browser_context.on("backgroundpage", handler)
on(“close”)

在浏览器上下文关闭时发出

  • 浏览器上下文已关闭。
  • 浏览器应用程序已关闭或崩溃。
  • 已调用 browser.close()方法。
on(“console”)

当页面中的 JavaScript 调用某个控制台 API 方法(例如 console.logconsole.dir)时发出

on(“dialog”)

当出现 JavaScript 对话框(例如 alertpromptconfirmbeforeunload)时发出。侦听器必须 dialog.accept()或 dialog.dismiss()对话框 - 否则页面将 冻结等待对话框,并且像单击之类的操作将永远不会完成

1
context.on("dialog", lambda dialog: dialog.accept())
on(“page”)

在 BrowserContext 中创建新页面时发出该事件。该页面可能仍在加载中。弹出页面也会触发该事件

on(“request”)

从通过此上下文创建的任何页面发出请求时发出。 request 对象是只读的。要仅侦听来自特定页面的请求,请使用 page.on(“request”)

on(“requestfailed”)

请求失败时发出,例如超时。如果仅侦听来自特定页面的失败请求,请使用 page.on(“requestfailed”)。

on(“requestfinished”)

在下载响应正文后请求成功完成时发出。对于成功的响应,事件顺序为 request、response 和 requestfinished。要侦听来自特定页面的成功请求,请使用 page.on(“requestfinished”)。

on(“response”)

当收到请求的 响应 状态和标头时发出。对于成功的响应,事件顺序为 request、response 和 requestfinished。要监听来自特定页面的响应事件,请使用 page.on(“response”)。

on(“serviceworker”)

仅基于 Chromium 的浏览器支持 Service Worker。

当在上下文中创建新的服务工作线程时发出。

on(“weberror”)

当此上下文中任何页面的异常未处理时发出。要监听来自特定页面的错误,请改用 page.on(“pageerror”)。

page元素定位

定位器 - page.Locator() 方法

定位器(Locator)是 Playwright 的自动等待和重试能力的核心部分。定位器是一种随时在网页上查找元素的方法,用于在元素上执行诸如 .click、.fill 之类的操作。可以使用 page.locator(selector, **kwargs) 方法创建自定义定位器。支持css、xpath。返回一个定位器;Locator 对象的主要优点是它们可以自动等待元素出现在页面上

参数讲解 locator(selector, **kwargs)

selector - 用于解析 DOM 元素的选择器。

定位器返回单个元素 - page.locator()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 通过特定选择器查找
loc = page.locator('button[type="submit"]') # 定位所有button下的DOM的type="submit"
loc = page.locator('input[name="username"][type="text"]') # 查找 name 为 "username" 且 type 为 "text" 的 input 元素 # 组合多个查找条件
# 通过id查找
loc = page.locator('#kw')
# 通过classname查找元素
loc = page.locator('.class')
loc = page.locator('.class1.class2.class3') #多个classname属性
# 通过 XPath 表达式查找元素
loc = page.locator('//div[@id="myDiv"]/span')
loc = page.locator('//div[@class="myClass"]')
loc = page.locator('//input[@name="myInput"]')
loc = page.locator('//input[@name="myInput" and @type="text"]') # 多个属性
# 通过文本内容查找元素
loc = page.locator('//span[text()="Sign In"]')
loc = page.locator(':text("Hello, World!")')
# 通过标签查找定位
loc = page.locator('div')
# 通过属性值模糊匹配查找元素
loc = page.locator('input[name^="user"]') # 匹配 name 属性值以 "user" 开头的input元素
# 通过索引查找元素集合中的特定元素:
loc = page.locator('input')[0] # 获取第一个 input 元素 与.nth()类似
page.locator() 方法返回一个元素定位器 Locator

所以通过page.locator() 返回的对象可以继续使用.locator()进行内部子元素定位

链式调用

page.locator() 的返回值本身也有 locator() 方法,这意味着你可以链式地定位元素。例如,

1
2
page.locator('.my-class').locator('.my-subclass')
# 会定位到所有具有 "my-class" 类的元素中,再次具有 "my-subclass" 类的元素。
返回全部元素 - locator.all()

locator.all() 不会等待元素匹配定位器,而是立即返回页面中存在的内容。

当定位器指向元素列 表时,这将返回一个定位器数组,指向它们各自的元素。

1
2
# 查找所有带有 class 为 'example' 的元素  
elements = page.locator('.example').all() # []
返回全部匹配元素的数量 - locator.count()
1
2
# 查找所有带有 class 为 'example' 的元素,并返回数量
elements = page.locator('.example').count() # int
匹配元素的文本内容 - locator.all_inner_texts()
1
2
3
4
5
6
7
8
9
# 获取文本内容
page.get_by_role('.example').all_inner_texts()
page.get_by_role('.example').inner_text()
# 获取元素的文本内容
page.locator('.example').textContent()
# 获取元素的html内容
page.locator('.example').innerHTML()
# 获取元素外部的html内容
page.locator('.example').outerHTML()
获取元素的DOM - locator.inner_html()
1
page.get_by_role("link").inner_html()
获取元素的文本内容 - locator.inner_text()
1
page.get_by_role("link").inner_text()
获取元素DOM属性值 - locator.get_attribute()

用于从匹配的元素中获取指定属性的值。您可以指定要获取的属性名称,并返回相应的属性值。

1
2
3
4
5
6
# 选择要获取属性值的元素  
element_locator = page.locator('input[type="text"]')

# 获取元素的 "value" 属性值
value_attribute = element_locator.get_attribute('value')
print(f'The value attribute of the element: {value_attribute}')
匹配多种元素
  • and_()

    1
    2
    # 匹配两种or多种元素定位
    button = page.get_by_role("button").and_(page.getByTitle("Subscribe"))
  • 使用多种元素匹配

    1
    2
    # 查找具有 role 为 'button' 和 title 为 'Subscribe' 的元素
    button = page.query_selector('[role="button"][title="Subscribe"]')
设置字段值locator.fill()
1
2
# 为输入字段设置值
page.get_by_role("textbox").fill("example value")
输入框聚焦 - locator.focus()

用于将页面上匹配的元素(通过定位器选择)设置为焦点。这可以模拟用户手动点击元素以使其聚焦的操作。

1
2
3
4
# 选择要设置焦点的输入框元素  
input_locator = page.locator('input[type="text"]')
# 将输入框元素设置为焦点
input_locator.focus()
输入框焦点 - locator.blur()
1
2
3
4
5
6
# 定位到一个输入框元素  
input_element = page.locator('input[type="text"]')
# 输入文本到输入框并使其失去焦点
input_element.focus() # 将焦点设置在指定的输入元素
input_element.fill('Hello, Playwright!') # 输入内容
input_element.blur() # 将焦点从指定的输入元素移除
获取定位属性的元素的边界框信息 - locator.bounding_box()
1
2
3
4
5
6
7
# 获取具有 role 为 'button' 的元素的边界框信息  {横坐标, 纵坐标, 宽, 高}
bounding_box = page.locator('[role="button"]').bounding_box()
print(bounding_box) # {'x': 0, 'y': 0, 'width': 1280, 'height': 60}

# 获取的信息可以通过模拟鼠标进行点击
page.mouse.click(box["x"] + box["width"] / 2, box["y"] + box["height"] / 2)
# 除以2是因为,点击目标元素的中心位置
选中选框/按钮元素 - locator.check()

确保选中复选框或单选按钮元素, 如果不是,则此方法会抛出异常。如果元素已选中,则此方法立即返回。注意:需要将元素滚动到视图中。

1
2
3
4
5
6
7
8
# 定位带有 role 为 'checkbox' 的元素并选中它  
checkbox_element = page.locator('input[type="checkbox"]')
# 判断复选框当前是否未选中,然后选中它
if not checkbox_element.is_checked():
checkbox_element.check()
print("复选框已选中")
else:
print("复选框已处于选中状态")
清除选中元素- locator.clear()

此方法会等待 可操作性 检查,聚焦元素,清除其内容并在清除后触发 input 事件。

如果目标元素不是 <input><textarea> 或 [contenteditable] 元素,则此方法会抛出错误。但是,如果元素位于具有关联 控件 的 <label> 元素内,则将清除该控件。

1
2
3
4
input_locator = page.locator('input[type="text"]')  # 替换成您要操作的输入框的选择器  
input_locator.fill("Hello, World!") # 填充文本框内容
# 清空输入框中的内容
input_locator.clear()
点击选中元素 - locator.click()
1
2
3
# 通过 CSS 选择器定位具有 role 为 'button' 的元素并点击  
button_element = page.locator('[role="button"]')
button_element.click()
双击选中元素 - locator.dblclick()
1
2
3
# 双击选中元素,参数与click()一致
button_element = page.locator('[role="button"]')
button_element.dblclick()
locator.dispatch_event()

以编程方式在匹配的元素上调度事件

1
2
3
4
5
# 通过 CSS 选择器定位需要模拟点击事件的元素  
button_locator = page.locator('button')

# 使用 evaluate 方法来执行 JavaScript 代码模拟触发点击事件
button_locator.evaluate('(element) => element.click()')
元素拖动 - locator.drag_to()
1
2
3
4
5
6
7
8
9
10
11
12
13
# 选择要拖动的源元素和目标元素  
source_locator = page.locator('source_element_selector')
target_locator = page.locator('target_element_selector')
# 将源元素拖动到目标元素位置
source_locator.drag_to(target_locator)

# 将target目标元素从某位置移动到特定位置
source.drag_to(
target,
source_position={"x": 34, "y": 7},
target_position={"x": 10, "y": 20}
)

执行js代码 - locator.evaluate()

执行 JavaScript 代码,并将结果返回给 Python 程序。

1
2
3
# 使用 evaluate 方法获取页面中某个节点的 innerText  
inner_text = page.evaluate('(node) => node.innerText') #[,'selector_of_the_node')]
print(inner_text)
执行js代码 - locator.evaluate_all()

这个方法用于在页面上下文中对所有匹配的元素执行JavaScript代码,并返回一个包含所有结果的数组。它对于处理多个匹配元素非常有用

1
2
elements = page.locator('div').evaluate_all('(elements) => elements.map(el => el.textContent)')  
print(elements)
执行js代码 - locator.evaluate_handle()

这个方法用于在页面上下文中执行JavaScript代码,返回一个 Promise,用于在后续的操作中获取结果。并使用结果返回一个远程对象(evaluateHandle对象),可通过调用evaluateHandle.dispose()方法显式地释放。

1
2
3
4
handle = page.locator('button').evaluate_handle('(buttons) => buttons[0]')  
result = handle.evaluate('button => button.textContent')
print(result)
handle.dispose()
过滤器 - locator.filter()
1
2
3
4
5
6
7
8
9
10
11
12
row_locator = page.locator("tr")
# 通过locator进一步过滤
row_locator.filter(has_text="text in column 1").filter(
has=page.get_by_role("button", name="column 2 button")
).screenshot()

# 通过js语句进一步过滤
# 过滤出包含特定文本的链接元素
filtered_links_locator = links_locator.filter('(element) => element.textContent.includes("example")')

# 获取满足条件的链接元素数量
num_filtered_links = filtered_links_locator.count()
切换iframe - locator.frame_locator()

用于在定位器的上下文中查找嵌套的 iframe 元素。在页面上有嵌套的 iframe 结构时,可以使用该方法来选择 iframe 元素并返回一个新的定位器,该定位器指向iframe

1
2
3
4
5
6
# 选择页面中包含 iframe 的元素  
frame_element_locator = page.locator('iframe#iframe_id')
# 在 iframe 元素的上下文中查找内部元素
inner_element_locator = frame_element_locator.frame_locator('selector_inside_iframe')
# 对内部元素执行操作
inner_element_locator.click()
高亮显示选中元素 - element.highlight()

对选中的元素,进行高亮显示

1
2
element = page.locator().get_by_text("新闻")
element.highlight()
鼠标悬停 - element.hover()

将鼠标悬停在匹配的元素上。

1
page.get_by_role("link").hover()
获取元素的value属性值 - locator.input_value()

返回匹配的 <input><textarea><select> 元素的值。

1
value = page.get_by_role("textbox").input_value()
返回定位元素是否选中 - locator.is_checked()

返回该元素是否被选中。 如果该元素不是复选框或单选按钮输入,则抛出异常

1
checked = page.get_by_role("checkbox").is_checked()
返回该元素是否被禁用 - locator.is_disabled()
1
disabled = page.get_by_role("button").is_disabled()
判断元素是否可编辑 - locator.is_editable()
1
editable = page.get_by_role("textbox").is_editable()
判断元素是否启用 - locator.is_enabled()
1
enabled = page.get_by_role("button").is_enabled()
判断元素是否隐藏 - locator.is_hidden()
1
hidden = page.get_by_role("button").is_hidden()
判断元素是否可见 - locator.is_visible()
1
visible = page.get_by_role("button").is_visible()
按下元素 - locator.press()

类似聚焦匹配的元素并按下的组合键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
page.locator('a').press("Backspace")
'Backspace' - 回退键
'ArrowLeft' - 左方向键
'ArrowRight' - 右方向键
'ArrowUp' - 上方向键
'ArrowDown' - 下方向键
'Enter' - 回车键
'Escape' - Esc 键
'Tab' - Tab 键
'Space' - 空格键
'Delete' - 删除键
'Shift' - Shift 键
'Control' - Ctrl 键
'Alt' - Alt 键
'Meta' - Meta (Windows 徽标键或 Command 键)
'F1' - F1 键(对应到 F12 键)
# mac电脑特殊功能键
"""
Delete键:用于删除光标后的字符。在 Playwright 中可通过传递 "Delete" 给 press 方法来模拟按下 Delete 键。
Command键(⌘):作为快捷键的一部分,通常用于复制、粘贴等操作,但不是一个普通字符键。在 Playwright 中可通过传递 "Meta" 给 press 方法来模拟按下 Command 键。
Option键(⌥):用于输入特殊字符、标点符号等。在 Playwright 中可通过传递 "Alt" 给 press 方法来模拟按下 Option 键。
Control键(⌃):用于快捷键操作,在 Playwright 中可通过传递 "Control" 给 press 方法来模拟按下 Control 键。
Shift键(⇧):用于切换大小写、输入符号等。在 Playwright 中可通过传递 "Shift" 给 press 方法来模拟按下 Shift 键。
"""
滚动元素至可见 - locator.scroll_into_view_if_needed()

将滚动到元素可见。这个方法会检查元素是否在视窗内,如果不在的话会自动滚动到元素可见。

1
2
3
4
5
6
# 使用locator定位需要操作的元素  
element_locator = page.locator('button')
# 滚动到元素可见
element_locator.scroll_into_view_if_needed()
# 进行其他操作,如点击等
element_locator.click()
选择单个或多个select - element.select_option()
1
2
3
4
5
6
7
8
9
10
11
12
13
""" html
<select multiple>
<option value="red">Red</div>
<option value="green">Green</div>
<option value="blue">Blue</div>
</select>
"""
element = page.locator('select')
# 单个选项值或标签
element.select_option("blue")
element.select_option(label="blue")
# 多个选项值或标签
element.select_option(value=["red", "green", "blue"])
设置单选框或复选框状态 - locator.set_checked()

设置复选框或单选按钮元素状态。
确保匹配的元素是复选框或单选按钮输入
将元素滚动到视图中-

1
page.get_by_role("checkbox").set_checked(True)
获取单选框或复选框状态 - locator.is_checked()
1
page.get_by_role("checkbox").is_checked() # 返回布尔值
取消选中 - locator.uncheck()

确保复选框或单选按钮元素未被选中。

1
page.get_by_role("checkbox").uncheck()
等待 - locator.wait_for()

当定位器指定的元素满足 state 选项时返回。
如果目标元素已满足条件,则该方法立即返回。否则,将最多等待 timeout 毫秒,直到满足条件

1
2
order_sent = page.locator("#order-sent")
order_sent.wait_for()
文件上传 - locator.set_input_files()

将文件或多个文件上传到 <input type=file>。对于具有 [webkitdirectory] 属性的输入,仅支持单个目录路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Select one file
page.get_by_label("Upload file").set_input_files('myfile.pdf')

# Select multiple files
page.get_by_label("Upload files").set_input_files(['file1.txt', 'file2.txt'])

# Select a directory
page.get_by_label("Upload directory").set_input_files('mydir')

# Remove all the selected files
page.get_by_label("Upload file").set_input_files([])

# Upload buffer from memory
page.get_by_label("Upload file").set_input_files(
files=[
{"name": "test.txt", "mimeType": "text/plain", "buffer": b"this is a test"}
],
)
点击选择器元素 - locator.tap()
1
page.locator('a').tap()
返回选择器的第一个元素 - locator.first
1
page.locator('a').first
返回选择器的最后一个元素 - locator.last
1
page.locator('a').last
获取第n个元素 - locator.nth()

返回第 n 个匹配元素的定位器。它是从零开始的,nth(0) 选择第一个元素

1
page.locator('a').nth(2) # 返回定位的所有a标签的3个元素
返回当前元素页面信息 - locator.page
1
page.locator('a').page

选择器(Selector)是用于创建定位器的字符串。Playwright 支持许多不同的选择器,比如 Text、CSS、XPath 等。阅读 in-depth guide 文档,了解更多关于可用的选择器以及如何进行选择的信息。

page.select() 下拉选项

它可以根据 option 元素的值、文本内容或索引来选择下拉列表中的选项。

基本使用
1
2
3
4
5
6
7
8
9
10
# 通过值选择选项  
page.select('select#fruits', 'apple')

# 通过文本内容选择选项
page.select('select#fruits', { label: 'Banana' })

# 通过索引选择选项
page.select('select#fruits', { index: 2 })

await page.selectOption('select#fruits', 'apple')
多选下拉列表
1
page.select('select#fruits', ['apple', 'banana'])
超时设置
1
page.select('select#fruits', 'apple', timeout=5000)
联动选择

在某些情况下,下拉列表的选项会受到先前的选择而改变。可以使用waitForElementState方法来等待元素变为可选择状态。

1
2
await page.waitForElementState('select#fruits', 'visible')  
await page.select('select#fruits', 'apple')
内置定位器
page.get_by_role()通过显式和隐式可访问性属性进行定位。role属性

用于根据 role 属性来定位元素。
ARIA(无障碍开发) role属性, 有些元素隐式存在role属性

1
2
3
4
5
6
7
8
9
10
11
12
""" html
<label>
<input type="checkbox" /> Subscribe
</label>
<br/>
<button>Submit</button>
"""
page.get_by_role("checkbox", name="Subscribe").check()
page.get_by_role("button", name=re.compile("submit", re.IGNORECASE)).click()
"""
role: "alert" | "alertdialog" | "application" | "article" | "banner" | "blockquote" | "button" | "caption" | "cell" | "checkbox" | "code" | "columnheader" | "combobox" | "complementary" | "contentinfo" | "definition" | "deletion" | "dialog" | "directory" | "document" | "emphasis" | "feed" | "figure" | "form" | "generic" | "grid" | "gridcell" | "group" | "heading" | "img" | "insertion" | "link" | "list" | "listbox" | "listitem" | "log" | "main" | "marquee" | "math" | "meter" | "menu" | "menubar" | "menuitem" | "menuitemcheckbox" | "menuitemradio" | "navigation" | "none" | "note" | "option" | "paragraph" | "presentation" | "progressbar" | "radio" | "radiogroup" | "region" | "row" | "rowgroup" | "rowheader" | "scrollbar" | "search" | "searchbox" | "separator" | "slider" | "spinbutton" | "status" | "strong" | "subscript" | "superscript" | "switch" | "tab" | "table" | "tablist" | "tabpanel" | "term" | "textbox" | "time" | "timer" | "toolbar" | "tooltip" | "tree" | "treegrid" | "treeitem"
"""
page.get_by_text()通过文本内容定位。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
""" html
<div>Hello <span>world</span></div>
<div>Hello</div>
"""
# 匹配 <span>
page.get_by_text("world")
# 匹配第一个 <div>
page.get_by_text("Hello world")
# 匹配第二个 <div>
page.get_by_text("Hello", exact=True)
# 匹配多个 <div>
page.get_by_text(re.compile("Hello"))
# 匹配第二个 <div>
page.get_by_text(re.compile("^hello$", re.IGNORECASE))
page.get_by_label()通过关联标签的文本定位表单控件。

用于根据标签文本来定位元素。
允许通过关联的 <label>aria-labelledby 元素的文本,或通过 aria-label 属性来定位输入元素

1
2
3
4
5
6
7
""" html
<input aria-label="Username">
<label for="password-input">Password:</label>
<input id="password-input">
"""
page.get_by_label("Username").fill("john")
page.get_by_label("Password").fill("secret")
page.get_by_placeholder()按占位符定位输入。
1
2
3
4
'''
<input type="email" placeholder="请输入账号"/>
'''
page.get_by_placeholder("请输入账号")
page.get_by_alt_text()通过替代文本定位元素,通常是图像。

用于根据元素的 alt 属性文本值来定位元素的内置方法是page.locator()结合image标签的alt属性,或结合aria-label属性等

1
2
3
4
""" html
<img alt='Playwright logo'>
"""
image1 = page.locator.get_by_alt_text('Playwright logo')
page.get_by_title()通过标题属性定位元素。

允许根据元素的 title 属性定位元素

1
2
3
4
""" html
<span title='Issues count'>25 issues</span>
"""
expect(page.get_by_title("Issues count")).to_have_text("25 issues")
page.get_by_test_id()根据data-testid属性定位元素可以配置其他属性)

按测试 ID 定位元素。

1
2
3
4
""" html
<button data-testid="directions">Itinéraire</button>
"""
page.get_by_test_id("directions").click()
locator.get_by_placeholder( ) 通过占位符文本定位输入元素。
1
2
3
4
""" html
<input type="email" placeholder="name@example.com" />
"""
page.get_by_placeholder("name@example.com").fill("playwright@microsoft.com")

page操作

方法
打开页面 - page.goto()
  • 参数

    • url - 跳转或打开的url

    • referer - 设置页面导航的引用url,模拟用户从某个特定页面跳转过来

      1
      page.goto(url, referer="https://example.com")
    • timeout - 超时时间, 单位毫秒

    • wait_until - 等待条件

      • load : 等待页面完全加载
      • domcontentloaded : 等待DOM内容加载完成
      • network:等待所有请求完毕
      1
      page.goto(url, wait_until=['load', 'domcontentloaded'])
获取标题名称 - page.title()
1
2
3
# 获取页面标题  
title = page.title()
print("Page title:", title)
获取页面html内容 - page.content()
1
2
3
# 获取页面的HTML内容,包括文档类型声明
content = page.content()
print("Page content:", content)
关闭页面 - page.close()
1
page.close()
判断页面是否关闭 - page.is_closed()
1
2
# 判断页面是否关闭
if not page.is_closed():
刷新页面 - page.reload()
1
2
# 刷新当前页面
page.reload()
返回上一页历史 - page.go_back()

如果有多个重定向,则导航将使用最后一个重定向的响应进行解析。如果无法前进,则返回 null

1
page.go_back()
跳转下一页历史 - page.go_forward()

如果有多个重定向,则导航将使用最后一个重定向的响应进行解析。如果无法前进,则返回 null

1
page.go_forward()
页面截图 - page.screenshot(options)
  • options

    • path

      保存路径及截图名

      1
      page.screenshot(path="./images/a.png")
等待页面元素加载成功 - page.wait_for_selector(selector, timeout=1000)
1
2
3
4
5
6
7
# 在页面上等待直到出现具有 class 为 'example' 的元素  
element = page.wait_for_selector('.example', timeout=1000)
if element:
print("找到了具有 class 为 'example' 的元素")
# 对找到的元素进行操作
else:
print("超时:未找到匹配的元素")
在页面中执行 JavaScript 代码 - page.evaluate(script)
1
2
3
4
5
6
# 使用 evaluate 方法执行 JavaScript 代码,结果返回js代码的返回值
result = page.evaluate('''() => {
return 2 + 3;
}''')

print(result) # 输出结果为 5
设置页面视口大小 - page.set_viewport_size(width, height)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
'''
要实现窗口最大化,且适应所有设备,需要使用 p.__class__.screen 来动态获取窗口宽高达到适应所有设备
'''
from playwright.sync_api import sync_playwright

playwright = sync_playwright().start()
# 获取设备宽高
screen = playwright.__class__.screen
screen_primary = screen.screen_primary()
width = screen_primary.available_width
height = screen_primary.available_height
# 启动浏览器
browser = p.chromium.launch(headless=False)
# 创建浏览器的上下文
context = browser.new_context()
# 创建一个页面
page = context.new_page()
# 打开页面
page.goto("https://www.baidu.com")
# 全屏
page.set_viewport_size(width, height)
page.close()
context.close()
browser.close()
模拟键盘输入 - page.type(selector, text)
1
2
3
page.type('#kw', 'asdf')
# 也可以在定位器后面操作
page.locator('#kw').type('asdf')
模拟键盘输入 - page.fill(selector, text)
1
2
3
page.fill('#kw', 'asdf')
# 也可以在定位后面操作
page.locator('#kw').fill('asdf')
清空输入框内容 - page.clear()
1
page.locator('#kw').clear()
模拟鼠标点击 - page.click(selector)
  • 参数
    • button - left(左键)、right(右键)、middle(中键)、默认left
    • click_count - 点击次数,默认为1
    • delay - 按下、释放间隔时间
    • modifiers - 同时要按下的修饰键:’Alt’ | ‘Control’ | ‘ControlOrMeta’ | ‘Meta’ | ‘Shift’
    • position - 点击的坐标位置:{x:10, y:20}
    • timeout - 等待元素可点击时间,超过即超时
    • force - 释放绕过可操作性检查
1
2
3
page.fill('#kw')
# or
page.locator('#kw').click()
模拟鼠标双击 - page.dblclick(selector)

参考click()

模拟鼠标悬停 - page.hover(selector)
1
page.locator('#kw').hover
获取页面上的所有链接 - page.query_selector_all('a')

返回所有page中’a’标签,返回JSHandle对象

1
2
3
element1 = page.querySelector('button')
element2 = page.query_selector_all('a')
print(element1, element2)
设置单选框或复选框的状态 - page.set_checked()
1
page.get_by_role("checkbox").set_checked(True)
取消选中的单选框 - page.uncheck()
1
page.get_by_role("checkbox").uncheck()
select下拉框选项 - page.select_option()
  • 选中一个值

    1
    page.locator('#city').selector_option('shenzhen')
  • 选中多个值

    1
    page.locator('#city').selector_option(value=['shenzhen','guangzhou','dongguan'])
文件上传 - page.set_input_files()
  • 单文件上传

    1
    page.locator('#upload').set_input_files('myfile.pdf')
  • 上传多个文件

    1
    page.locator('#upload').set_input_files(['myfile1.pdf', 'myfile2.pdf'])
元素拖拽 - page.drag_and_drop()
  • 参数

    • source

      用于搜索要拖动元素的选择器。如果有多个元素满足选择器,则将使用第一个元素。

    • target

      用于搜索要放置元素的选择器。如果有多个元素满足选择器,则将使用第一个元素。

    • source_position (可选)相对于元素填充框的左上角单击此点上的源元素。如果未指定,则使用元素的某个可见点。

      • x
      • y
    • target_position(可选)相对于元素填充框的左上角放置在目标元素上的此点。如果未指定,则使用元素的某个可见点。

      • x
      • y
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    source = page.locator('#source')
    target = page.locator('#target')
    page.drag_and_drop("#source", "#target")
    # or specify exact positions relative to the top-left corners of the elements:
    page.drag_and_drop(
    "#source",
    "#target",
    source_position={"x": 34, "y": 7},
    target_position={"x": 10, "y": 20}
    )
聚焦元素 - page.focus()
1
page.locator("#source").focus()
用于模拟按下和释放键盘按键的操作 - press()
1
2
# 模拟按下回车键  
page.press('Enter')
对话框
  • page.on()
1
2
3
4
5
6
7
8
9
# 使用page.on监听dialog事件,监听并处理弹窗  
def handle_dialog(dialog):
print(f'Dialog message: {dialog.message}')
if dialog.type == 'alert':
dialog.accept()
elif dialog.type == 'confirm':
dialog.dismiss() # 或者 dialog.accept() 来接受弹窗

page.on('dialog', handle_dialog)
  • dialog方法&属性 解析

  • dialog 方法

    • dialog.accept(prompt_text)

      prompt_text 指要在提示中输入的文本。如果对话框的 type 不是提示,则不会产生任何效果

    • dialog.dismiss()

      在关闭对话框后返回。

  • dialog 属性

    • dialog.default_value

      如果对话框是提示,则返回默认提示值。否则,返回空字符串。

    • dialog.message

      对话框中显示的消息。

    • dialog.page

      启动此对话框的页面

    • dialog.type

      返回对话框的类型,可以是 alertbeforeunloadconfirmprompt 之一。

下载
  • page.on(filename)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    browser = p.chromium.launch()  
    page = browser.new_page()

    # 监听并处理下载
    def handle_download(download):
    download_path = f"/path/to/save/{download.suggested_filename}"
    download.path = download_path
    print(f"Download started: {download.url}, saving to {download_path}")
    # 添加下载监听
    page.on("download", handle_download)
    # 打开一个页面,包含下载链接
    page.goto('https://www.example.com')
    # 点击触发下载的链接
    page.click('a#download-btn')
    # 等待下载完成
    with page.expect_download() as download_info:
    download_info.save_as('/path/to/save/file.txt')
  • download方法&属性 解析

  • download 方法

    • download.cancel()

    取消下载。如果下载已经完成或取消,则不会失败。成功取消后,download.failure() 将解析为 'canceled'

    • download.delete()

    删除已下载的文件。如有必要,将等待下载完成。

    • download.failure()

    如果存在下载错误,则返回该错误。如有必要,将等待下载完成。

    • download.path()

    返回成功下载的已下载文件路径,如果下载失败/取消,则抛出异常。如有必要,该方法将等待下载完成。

    • download.save_as(“/path/to/save/at/“ + download.suggested_filename)

    另存外方法。下载仍在进行中时,可以安全地调用此方法。

  • download 属性

    • download.page

      获取下载所属的页面。

    • download.suggested_filename

      返回此下载的建议文件名。

    • download.url

      返回下载的网址。

文件上传
  • page.on(“filechooser”, handle_file_chooser)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def handle_file_chooser(file_chooser):  
    # 上传文件,file_chooser.accept(["绝对路径文件名"])
    file_chooser.accept(["/path/to/file.txt"])
    print("File uploaded successfully")

    # 监听文件选择器事件
    page.on("filechooser", handle_file_chooser)
    page.goto('https://www.example.com')
    # 找到上传文件的input元素
    upload_input = page.locator('input[type="file"]')
    # 触发文件选择器并上传文件
    upload_input.set_input_files("/path/to/file.txt")
  • 方法

    • file_chooser.set_files(files)

      设置此选择器关联的文件输入的值

  • 属性

    • file_chooser.element

      返回与此文件选择器关联的输入元素。

    • file_chooser.is_multiple()

      返回此文件选择器是否接受多个文件。

    • file_chooser.page

      返回此文件选择器所属的页面。

iframe
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
browser = p.chromium.launch()  
page = browser.new_page()

page.goto('https://www.example.com')

# 找到包含 iframe 的元素
iframe_element = page.locator('iframe#my-iframe')

# 进入 iframe 内部
frame = iframe_element.content_frame()

# 在 iframe 内部执行操作,比如查找元素并进行点击
frame.click('button#submit-button')

browser.close()
注册自定义的定位器处理程序 - page.add_locator_handler()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 自定义的定位器处理程序,用于根据元素的文本内容来定位元素  
def text_locator_handler(page, text):
return page.locator(f'//*[text()="{text}"]')

with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()

# 注册自定义的定位器处理程序
page.add_locator_handler('text', text_locator_handler)

page.goto('https://www.example.com')

# 使用自定义的文本定位器来定位包含指定文本的元素
element = page.locator('text=Hello World')
element.click()

browser.close()
删除自定义定位处理程序 - page.remove_locator_handler()
1
page.remove_locator_handler(locator)
页面置顶 - page.bring_to_front()

将页面置顶(激活标签页)。

脚本调试 - page.pause()

执行到这一步时,会会弹出playwright调试工具(可以使用playwright定位方法),打断点;类似于前端debugger

img

生成pdf - page.pdf(path=”page.pdf”)
属性
获取页面的 URL - page.url
获取浏览器所属上下文 - page.context
获取附加页面的所有框架数组 - page.frames
指示页面已关闭 - page.is_closed()
页面的主框架 - page.main_frame
获取页面宽高 - page.viewport_size
获取页面关联的视频对象 - page.video

鼠标操作

每个 页面 对象都有自己的鼠标,可通过 page.mouse 访问。

移动 - mouse.move(x, y)

调度 mousemove 事件。

1
mouse.move(x, y)
按下 - mouse . down()

调度 mousedown 事件。

1
mouse.down()
抬起 - mouse.up()

调度 mouseup 事件。

1
mouse.up()
滚轮 - mouse.whell()

调度 wheel 事件。此方法通常用于手动滚动页面。有关滚动的其他方法

1
mouse.wheel(delta_x, delta_y)
点击 - mouse.click(x, y)

类似mouse.move(x, y)、mouse.down()、mouse.up() 的快捷方式。

1
mouse.click(x, y)
双击 - mouse.dblclick(x, y)

mouse.move(x, y)、mouse.down()、mouse.up()、mouse.down() 和 mouse.up() 的快捷方式。

1
mouse.dblclick(x, y)

键盘操作

在大多数情况下,您应该使用 locator.fill() 代替。只有在页面上有特殊的键盘处理时才需要逐个按键

page.keyboard 键盘 属性&方法

keyboard.down(key)

键的示例如下:
F1 - F12Digit0- Digit9KeyA- KeyZBackquoteMinusEqualBackslashBackspaceTabDeleteEscapeArrowDownEndEnterHomeInsertPageDownPageUpArrowRightArrowUp 等。
还支持以下修饰键快捷键:ShiftControlAltMetaShiftLeftControlOrMetaControlOrMeta 在 Windows 和 Linux 上解析为 Control,在 macOS 上解析为 Meta
按住 Shift 键将键入与 key 对应的大写字母文本。
如果 key 是单个字符,则区分大小写,因此值 aA 将生成不同的文本。

1
2
3
4
5
6
7
8
9
10
11
# 按下键盘的 "Enter" 键  
page.keyboard.down("Enter")
# 等待一段时间,模拟按键按下的动作
page.wait_for_timeout(2000)
# 松开键盘的 "Enter" 键
page.keyboard.up("Enter")
# 全选
# on windows and linux
page.keyboard.press("Control+A")
# on mac_os
page.keyboard.press("Meta+A")
keyboard.insert_text(key)

仅调度 input 事件,不发出 keydownkeyupkeypress 事件
用户在键盘上键入文本的操作。这个方法可以用来模拟用户键入文本到输入框或其他元素上。

1
2
3
4
5
6
# 定位到输入框  
input_element = page.locator('input[type="text"]')

# 向输入框发送文本
input_element.focus()
page.keyboard.insert_text("Hello, World!")
keyboard.press(key)

在大多数情况下,您应该使用 locator.press() 代替。

模拟按下键盘上的特定按键

1
2
3
4
# 模拟输入 'a'
page.keyboard.press("a")
# 模拟按下键盘的 "Enter" 键
page.keyboard.press('Enter')
keyboard.type()

在大多数情况下,您应该使用 locator.fill() 代替。只有在页面上有特殊的键盘处理时才需要逐个按键

1
2
3
4
5
6
7
# 定位到输入框元素  
input_element = page.locator('input[type="text"]')

# 聚焦输入框
input_element.focus()
# 输入文本
page.keyboard.type("Hello, World!")
keyboard.up(key)

调度一个 keyup 事件。

1
2
3
4
5
6
7
8
# 按下键盘的 "Enter" 键  
page.keyboard.down("Enter")

# 等待一段时间,模拟按键按下的动作
page.wait_for_timeout(2000)

# 松开键盘的 "Enter" 键
page.keyboard.up("Enter")

请求监听 - request

当页面发送网络资源请求时,页面 都会发出以下事件序列

  • 当页面发出请求时,会发出 page.on(“request”)。
  • 当/如果收到请求的响应状态和标头时,会发出 page.on(“response”)。
  • 当响应正文下载完成且请求完成时,会发出 page.on(“requestfinished”)。
1
2
3
4
5
6
7
# 监听页面内请求  
def on_request(request):
print('Request URL:', request.url)
print('Request Method:', request.method)
print('Request Headers:', request.headers)

page.on('request', on_request)
方法
包含所有请求 HTTP 标头 - request.all_headers()

标头名称采用小写形式。

1
2
3
4
5
# 监听页面内请求  
def on_request(request):
headers = request.all_headers()
print(headers)
page.on('request', on_request)
返回与名称匹配的标头的值 - request.header_value(name)

名称不区分大小写。

1
2
3
4
5
# 监听页面内请求
def on_request(request):
headers = request.header_value('accept')
print(headers)
page.on('request', on_request)
一个数组,包含与此请求关联的所有请求 HTTP 标头 - request.headers_array()

以键值对的形式返回

1
2
3
4
5
# 监听页面内请求
def on_request(request):
headers = request.headers_array()
print(headers)
page.on('request', on_request)
返回匹配的 响应 对象 - request.response()

如果由于错误而未收到响应,则返回 null

1
2
3
4
def on_request(request):
headers = request.response()
print(headers)
page.on('request', on_request)
返回给定请求的资源大小信息 - request.sizes()
1
2
3
4
def on_request(request):
headers = request.sizes()
print(headers)
page.on('request', on_request)
属性
记录所有失败请求 - request.failure
1
page.on("requestfailed", lambda request: print(request.url + " " + request.failure))
获取请求方法 - request.method
1
page.on("requestfailed", lambda request: print(request.url + " " + request.method))
请求的帖子正文 - request.post_data
1
page.on("requestfailed", lambda request: print(request.url + " " + request.post_data))
返回解析后的请求正文 - request.post_data_json
1
page.on("requestfailed", lambda request: print(request.url + " " + request.post_data_json))
重定向前请求 - request.redirected_from

没有重定向操作则为None

1
page.on("requestfailed", lambda request: print(request.url + " " + request.redirected_from))
重定向后请求 - request.resource_type
1
page.on("requestfailed", lambda request: print(request.url + " " + request.redirected_to))
渲染引擎感知到的请求资源类型 - request.resource_type
1
page.on("requestfailed", lambda request: print(request.url + " " + request.resource_type))

响应 - response

方法
关联的所有响应 HTTP 标头 - response.all_headers()
1
page.on("response", lambda request: print(request.url + " " + response.all_headers()))
响应正文的缓冲区 - response.body()
1
page.on("response", lambda request: print(request.url + " " + response.body()))
返回与名称匹配的标头的值 - header_value()
1
page.on("response", lambda request: print(response.header_value('content-type')))
一个数组,包含与此响应关联的所有请求 HTTP 标头 - response.headers_array()
返回服务器的 IP 地址和端口 - response.server_addr()
返回响应正文的 JSON 表示形式 - response.json()
返回响应正文的文本表示形式 - response.text()
属性
一个包含响应 HTTP 标头的对象 - response.headers
返回响应是否成功 - response.ok
返回匹配的 Request 对象 - response.request
返回响应状态码 - response.status
响应的状态文本(成功时通常为“OK”) - response.status_text
响应url - response.url

CDP(Chrome DevTools Protocol)会话

CDPSession 实例用于与原始 Chrome Devtools 协议进行通信

  • 可以使用 session.send 方法调用协议方法。
  • 可以使用 session.on 方法订阅协议事件。

跟踪查看器 - Tracing

使用跟踪查看器(Tracing),生成的zip压缩包可以使用playwright show-trace xxx.zip进行GUI分析

开始跟踪 - tracing.start(screenshots=True, snapshots=True)
1
2
3
4
5
6
7
8
9
10
11
12
13
browser = p.chromium.launch()
context = browser.new_context()
# 根据上下文,开始跟踪
"""
screenshots bool (可选)
是否在跟踪期间捕获屏幕截图。屏幕截图用于构建时间线预览。

snapshots bool (可选)#
如果此选项为 true,则跟踪将
在每次操作时捕获 DOM 快照
记录网络活动
"""
context.tracing.start(screenshots=True, snapshots=True)
在 tracing.start中开辟多个跟踪块 - start_chunk&stop_chunk

开始新的跟踪块。如果您想在同一个 BrowserContext 上记录多个跟踪,请使用一次 tracing.start(),然后使用 tracing.start_chunk() 和 tracing.stop_chunk() 创建多个跟踪块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
context = 
context.tracing.start(screenshots=True, snapshots=True)
page = context.new_page()
page.goto("https://playwright.net.cn")

context.tracing.start_chunk()
page.get_by_text("Get Started").click()
# Everything between start_chunk and stop_chunk will be recorded in the trace.
context.tracing.stop_chunk(path = "trace1.zip")

context.tracing.start_chunk()
page.goto("http://example.com")
# Save a second trace file with different actions.
context.tracing.stop_chunk(path = "trace2.zip")
结束跟踪并生成文件 - tracing.stop(path=”trace.zip”)
1
context.tracing.stop(path="trace.zip")

视频 - page.video

删除视频文件 - page.video.delete()

删除视频文件。如有必要,将等待视频完成。

返回此视频将录制到的文件系统路径 - page.video.path()
将视频保存到用户指定的路径 - page.video.save_as(path)

错误处理 - WebError

WebError 类表示页面中抛出的未处理异常。它通过 context.on(“weberror”) 事件进行调度

1
context.on("weberror", lambda web_error: print(f"uncaught exception: {web_error.error}"))
抛出未处理错误 - web_error.error
1
context.on("weberror", lambda web_error: print(f"uncaught exception: {web_error.error}"))
产生此未处理异常的页面 - web_error.page
1
context.on("weberror", lambda web_error: print(f"uncaught exception: {web_error.page}"))

路由 - route

pass

tracing 解析

使用playwright 发送请求

playwright 也可以调用请求的方式,进行接口测试

1