定制软件开发playwright最详细使用教程

定制软件开发推荐阅读按照自己需要的顺序,定制软件开发不需要从头开始。


简单介绍

playwright定制软件开发是一款新型的自动化测试工具,功能非常强大,定制软件开发使用下来有很多的优点 👍 :

  • 支持异步。
  • 定制软件开发内置浏览器驱动。
  • 定制软件开发支持移动端。
  • 代码生成。
  • 定制软件开发安装和使用都非常简单。

缺点:

  • 定制软件开发使用的人比较少。
  • 教程少👉()
  • 定制软件开发没有中文文档。

安装

pip

pip install --upgrade pippip install playwright  # 安装playwrightplaywright install  # 安装驱动
  • 1
  • 2
  • 3

conda

conda config --add channels conda-forgeconda config --add channels microsoftconda install playwright  # 安装playwrightplaywright install  # 安装驱动
  • 1
  • 2
  • 3
  • 4

定制软件开发基本使用方法

同步模式

from playwright.sync_api import sync_playwrightwith sync_playwright() as p:    browser = p.chromium.launch(headless=False)    page = browser.new_page()    page.goto('http://www.baidu.com')    print(page.title)    browser.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

异步模式

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://www.baidu.com")        print(await page.title())        await browser.close()asyncio.run(main())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

方法简介

定制软件开发创建浏览器对象

定制软件开发可以选择不同的浏览器版本,msedge就是Microsoft Edge浏览器,定制软件开发同时还支持beta版本。定制软件开发在大部分情况下,定制软件开发使用默认的chrome定制软件开发浏览器足够了。

# 同步# Can be "msedge", "chrome-beta", "msedge-beta", "msedge-dev", etc.browser = playwright.chromium.launch(channel="chrome")
  • 1
  • 2
  • 3
# 异步# Can be "msedge", "chrome-beta", "msedge-beta", "msedge-dev", etc.browser = await playwright.chromium.launch(channel="chrome")
  • 1
  • 2
  • 3

定制软件开发浏览器上下文

定制软件开发浏览器上下文对象是浏定制软件开发览器实例中一个类似于定制软件开发隐身模式的会话,定制软件开发简单说就是该上下文资定制软件开发源是完全独立的,与其他的上下文互不干扰,所以在自动化测试中,可以对每一个测试用例都单独开一个浏览器上下文。这对于多用户,多场景的测试非常有用。

# 同步browser = playwright.chromium.launch()context = browser.new_context()page = context.new_page()# 异步browser = await playwright.chromium.launch()context = await browser.new_context()page = await context.new_page()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

食用栗子

# 同步模式from playwright.sync_api import sync_playwrightdef run(playwright):    # 创建一个浏览器实例    chromium = playwright.chromium    browser = chromium.launch()    # 创建两个浏览器上下文    user_context = browser.new_context()    admin_context = browser.new_context()    # 创建选项卡和上下文之间的交互with sync_playwright() as playwright:    run(playwright)# 异步模式import asynciofrom playwright.async_api import async_playwrightasync def run(playwright):    # 创建一个浏览器实例    chromium = playwright.chromium    browser = await chromium.launch()    # 创建两个浏览器上下文    user_context = await browser.new_context()    admin_context = await browser.new_context()    # 创建各种页面和上下文的交互async def main():    async with async_playwright() as playwright:        await run(playwright)asyncio.run(main())
  • 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

个人理解 😳


Page对象

一般来说,一个page对应一个浏览器选项卡。而Page对象的作用在于和页面的内容进行交互,以及导航和加载新的页面。

创建对象

page = context.new_page()
  • 1

管理多页面

page_one = context.new_page()page_two = context.new_page()all_pages = context.pages  # 获得该上下文的所有的page对象
  • 1
  • 2
  • 3
  • 4

打开新的选项卡

# 可以预期的with context.expect_page() as new_page_info:    page.locator('a[target="_blank"]').click() # 打开新的选项卡new_page = new_page_info.valuenew_page.wait_for_load_state()print(new_page.title())# 不可以预期的def handle_page(page):	page.wait_for_load_state()	print(page.title())context.on('page', handle_page)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

处理弹窗信息

# 可以预期的with page.expect_popup() as popup_info:    page.locator("#open").click()popup = popup_info.valuepopup.wait_for_load_state()print(popup.title())# 不可以预期的def handle_popup(popup):	popup.wait_for_load_state()	print(popup.title())page.on('popup', handle_popup)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

终端命令使用

1.安装浏览器
playwright install   # 安装playwright支持的默认的浏览器驱动playwright install webkit  # 安装webkitplaywright install --help  # 查看命令帮助
  • 1
  • 2
  • 3
2.安装系统依赖
playwright install-deps  # 安装playwright支持的浏览器的所有依赖playwright install-deps chromium  # 单独安装chromium驱动的依赖
  • 1
  • 2
3.代码生成 😃

测试网站:wikipedia.org

playwright codegen wikipedia.org
  • 1
4.保存认证状态(将会保存所有的cookie和本地存储到文件)
playwright codegen --save-storage=auth.json  # 存储到本地playwright open --load-storage=auth.json my.web.app  # 打开存储playwright codegen --load-storage=auth.json my.web.app # 使用存储运行生成代码(保持认证状态)
  • 1
  • 2
  • 3
5.打开网页,playwright内置了一个跨平台的浏览器webkit内核。
playwright open www.baidu.com  # 使用默认的chromium打开百度playwright wk www.baidu.com  # 使用webkit打开百度
  • 1
  • 2
6.模拟设备打开(挺有趣的😲)
# iPhone 11.playwright open --device="iPhone 11" www.baidu.com# 屏幕大小和颜色主题playwright open --viewport-size=800,600 --color-scheme=dark www.baidu.com# 模拟地理位置、语言和时区playwright open --timezone="Europe/Rome" --geolocation="41.890221,12.492348" --lang="it-IT" maps.google.com
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
7.Inspect selectors,在浏览器调试工具使用命令。下面是一些例子。
playwright.$(selector)  # 定位到匹配的第一个元素playwright.$$(selector)  # 定位到所有匹配的元素playwright.selector(element)  # 给指定的元素生成selectorplaywright.inspect(selector)  # 在 Elements 面板中显示元素(如果相应浏览器的 DevTools 支持它playwright.locator(selector)  # 使用playwright内置的查询引擎来查询匹配的节点playwright.highlight(selector)  # 高亮显示第一个匹配的元素playwright.clear()  #取消现存的所有的高亮
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

8.截图
playwright screenshot --help  # 查看帮助# 模拟iPhone截图,等待3秒后截图,保存为twitter-iphone.pngplaywright screenshot \    --device="iPhone 11" \    --color-scheme=dark \    --wait-for-timeout=3000 \    twitter.com twitter-iphone.png# 全屏截图playwright screenshot --full-page www.baidu.com baidu-full.png
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

使用代码截图

# 全屏截图page.screenshot(path="screenshot.png", full_page=True)# 对元素截图page.locator(".header").screenshot(path="screenshot.png")
  • 1
  • 2
  • 3
  • 4
  • 5
9.生成PDF(只有在无头模式下才能运行)
# 生成PDFplaywright pdf https://en.wikipedia.org/wiki/PDF wiki.pdf
  • 1
  • 2

调试

1.无头和有头模式(默认为无头模式)

headless是否无头,slow_mo放慢执行速度,从而可以容易跟踪操作。

chromium.launch(headless=False, slow_mo=100) # or firefox, webkit  同步await chromium.launch(headless=False, slow_mo=100) # or firefox, webkit  异步
  • 1
  • 2
2.打开开发工具
chromium.launch(devtools=True)  # 同步await chromium.launch(devtools=True)  # 异步
  • 1
  • 2

下载文件

同步模式

# 开始下载with page.expect_download() as download_info:    # 点击下载的按钮    page.locator("button#delayed-download").click()download = download_info.value# 等待下载print(download.path())# 保存文件download.save_as("/path/to/save/download/at.txt")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

异步模式

# 开始下载async with page.expect_download() as download_info:	await page.locator("button#delayed-download").click()download = await download_info.value# 等待下载print(await download.path())# 保存文件download.save_as("/path/to/save/download/at.txt")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

如果不知道会存在什么下载事件,那么就可以添加一个事件监听器:

page.on("download", lambda download: print(download.path()))  # 同步# 异步async def handle_download(download):	print(await download.path())page.on("download", handle_download) 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Handles(句柄)

Playwright 可以为页面 DOM 元素或页面内的任何其他对象创建句柄。 这些句柄存在于 Playwright 进程中,而实际对象存在于浏览器中。 有两种类型的手柄。

  • JSHandle:该浏览页中,任意的javascript对象的引用。
  • ElementHandle: 该浏览页中,任意DOM元素的引用。

实际上所有的DOM元素也是javascript对象,所有ElementHandle也是JSHandle。

# 获取JSHandlejs_handle = page.evaluate_handle('window')# 获取ElementHandleelement_handle = page.wait_for_selector('#box')# 可以使用assert方法来检测元素的属性bounding_box = element_handle.bounding_boxassert bounding_box.width == 100
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

定位器(locator)

playwright的核心功能之一,简单来说,就是一种可以随时在页面上查找元素的方法。

创建定位器: 使用 page.locator(selector, **kwargs)

locator = page.locator("text=Submit")locator.click() #点击操作
  • 1
  • 2

注意:定位器的一个操作严格匹配唯一的元素,否则将引发异常。 👇

page.locator('button').click()  # 如果有多个按钮将发生异常# 正确的做法,应该指定位置或者使用定位器的过滤器locator.first, locator.last, locator.nth(index)
  • 1
  • 2
  • 3
  • 4

定位器过滤器

# 包含sign up文本的按钮page.locator("button", has_text = "sign up")  # 包含另外一个locator的过滤器,内部的locator是从外部的locator开始的page.locator('article', has=page.locator('button.subscribe'))# 使用locator.filter(**kwargs)方法row_locator = page.locator('tr')row_locator	.filter(has_text="text in column 1")	.filter(has=page.locator('tr', has_text='column 2 button'))	.screenshot()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

locator和elementHandle的区别

  • locator保存的是元素捕获的逻辑。
  • elementHandle指向特定的元素。

举个例子,如果一个elementHandle所指向的元素的文本或者属性发生改变,那么elementHandle仍然指向原来那个没有改变的元素,而locator每一个都会根据捕获的逻辑去获取最新的那个元素,也就是改变后的元素。


文本输入

简单使用 fill 函数就好了。

# 文本框输入page.locator('#name').fill('Peter')# 日期输入page.locator('#date').fill('2020-02-02')# 时间输入page.locator('#time').fill('13:15')# 本地时间输入page.locator('#local').fill('2020-03-02T05:15')# 标签定位输入page.locator('text=First Name').fill('Peter')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

模拟

1.模拟设备

Playwright 带有用于选定移动设备的设备参数注册表,可用于模拟移动设备上的浏览器行为。

同步模式

from playwright.sync_api import sync_playwrightdef run(playwright):    pixel_2 = playwright.devices['Pixel 2']  # Pixel 2 是谷歌的一款安卓手机    browser = playwright.webkit.launch(headless=False)    # 使用设备Pixel 2的参数注册表来构建上下文    context = browser.new_context(        **pixel_2,    )with sync_playwright() as playwright:    run(playwright)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

异步模式

import asynciofrom playwright.async_api import async_playwrightasync def run(playwright):    pixel_2 = playwright.devices['Pixel 2']    browser = await playwright.webkit.launch(headless=False)    context = await browser.new_context(        **pixel_2,    )async def main():    async with async_playwright() as playwright:        await run(playwright)asyncio.run(main())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
2.模拟UA
# 同步context = browser.new_context(  user_agent='My user agent')# 异步context = await browser.new_context(  user_agent='My user agent')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
3.调整窗口大小
# 创建上下文context = browser.new_context(  viewport={ 'width': 1280, 'height': 1024 })# 调整page的视图大小await page.set_viewport_size({"width": 1600, "height": 1200})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
4.调整地理时间,颜色主题
context = browser.new_context(  locale='de-DE',  timezone_id='Europe/Berlin',  color_scheme='dark')
  • 1
  • 2
  • 3
  • 4
  • 5

执行JavaScript 👏

page.evaluate(expression, **kwargs)# expression: javascript脚本# kwargs: 参数(可序列化对象,JSHandle实例,ElementHandle实例)
  • 1
  • 2
  • 3
  • 4

不带参数

href = page.evaluate('() => document.location.href')  # 同步href = await page.evaluate('() => document.location.href')  # 异步
  • 1
  • 2

带参数

# 一个值page.evaluate('num => num', 32)# 一个数组page.evaluate('array => array.length', [1,2,3])# 一个对象page.evaluate('object => object.foo', {'foo': 'bar'})# jsHandlerbutton = page.evaluate('window.button')page.evaluate('button => button.textContent', button)# 使用elementhandler.evaluate的方法替代上面的写法button.evaluate('(button, from) => button.textContent.substring(from)', 5)# 对象与多个jshandler结合button1 = page.evaluate('window.button1')button2 = page.evaluate('.button2')page.evaluate("""o => o.button1.textContent + o.button2.textContent""",    { 'button1': button1, 'button2': button2 })# 使用析构的对象,注意属性和变量名字必须匹配。page.evaluate("""    ({ button1, button2 }) => button1.textContent + button2.textContent""",    { 'button1': button1, 'button2': button2 })# 数组析构也可以使用,注意使用的括号page.evaluate("""    ([b1, b2]) => b1.textContent + b2.textContent""",    [button1, button2])# 可序列化对象的混合使用page.evaluate("""    x => x.button1.textContent + x.list[0].textContent + String(x.foo)""",    { 'button1': button1, 'list': [button2], 'foo': None })
  • 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

事件

playwright允许监听各种类型的事件,一般来说大部分时间都在等待。

可以预期的事件

# 监听请求with page.expect_request('**/*login*.png') as first:	page.goto('http://www.baidu.com')print(first.value.url)# 异步模式async with page.expect_request('**/*login*.png') as first:	await page.goto('http://www.baidu.com')first_request = await first.valueprint(first_request.url)# 监听弹窗with page.expect_popup() as popup:	page.evaluate('window.open()')popup.value.goto('http://www.baidu.com')# 异步模式async with page.expect_popup() as popup:	await page.evaluate('window.open()')child_page = await popup.valueawait child_page.goto('http://www.baidu.com')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

但是大部分情况下时间的发生不可预期,所以需要添加事件监听处理(和异常捕获一样)

def print_request_sent(request):	print("request sent: " + request.url)def print_request_finished(request):	print("request finished:" + request.url)page.on("request", print_request_sent)  # 添加事件监听page.on("requestfinished", print_request_finished)page.goto('http://www.baidu.com')page.remove_listener("request", print_request_sent) # 移除事件监听page.goto('http://wikipedia.org')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

一次性监听器

page.once('dialog', lambda dialog: dialog.accept('2022'))page.evaluate('prompt("Enter a number: ")')
  • 1
  • 2

扩展

默认情况下,引擎直接在框架的 JavaScript 上下文中运行,例如,可以调用应用程序定义的函数。 要将引擎与框架中的任何 JavaScript 隔离,但保留对 DOM 的访问权限,请使用 {contentScript: true} 选项注册引擎。 内容脚本引擎更安全,因为它可以防止任何对全局对象的篡改,例如更改 Node.prototype 方法。所有内置的选择器引擎都作为内容脚本运行。 请注意,当引擎与其他自定义引擎一起使用时,不能保证作为内容脚本运行。

注册选择器引擎的示例,该引擎根据标签名称查询元素:

tag_selector = """    {      // 找到第一个匹配的元素      query(root, selector) {        return root.querySelector(selector);      },      // 找到所有的匹配元素      queryAll(root, selector) {        return Array.from(root.querySelectorAll(selector));      }    }"""# 注册引擎playwright.selectors.register("tag", tag_selector)# 使用自定义的selectorbutton = page.locator("tag=button")button.click()# 与 “>>” 结合使用page.locator("tag=div >> span >> \"click me\"").click()# 在所有支持selector的情况下使用button_count = page.locator("tag=button").count()
  • 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

框架

通常来说很多网页有多个框架,html表现为使用<iframe>标签。如果要定位到子框架里面的元素则必须先要指定frame的selector。 😑 😑 😑

# 填充子框架里面的用户名# 同步模式username = page.from_locator('.frame-class').locator('#username-input')username.fill('Mike')# 异步模式username = await page.from_locator('.frame-class').locator('#username-input')await username.fill('Mike')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

或者可以直接使用frame对象来完成交互。

# 从名字属性获取frame对象frame = page.frame('frame-login')# 从url获取frame对象frame = page.frame(url=r'.*domain.*')# 使用frame对象await frame.fill('#username-input', 'John')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

playwright导航周期

Playwright 将在页面中显示新文档的过程分为导航和加载。导航从更改页面 URL 或与页面交互(例如,单击链接)开始。 导航意图可能会被取消,例如,在点击未解析的 DNS 地址或转换为文件下载时。当响应头被解析并且会话历史被更新时,导航被提交。 只有在导航成功(提交)后,页面才开始加载文档。加载包括通过网络获取剩余的响应体、解析、执行脚本和触发加载事件:

  1. page.url设置新的url
  2. 文档的内容通过网络传输加载进入本地,并且被解析为头部,响应体等。
  3. page.on(“domcontentloaded”) 事件被触发。
  4. page对象开始执行一些脚本,从本地资源加载静态的资源(样式表,图片等等)
  5. page.on(“load”) 事件被触发。
  6. page对象执行动态加载的脚本。
  7. 当 500 毫秒内没有新的网络请求时触发 networkidle
等待

默认情况下,playwright会自动等待直到触发load事件。

# 同步模式page.goto("http://www.baidu.com")# 异步模式await page.goto("http://www.baidu.com")
  • 1
  • 2
  • 3
  • 4

可以重载等待的事件,比如等待直到触发networkidle

page.goto("http://www.baidu.com", wait_until="networkidle")
  • 1

也可以为特定的元素添加等待

page.goto("http://www.baidu.com")page.locator("title").wait_for()   # 等待直到title元素被加载完全# 会自动等待按钮加载好再执行点击page.locator("button", has_text="sign up").click() 
  • 1
  • 2
  • 3
  • 4
  • 5

一些有用的API(用来模拟导航栏的功能 😃):

page.goto(url, **kwargs) # 定向page.reload(**kwargs) # 刷新page.go_back(**kwargs)  # 后退page.go_forward(**kwargs)  # 前进
  • 1
  • 2
  • 3
  • 4

网络

1.HTTP认证
context = browser.new_context(	http_credenttials={		"username": "Mike",		"password": "123456"	})page = context.new_page()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
2.HTTP代理

全局代理设置

# 指定代理服务器,和代理所使用的用户和密码browser = chromium.launch(	"server": "http://myproxy.com:port",	"username": "Mike",	"password": "123456")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

局部代理,为每一个上下文都创建一个代理

browser = chromium.launch(proxy={"server": "per-context"})context = browser.new_context(proxy={"server": "http://myproxy.com:port"})
  • 1
  • 2
3.监听所有的请求和响应
from playwright.sync_api import sync_playwrightdef run(playwright):    chromium = playwright.chromium    browser = chromium.launch()    page = browser.new_page()    # 监听请求和响应事件    page.on("request", lambda request: print(">>", request.method, request.url))    page.on("response", lambda response: print("<<", response.status, response.url))    page.goto("https://example.com")    browser.close()with sync_playwright() as playwright:    run(playwright)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

选择器(selector)

1.文本选择器

基本使用

page.locator('text=Log in').click()  # 匹配(包含Log in)page.locator("text='Log in'").click()  # 匹配(等于Log in)
  • 1
  • 2

正则表达式(支持JavaScript风格的正则表达式,👉 )

page.locator("text=/Log\s*in/i").click()# 下面的都可以匹配'''<button>Login</button><button>Log IN</button>'''
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

has-text()伪类选择器和text伪类选择器

  • has-text():检测包含(返回找到的所有元素)
  • text():检测等于(返回第一个找到的元素)
  • 使用在一个特定的css选择器里面。
# Wrong, will match many elements including <body>page.locator(':has-text("Playwright")').click()# Correct, only matches the <article> elementpage.locator('article:has-text("All products")').click()# Find the minimum element contain the text of Homepage.locator("#nav-bar :text('Home')").click()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
2.CSS选择器
page.locator('button').click()page.locator('#nav-bar .contact-us-item').click()page.locator('[data-test=login-button]').click()page.locator("[aria-label='Sign in']").click()
  • 1
  • 2
  • 3
  • 4
3.CSS+Text
page.locator("article:has-text('Playwright')").click()page.locator("#nav-bar :text('Contact us')").click()
  • 1
  • 2
4.CSS+CSS
page.locator(".item-description:has(.item-promo-banner)").click()
  • 1
5.xpath选择器
page.locator("xpath=//button").click()
  • 1
6.nth选择
# Click first buttonpage.locator("button >> nth=0").click()# Click last buttonpage.locator("button >> nth=-1").click()
  • 1
  • 2
  • 3
  • 4
  • 5
最佳实践
  • 优先考虑面向用户的属性(因为这些元素很少变化,维护方便)

    • 文本输入
    • 用户名和密码输入
    • 用户角色标签
    • etc
  • 避免使用很长的xpath或者css选择路径,会导致选择器过分依赖于DOM结构。

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发