Playwright for Python
简介
- Playwright 是由 Microsoft 开发的,最初版本发布于 2020 年初。它是在 Google 的 Puppeteer(另一种流行的浏览器自动化库)的基础上构建的,并且由相同的团队成员开发,这些成员后来加入了 Microsoft。Playwright 旨在解决跨浏览器测试的兼容性和一致性问题,提供一个统一的 API 来支持多个浏览器,这使得它很快成为自动化测试和Web开发者社区中受欢迎的工具。
- Playwright 是一个开源的自动化库和工具,用于Web测试和自动化。它允许开发人员通过使用相同的应用编程接口 (API) 在多个浏览器中(如Chromium, Firefox, 和 WebKit)编写脚本以模拟用户操作。Playwright 支持多种编程语言,包括 JavaScript、TypeScript、Python、C# 和 Java,这使得它可以集成到不同的开发环境中。
- 使用 Playwright,开发人员可以执行各种浏览器自动化任务,如页面导航、元素选择、文本输入、文件上传下载、执行JavaScript等,以及创建端到端的测试用例。此外,Playwright 能够处理现代Web应用程序中的高级用例,包括对单页应用程序 (SPA) 的支持、执行网络请求拦截和模拟、捕获浏览器控制台的日志,以及生成页面截图和PDF文件等。
- Playwright 支持当前所有主流浏览器,包括 Chrome 和 Edge(基于 Chromium)、Firefox、Opera、Safari(基于 WebKit) ,提供完善的自动化控制的 API。
- Playwright 可以在 Windows、Linux 和 macOS 上进行本地或 CI、无头或有头测试。
- Playwright 可以在 TypeScript、JavaScript、Python、.NET、Java 中使用 Playwright API 。
- Playwright 支持移动端页面测试,使用设备模拟技术可以使我们在移动 Web 浏览器中测试响应式 Web 应用程序。
- Playwright 提供了自动等待相关的 API,当页面加载的时候会自动等待对应的节点加载,大大简化了 API 编写复杂度。
- Playwright 为每个测试创建一个浏览器上下文。浏览器上下文相当于一个全新的浏览器配置文件。这提供了零开销的完整测试隔离。创建一个新的浏览器上下文只需要几毫秒。
- Playwright 可以录制我们在浏览器中的操作并将代码自动生成出来。
- Playwright 的安装和配置非常简单,安装过程中会自动安装对应的浏览器和驱动,不需要额外配置 WebDriver 等。
-
Pip
1
2
3pip install --upgrade pip
pip install playwright
playwright install -
Conda
1
2
3
4conda config --add channels conda-forge
conda config --add channels microsoft
conda install playwright
playwright install -
说明
- 要使用 Playwright,需要 Python 3.7 版本及以上,请确保 Python 的版本符合要求。
playwright install
是初始化操作,这时候 Playwrigth 会安装 Chromium、Firefox、WebKit 浏览器并配置一些驱动,我们不必关心中间配置的过程,Playwright 会为我们配置好。- 安装完成之后,我们便可以使用 Playwright 启动 Chromium 或 Firefox 或 WebKit 浏览器来进行自动化操作了。
-
说明
Playwright 支持两种编写模式,一种是类似 Pyppetter 一样的异步模式,另一种是像 Selenium 一样的同步模式,我们可以根据实际需要选择使用不同的模式。
-
同步
1
2
3
4
5
6
7
8from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.webkit.launch()
page = browser.new_page()
page.goto("http://whatsmyuseragent.org/")
page.screenshot(path="example.png")
browser.close()默认情况下,Playwright 以无头模式运行浏览器。
-
异步
1
2
3
4
5
6
7
8
9
10
11
12import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch(headless=False)
page = await browser.new_page()
await page.goto("http://playwright.dev")
print(await page.title())
await browser.close()
asyncio.run(main())要使用带界面的模式,请在启动浏览器时传递
headless=False
参数。 -
代码生成
-
说明
Playwright 的代码生成功能,可以录制我们在浏览器中的操作并将代码自动生成出来,有了这个功能,我们甚至都不用写任何一行代码,这个功能可以通过 playwright 命令行调用 codegen 来实现,我们先来看看 codegen 命令都有什么参数,输入如下命令:
1
playwright codegen --help
结果如下:
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
30Usage: playwright codegen [options] [url]
open page and generate code for user actions
Options:
-o, --output <file name> saves the generated script to a file
--target <language> language to generate, one of javascript, test, python, python-async, csharp (default: "python")
-b, --browser <browserType> browser to use, one of cr, chromium, ff, firefox, wk, webkit (default: "chromium")
--channel <channel> Chromium distribution channel, "chrome", "chrome-beta", "msedge-dev", etc
--color-scheme <scheme> emulate preferred color scheme, "light" or "dark"
--device <deviceName> emulate device, for example "iPhone 11"
--geolocation <coordinates> specify geolocation coordinates, for example "37.819722,-122.478611"
--ignore-https-errors ignore https errors
--load-storage <filename> load context storage state from the file, previously saved with --save-storage
--lang <language> specify language / locale, for example "en-GB"
--proxy-server <proxy> specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"
--proxy-bypass <bypass> comma-separated domains to bypass proxy, for example ".com,chromium.org,.domain.com"
--save-storage <filename> save context storage state at the end, for later use with --load-storage
--save-trace <filename> record a trace for the session and save it to a file
--timezone <time zone> time zone to emulate, for example "Europe/Rome"
--timeout <timeout> timeout for Playwright actions in milliseconds (default: "10000")
--user-agent <ua string> specify user agent string
--viewport-size <size> specify browser viewport size in pixels, for example "1280, 720"
-h, --help display help for command
Examples:
$ codegen
$ codegen --target=python
$ codegen -b webkit https://example.com -
参数
参数 说明 默认值 -o
将生成的脚本保存到一个文件中 --target
生成的代码语言 python -b
使用的浏览器 chromium --channel
Chromium 发行渠道 --color-scheme
模仿首选的配色方案,“浅色”或“深色” --device
模拟设备,例如:“iPhone 11” --geolocation
指定地理位置坐标 --ignore-https-errors
忽略 https 错误 --load-storage
从文件中加载上下文存储状态 --lang
指定语言/区域设置,例如: "en-GB"
--proxy-server
指定代理服务器 --proxy-bypass
逗号分隔的域可以绕过代理 --save-storage
在末尾保存上下文存储状态 --save-trace
记录会话的跟踪,并将其保存到文件中 --timezone
要模拟的时区 --timeout
Playwright 动作的超时时间(以毫秒为单位) 10000 --user-agent
指定用户代理字符串 --viewport-size
指定浏览器视窗大小(以像素为单位),如: "1280, 720"
-h, --help
显示命令帮助 -
示例
1
playwright codegen -o script.py -b firefox
- 这时候就弹出了一个 Firefox 浏览器,同时右侧会输出一个脚本窗口,实时显示当前操作对应的代码。
- 我们可以在浏览器中做任何操作,操作完毕之后,关闭浏览器,Playwright 会生成一个
script.py
文件。 - 运行该文件,就可以看到它又可以复现我们刚才所做的操作了。
-
-
移动端浏览器支持
- Playwright 另外一个特色功能就是可以支持移动端浏览器的模拟,比如模拟打开 iPhone 12 Pro Max 上的 Safari 浏览器,然后手动设置定位,并打开百度地图并截图。首先我们可以选定一个经纬度,比如故宫的经纬度是 39.913904, 116.39014,我们可以通过 geolocation 参数传递给 Webkit 浏览器并初始化。
- 示例代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16from playwright.sync_api import sync_playwright
with sync_playwright() as p:
iphone_12_pro_max = p.devices['iPhone 12 Pro Max']
browser = p.webkit.launch(headless=False)
context = browser.new_context(
**iphone_12_pro_max,
locale='zh-CN',
geolocation={'longitude': 116.39014, 'latitude': 39.913904},
permissions=['geolocation']
)
page = context.new_page()
page.goto('https://amap.com')
page.wait_for_load_state(state='networkidle')
page.screenshot(path='location-iphone.png')
browser.close()
-
文本选择器
- 同步
1
page.locator("text=Log in").click()
- 异步
1
await page.locator("text=Log in").click()
- 说明
- 默认匹配不区分大小写并搜索子字符串
- 同步
-
CSS 选择器
- 同步
1
page.locator("button").click()
- 异步
1
await page.locator("button").click()
- 说明
css
默认情况下,引擎会穿透打开的影子 DOM。- Playwright 添加了自定义伪类,例如
:visible
、:text
等等。
- 同步
-
XPath 选择器
- 同步
1
page.locator("xpath=//button").click()
- 异步
1
await page.locator("xpath=//button").click()
- 说明
- 以
//
或者..
开头的选择器被假定为 xpath 选择器。 - 需要在开头指定
xpath=
字符串,代表后面是一个 XPath 表达式。
- 以
- 同步
-
文字输入
- 说明
这是填写表单域的最简单方法。input
它聚焦元素并使用输入的文本触发事件。它适用于<input>
、<textarea>
、[contenteditable]
、<label>
等与输入或文本区域相关联的标签。 - 同步
1
2
3
4
5
6
7
8
9
10
11
12
13
14# Text input
page.fill('#name', 'Peter')
# Date input
page.fill('#date', '2020-02-02')
# Time input
page.fill('#time', '13:15')
# Local datetime input
page.fill('#local', '2020-03-02T05:15')
# Input through label
page.fill('text=First Name', 'Peter') - 异步
1
2
3
4
5
6
7
8
9
10
11
12
13
14# Text input
await page.fill('#name', 'Peter')
# Date input
await page.fill('#date', '2020-02-02')
# Time input
await page.fill('#time', '13:15')
# Local datetime input
await page.fill('#local', '2020-03-02T05:15')
# Input through label
await page.fill('text=First Name', 'Peter')
- 说明
-
复选框和单选按钮
- 说明
这是选中和取消选中复选框或单选按钮的最简单方法。此方法可与input[type=checkbox]
、input[type=radio]
、[role=checkbox]
或label
与复选框或单选按钮相关联的标签一起使用。 - 同步
1
2
3
4
5
6
7
8
9
10
11# Check the checkbox
page.check('#agree')
# Assert the checked state
assert page.is_checked('#agree') is True
# Uncheck by input <label>.
page.uncheck('#subscribe-label')
# Select the radio button
page.check('text=XL') - 异步
1
2
3
4
5
6
7
8
9
10
11# Check the checkbox
await page.check('#agree')
# Assert the checked state
assert await page.is_checked('#agree') is True
# Uncheck by input <label>.
await page.uncheck('#subscribe-label')
# Select the radio button
await page.check('text=XL')
- 说明
-
选择选项
- 说明
选择<select>
元素中的一个或多个选项。您可以指定选项value
,label
或elementHandle
选择。可以选择多个选项。 - 同步
1
2
3
4
5
6
7
8
9
10
11
12# Single selection matching the value
page.select_option('select#colors', 'blue')
# Single selection matching the label
page.select_option('select#colors', label='Blue')
# Multiple selected items
page.select_option('select#colors', ['red', 'green', 'blue'])
# Select the option via element handle
option = page.query_selector('#best-option')
page.select_option('select#colors', option) - 异步
1
2
3
4
5
6
7
8
9
10
11
12# Single selection matching the value
await page.select_option('select#colors', 'blue')
# Single selection matching the label
await page.select_option('select#colors', label='Blue')
# Multiple selected items
await page.select_option('select#colors', ['red', 'green', 'blue'])
# Select the option via element handle
option = await page.query_selector('#best-option')
await page.select_option('select#colors', option)
- 说明
-
鼠标点击
- 说明
执行简单的人工点击。 - 同步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# Generic click
page.click('button#submit')
# Double click
page.dblclick('#item')
# Right click
page.click('#item', button='right')
# Shift + click
page.click('#item', modifiers=['Shift'])
# Hover over element
page.hover('#item')
# Click the top left corner
page.click('#item', position={ 'x': 0, 'y': 0}) - 异步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# Generic click
await page.click('button#submit')
# Double click
await page.dblclick('#item')
# Right click
await page.click('#item', button='right')
# Shift + click
await page.click('#item', modifiers=['Shift'])
# Hover over element
await page.hover('#item')
# Click the top left corner
await page.click('#item', position={ 'x': 0, 'y': 0})
- 说明
-
输入字符
- 说明
一个字符一个字符地输入字段,就好像它是一个使用真正键盘的用户一样。 - 同步
1
2# Type character by character
page.type('#area', 'Hello World!') - 异步
1
2# Type character by character
await page.type('#area', 'Hello World!')
- 说明
-
键和快捷键
- 说明
此方法聚焦所选元素并产生单个击键。它接受在键盘事件的 keyboardEvent.key 中发出的逻辑键名。 - 同步
1
2
3
4
5
6
7
8# Hit Enter
page.press('#submit', 'Enter')
# Dispatch Control+Right
page.press('#name', 'Control+ArrowRight')
# Press $ sign on keyboard
page.press('#value', '$') - 异步
1
2
3
4
5
6
7
8# Hit Enter
await page.press('#submit', 'Enter')
# Dispatch Control+Right
await page.press('#name', 'Control+ArrowRight')
# Press $ sign on keyboard
await page.press('#value', '$')
- 说明
-
上传文件
- 说明
您可以使用 age.set_input_files(selector, files, **kwargs) 方法选择在该页面要上传的输入文件。它期望第一个参数是指向类型为"file"
的 输入元素 。可以在数组中传递多个文件。如果某些文件路径是相对的,则它们将相对于当前工作目录进行解析。空数组清除所选文件。 - 同步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16# Select one file
page.set_input_files('input#upload', 'myfile.pdf')
# Select multiple files
page.set_input_files('input#upload', ['file1.txt', 'file2.txt'])
# Remove all the selected files
page.set_input_files('input#upload', [])
# Upload buffer from memory
page.set_input_files(
"input#upload",
files=[
{"name": "test.txt", "mimeType": "text/plain", "buffer": b"this is a test"}
],
) - 异步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16# Select one file
await page.set_input_files('input#upload', 'myfile.pdf')
# Select multiple files
await page.set_input_files('input#upload', ['file1.txt', 'file2.txt'])
# Remove all the selected files
await page.set_input_files('input#upload', [])
# Upload buffer from memory
await page.set_input_files(
"input#upload",
files=[
{"name": "test.txt", "mimeType": "text/plain", "buffer": b"this is a test"}
],
)
- 说明
-
焦点元素
- 说明
对于处理焦点事件的动态页面,您可以聚焦给定元素。 - 同步
1
page.focus('input#name')
- 异步
1
await page.focus('input#name')
- 说明