PlayWright 工具(一)

🍰 2020年微软开源的UI自动化测试工具, 支持Node.js、Python、Java等,满足端到端测试创建,支持现代渲染引擎。

1 浏览器操作

1
2
3
pip install playwright                                           # 安装Playwright,Python版本要求3.7+
playwright install # 安装浏览器chromium、webkit和firefox
playwright install chromium # 安装指定的chromium浏览器

1-1 API

  • API
    • Playwright支持两种运行方式:同步和异步。
      • 同步运行指的是程序按照顺序执行,每个任务必须等待上一个任务完成后,才能进行下一个任务。
      • 异步运行指的是程序可以同时执行多个任务,不需要等待上一个任务完成,就能进行下一个任务。
    • 官网地址:https://playwright.dev/python/

(1) 同步运行

1
2
3
4
5
6
7
8
9
10
11
from playwright.sync_api import sync_playwright                 # sync同步(顺序)运行

with sync_playwright() as p:
browser = p.chromium.launch(headless=False) # 启动chromium浏览器
# browser = p.firefox.launch(headless=False) # 启动firefox浏览器
# browser = p.webkit.launch(headless=False) # 启动webkit浏览器
page = browser.new_page() # 打开一个标签页
page.goto("https://www.baidu.com") # 打开百度地址
print(page.title()) # 打印当前页面title

browser.close() # 关闭浏览器对象

(2) 异步运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import asyncio
from playwright.async_api import async_playwright # async异步(同时)运行


async def main():
async with async_playwright() as p:
browser = await p.chromium.launch(headless=False) # 启动chromium浏览器
# browser = await p.firefox.launch(headless=False) # 启动firefox浏览器
# browser = await p.webkit.launch(headless=False) # 启动webkit浏览器
page = await browser.new_page() # 打开一个标签页
await page.goto("https://www.baidu.com") # 打开百度地址
print(await page.title()) # 打印当前页面title

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

asyncio.run(main())

1-2 上下文

  • 上下文
    • Playwright的测试层级关系:Broswer > Context > Page
    • browser = playwright.chromium.launch():启动浏览器。
    • context = browser.new_context():创建context对象,即启动上下文。
      • context之间相互隔离,即为轻量级的浏览器实例,不与其他浏览器上下文共享cookie或缓存。
      • 如果需要不同用户登录同一个网页,不用创建多个浏览器实例,只需要创建多个context即可。
    • context.add_cookies([cookie_object1, cookie_object2]):添加cookie。
    • 配置选项:无痕模式启动浏览器适合自动化测试、非无痕模式启动适合爬数据。
    • launch_persistent_context参数
      • user_data_dir:用户数据目录,此参数必须,可自定义目录,不推荐写Chrome安装目录。
        • 网站是否可以保持登录,主要看cookies有效期,有些网站关闭浏览器登录状态就失效。
        • 例如银行网站,有些博客站,登录后cookies一直生效,即利用缓存的cookies保持登录。
      • channel:指定浏览器的类型,默认chromium,支持chromium、chrome、msedge。
      • 若同时打开多个浏览器,每个浏览器登录不同的账号进行操作,则要用到多线程。
    • 测试在浏览器上下文的隔离全新环境中执行,隔离模型提高了可重复性,并防止级联测试失败。
      • Playwright使用BrowserContext实现测试隔离,相当于隐身式配置文件。
      • 测试隔离有两种不同的策略,从头开始清理,以及在两者之间进行清理。
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
from datetime import datetime, timedelta
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False) # 启动浏览器
# context = browser.new_context() # 启动上下文
context = browser.new_context(base_url="https://www.baidu.com")
current_time = datetime.now() # 获取当前日期时间
expires_time = current_time + timedelta(days=7) # 计算过期时间
expires_time = int(expires_time.timestamp()) # 转换为时间戳
context.add_cookies([ # 添加cookie
{
"name": "BIDUPSID", # 参数为字典列表,url可选
"value": "BFBAD64EE3482122786F502E5D073773",
"path": "/",
"domain": ".baidu.com",
"expires": expires_time, # 这里使用的是时间戳,可选
"httpOnly": False, # 是否为httpOnly,可选
"secure": False, # 安全模式,可选
"sameSite": "Lax" # Strict|Lax|None,同站策略,可选
},
{
"name": "session_token",
"value": "BFBAD64EE3482122786F502E5D073774",
"path": "/",
"domain": "baidu.com",
"expires": expires_time,
"httpOnly": False,
"secure": False,
"sameSite": "Lax"
}
])
page = context.new_page() # context上新建一个页面对象
# page.goto("https://www.baidu.com") # 打开百度地址
page.goto("/") # base_url参数在新建上下文时使用
print(page.title()) # 打印当前页面title

cookies = context.cookies() # 检查是否成功添加cookie
print(cookies)

browser.close() # 关闭浏览器对象

(1) 添加权限

1
2
3
4
5
6
7
8
9
10
11
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False) # 启动浏览器
context = browser.new_context() # 启动上下文

context.grant_permissions(["camera", "microphone"]) # 设置允许开启权限
page = context.new_page() # 打开标签页
page.goto("https://www.baidu.com/")

browser.close() # 关闭浏览器对象

(2) 无痕模式

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

with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
context = browser.new_context() # launch默认无痕模式启动
page = context.new_page() # 打开标签页

page.goto("https://www.baidu.com")
page.wait_for_timeout(3000)

context.close() # 关闭上下文
browser.close() # 关闭浏览器对象

(3) 非无痕模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from playwright.sync_api import sync_playwright

p = sync_playwright().start()
browser = p.chromium.launch_persistent_context( # 创建上下文对象
user_data_dir=f".\cache", # 自定义用户缓存地址
accept_downloads=True, # 接收下载事件
headless=False, # 设置无头模式
bypass_csp=True,
slow_mo=1000,
channel="chromium", # 指定浏览器类型,默认chromium
args=["--start-maximized"], # args结合no_viewport窗口最大化
no_viewport=True, # 全屏覆盖
# viewport={"width": 1920, "height": 1080}, # 设置屏幕分辨率,全屏移位
)
# page = browser.new_page() # 该方法会打开一个空白的tab页
page = browser.pages[0] # 默认打开tab标签页对象

page.goto("https://www.baidu.com")
page.wait_for_timeout(3000)

browser.close() # 关闭浏览器对象

(4) 同浏不同标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=1000) # 启动浏览器
context = browser.new_context() # 启动上下文

page1 = context.new_page() # 打开标签页1
page1.goto("https://www.baidu.com/")
page1.fill("#kw", "Playwright")
page1.wait_for_timeout(3000)

page2 = context.new_page() # 打开标签页2
page2.goto("https://hao.360.com/")
page2.fill("#search-kw", "English")
page2.wait_for_timeout(3000)

browser.close() # 关闭浏览器对象

(5) 不同浏同标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=1000) # 启动浏览器
context1 = browser.new_context() # 创建上下文1,浏览器实例1
context2 = browser.new_context() # 创建上下文2,浏览器实例2
page1 = context1.new_page() # 打开标签页1
page1.goto("https://www.baidu.com/")

page2 = context2.new_page() # 打开标签页1
page2.goto("https://www.baidu.com/")

context1.close()
context2.close() # 关闭上下文时,所属页面也会一起关闭
browser.close() # 先关闭上下文,再退出浏览器

1-3 无头模式

  • 无头模式
    • 默认情况下,Playwright是以headless无头模式运行浏览器的。
    • headless=False在启动浏览器时传递标志,可查看浏览器UI。
1
2
3
4
5
6
7
8
9
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(slow_mo=1000) # 无头模式启动chromium浏览器
page = browser.new_page() # 打开一个标签页
page.goto("https://www.baidu.com") # 打开百度地址
print(page.title()) # 打印当前页面title

browser.close() # 关闭浏览器对象

1-4 截图使用

  • 截图使用
    • 除截取当前屏幕外,还支持截取长图,也可对某个元素截图。
    • 快速截图方法:page.screenshot(path="screenshot.png")
    • 截取单元素方法:page.locator().screenshot(path="screenshot.png")
    • 捕获图片数据流:print(base64.b64encode(page.screenshot()).decode())
    • 截取长图方法:page.screenshot(path="screenshot.png", full_page=True)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import base64
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False) # 启动chromium浏览器
page = browser.new_page() # 打开一个标签页
page.goto("https://www.baidu.com") # 打开百度地址
print(page.title()) # 打印当前页面title

page.screenshot(path="pictures/baidu_com.png") # 快速截图
screenshot_bytes = page.locator("#s_lg_img").screenshot(path="pictures/single_element.png")
print(base64.b64encode(screenshot_bytes).decode()) # 捕获单元素图片数据流

page.goto("https://news.baidu.com/") # 打开百度新闻
print(page.title()) # 打印当前页面title
page.screenshot(path="pictures/baidu_news.png", full_page=True)

browser.close() # 关闭浏览器对象

1-5 视频录制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=100)
context = browser.new_context(
record_video_dir="videos/",
record_video_size={"width": 640, "height": 480} # 指定视频大小,默认800x800
)
page = context.new_page() # 打开标签页
page.goto("https://www.baidu.com/")
page.wait_for_timeout(1000)

path = page.video.path()
print(path) # 获取保存视频路径

context.close() # 确保调用close,视频才会保存
browser.close() # 关闭浏览器对象

1-6 弹出窗口

  • 弹出窗口
    • 页面中,通过链接打开一个弹出窗口,可以通过监听页面上的事件target="_blank"来获取引用。
    • 处理弹出窗口时,可以选择监听此特定事件,以便更精确地捕获与当前页面关联的弹出窗口信息。
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
from playwright.sync_api import sync_playwright


def handle_popup(popup): # 触发弹出窗口的操作未知时使用
popup.wait_for_load_state()
print(popup.title())


with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=1000) # 启动chromium浏览器
page = browser.new_page() # 打开一个标签页
page.goto("https://www.baidu.com/")
print(page.title())

# with page.expect_popup() as popup_info:
# page.locator("#s-top-left > a:nth-child(1)").click()
# popup = popup_info.value
# popup.wait_for_load_state() # 等待弹出窗口加载完毕
# page.on("popup", handle_popup)
# print(popup.title())

page.locator("#s-top-left > a:nth-child(1)").click()
page.wait_for_timeout(1000) # 操作未知,最好加一个等待

browser.close() # 关闭浏览器对象

1-7 切换标签页

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=1000) # 启动浏览器
context = browser.new_context() # 启动上下文
page = context.new_page() # 打开标签页
page.goto("https://www.baidu.com/")
print(page.title())

with context.expect_page() as new_page_info:
page.click("text=新闻")
new_page = new_page_info.value
new_page.wait_for_load_state() # 等待页面加载到指定状态
print(new_page.title())

browser.close() # 关闭浏览器对象

(1) 遍历page对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=1000) # 启动浏览器
context = browser.new_context() # 启动上下文
page = context.new_page() # 打开标签页
page.goto("https://www.baidu.com/")

for link in page.locator("#s-top-left>a").all(): # 点开多个标签页
link.click()
for i in context.pages: # 遍历page对象
print(i.title())

browser.close() # 关闭浏览器对象

(2) title和url判断

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
from playwright.sync_api import sync_playwright


def switch_to_page(context, title=None, url=None):
for item_page in context.pages:
if title:
if title in item_page.title():
item_page.bring_to_front() # 激活当前选项卡
# print(item_page.title()) # 打印页面标题
return item_page
elif url:
if url in item_page.url:
item_page.bring_to_front() # 激活当前选项卡
return item_page
else:
print("找不到标题或URL!")
return context.pages[0]


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/")

for link in page.locator("#s-top-left>a").all(): # 点开多个标签页
link.click()
page.wait_for_timeout(1000)
page_switch = switch_to_page(context, title="hao") # 切换到包含hao的标签页
print(page_switch.title())

for item_page in context.pages: # 切换并打印所有标签页的标题
switch_to_page(context, title=item_page.title())
print(item_page.title())

browser.close() # 关闭浏览器对象

1-8 浏览器窗口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
context = browser.new_context(
viewport={"width": 1200, "height": 600}, # viewport参数指定窗口大小
)
page = context.new_page()
page.goto("https://www.baidu.com/")
page.fill("#kw", "Playwright教程")
page.click("#su")
page.wait_for_timeout(3000)

page.go_back() # 后退 <—
page.wait_for_timeout(3000)
page.reload() # 刷新
page.wait_for_timeout(3000)
page.go_forward() # 前进 —>
page.wait_for_timeout(3000)

browser.close() # 关闭浏览器对象
  • 窗口最大化
    • 启动浏览器时,通过参数args=['--start-maximized']开启最大化。
    • 并设置no_viewport参数为True,否则默认按800*600像素创建窗口。
    • 若不是通过context上下文创建的page对象,可以直接将viewport参数写到new_page()里。
1
2
3
4
5
6
7
8
9
10
11
12
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(
headless=False,
args=["--start-maximized"] # 启动浏览器时,开启最大化
)
context = browser.new_context(no_viewport=True) # no_viewport禁止限制窗口大小
page = context.new_page()
page.goto("https://www.baidu.com/")

page.pause() # 调试方法,在当前页面暂停执行,等待操作

1-9 特定设备打开

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


with sync_playwright() as p:
iphone_13 = p.devices["iPhone 13"] # 以iPhone 13形式打开
browser = p.chromium.launch(headless=False)
context = browser.new_context(**iphone_13)
page = context.new_page()
page.goto("https://www.baidu.com/")
page.wait_for_timeout(3000)

browser.close() # 关闭浏览器对象

2 页面元素定位

  • 页面元素定位
    • 选择器(Selector):用于创建定位器的字符串。
      • CSS和XPath
        • 通过CSS选择器、XPath选择器,依据元素属性、标签名、文本内容等特征来定位元素。
        • 操作元素可使用定位器(Locator)先定位再操作,也可直接定位方法后再传选择器(推荐)。
      • 文本内容:指定元素所包含的文本内容,可以找到对应的元素。
      • 属性:指定元素的某个属性及其对应的值,可以唯一地定位到元素。
      • 坐标:通过指定元素在页面中的x和y坐标,可以精确地定位到元素。
      • 元素层级关系:通过元素的父元素、子元素、相邻元素等关系来确定元素的位置。
    • 定位器(Locator):Playwright自动等待和重试能力的核心部分。
    • 输入文字:fill()是填写表单字段的最简方法、type()键入字符。
    • 鼠标点击click()、拖放drag_to()、下拉框选择select_option()。
    • iframe定位、单选与复选set_checked()、聚焦给定元素focus()。

2-1 选择器

  • 选择器
    • CSS和XPath
      • 浏览器F12开发者工具,Elements中鼠标选中元素右键可以Copy选择器或XPath内容。
      • page.fill('css=#kw', 'Playwright')page.click('xpath=//*[@id="su"]')
      • page.fill('#kw', 'Playwright')page.click('//*[@id="su"]')(前缀可不写)。
      • 长CSS或XPath链容易不稳定,选择器绑定到DOM,结构发生变化时选择器可能会中断。
    • 文本内容
      • Playwright封装了两种text文本定位的方式。
        • page.click("text=xxx"):无引号,模糊匹配,对大小写不敏感。
        • page.click("text='xxx'"):有引号,精确匹配,对大小写敏感。
      • 除了支持定位a标签,还可以定位button按钮、input标签的button按钮。

(1) CSS和XPath

1
2
3
4
5
6
7
8
9
10
11
12
13
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=1000) # 启动浏览器
page = browser.new_page()
page.goto("https://www.baidu.com") # 打开百度地址
print(page.title()) # 打印当前页面title
page.wait_for_timeout(3000)

page.fill("//*[@id='kw']", "Playwright") # XPath
page.click("#su") # CSS

browser.close() # 关闭浏览器对象

(2) 文本定位a标签

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

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=1000) # 启动浏览器
page = browser.new_page()
page.goto("https://www.baidu.com") # 打开百度地址
print(page.title()) # 打印当前页面title

page.click("text=新闻")
page.wait_for_timeout(3000) # 等待3秒

browser.close() # 关闭浏览器对象

(3) 文本定位button

1
2
3
4
5
6
7
8
9
10
11
12
13
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=1000) # 启动浏览器
page = browser.new_page()
page.goto("https://www.baidu.com") # 打开百度地址
print(page.title()) # 打印当前页面title

page.fill("#kw", "Playwright")
page.click("text=百度一下")
page.wait_for_timeout(3000) # 等待3秒

browser.close() # 关闭浏览器对象

(4) 获取内容与文本

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
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=100)
context = browser.new_context() # 创建上下文
page = context.new_page() # 打开标签页
page.goto("https://www.baidu.com/")

print(page.content()) # 获取整个页面的html内容
page.wait_for_load_state("networkidle")
print("-----------------分割线-----------------")

a = page.locator("#s-top-left>a").first
print(a.get_attribute("href")) # 获取元素属性
print("-----------------分割线-----------------")

b = page.locator("//*[@id='s-top-left']")
print(b.inner_html()) # 获取元素下的html内容
print("-----------------分割线-----------------")
print(b.inner_text()) # 获取元素的文本内容
print("-----------------分割线-----------------")
print(b.all_inner_texts()) # 列表显示获取到的页面内容
print("-----------------分割线-----------------")
print(b.all_text_contents()) # 列表显示获取到的元素下所有内容

browser.close() # 关闭浏览器对象

(5) 获取输入框的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=100)
context = browser.new_context() # 创建上下文
page = context.new_page() # 打开标签页
page.goto("https://www.baidu.com/")
page.wait_for_load_state("networkidle")

input1 = page.locator("#kw")
input1.fill("Playwright")
print(input1.input_value()) # 获取输入框的值

browser.close() # 关闭浏览器对象

(6) 选择器组合定位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=1000) # 启动浏览器
page = browser.new_page()
page.goto("https://passport.baidu.com/v2/?login") # 打开百度登录页面
print(page.title()) # 打印当前页面title

page.wait_for_selector("text=用户名登录").click()
page.fill("form >> [name='userName']", "Test") # 不同的选择器组合使用,用>>连接
# page.locator("form").locator("[name='userName']").fill("Test")

page.fill("form >> [name='password']", "A.123456")
# page.locator("form").locator("[name='password']").fill("A.123456")

page.click("text='登录'")
# page.click("//*[@id='TANGRAM__PSP_3__submit']")
page.wait_for_timeout(3000) # 等待3秒

browser.close() # 关闭浏览器对象

2-2 定位器

  • 定位器
    • 过滤定位器:locator.filter()可以对locator定位到的元素进行筛选过滤。
      • count():统计元素的个数。
      • first:定位多个时,匹配第一个。
      • last:定位多个时,匹配最后一个。
      • nth():定位多个时,根据索引定位元素。
    • Locator定位机制与元素句柄ElementHandle
      • locator()是定位当前页面元素,不会自动等待,若结合click等方法,会自动等待处于可操作状态。
      • JavaScript中ElementHandle即页内DOM元素,ElementHandles使用page.query_selector()方法创建。
      • Locator和ElementHandle之间的区别在于后者指向特定元素,而Locator捕获元素,检索元素的逻辑。
      • 不鼓励使用ElementHandle,而是使用Locator对象和网络优先断言。
        • Selenium采用的是HTTP协议,获取的元素句柄是固定的,不能实时去获取页面上的元素。
        • Playwright采用Websocket协议,可实时获取页面元素,DOM结构变化时也能重新获取到。
    • page.get_by_role()
      • 优先考虑通过显式和隐式可访问性属性进行定位具有可访问性特征的元素。
        • 显式可访问性属性,指开发人员明确地为元素设置的属性,具有描述元素及其功能的可访问性信息。
        • 隐式可访问性属性,根据元素的其他属性和内容推断可访问性信息,而非特定设置为可访问性属性。
      • 可以考虑将鼠标悬停在定位器指向的元素上,再点击定位器所指向的元素,底层DOM元素将被定位两次。
      • 创建定位器的方法也可用于Locator类和FrameLocator类,支持将其链接起来并迭代地缩小定位器的范围。
        • Locator类定位页面上的元素,page.locator()ElementHandle.locator()locator.nth()
        • FrameLocator类定位页面上的框架(iframe),方法有page.frame_locator()locator.frame()
      • 考虑DOM结构,也可以通过隐含角色定位元素。
        • 角色定位器包括按钮、复选框、标题、链接、列表、表格等。
        • 且定位遵循ARIA角色、ARIA属性和可访问名称的W3C规范。
    • page.get_by_text():文本内容定位,非交互式元素如div、span、p等。
    • page.get_by_title():通过标题title的属性定位元素。
    • page.get_by_label():通过关联标签的文本定位元素。
    • page.get_by_test_id():依据data-testid属性定位元素。
    • page.get_by_alt_text():依据替代文本来进行图像定位,例如img、area元素。
    • page.get_by_placeholder():定位无标签但具有占位符文本的表单元素时使用。
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
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=100)
page = browser.new_page() # 打开标签页
page.goto("https://www.baidu.com/")

lacate = page.locator("#s-top-left>a")
lacate.first.click() # 点击第一个
lacate.last.click() # 点击最后一个
lacate.nth(2).click() # 索引第三个元素
print(lacate.count()) # 统计元素的个数
page.wait_for_timeout(3000)

loc1 = page.locator("id=kw")
print(loc1)
print(loc1.count()) # 1表示元素存在
loc1 = page.query_selector("#kw") # ElementHandles
print(loc1)
loc1 = page.query_selector_all("#kw")
print(loc1) # 复数定位方式返回list

loc2 = page.locator("id=test")
print(loc2)
print(loc2.count()) # 0表示元素不存在
loc2 = page.query_selector("#test")
print(loc2)
loc2 = page.query_selector_all("#test")
print(loc2)

browser.close() # 关闭浏览器对象

(1) 过滤链式定位

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
import re
from playwright.sync_api import sync_playwright, expect

with sync_playwright() as pw:
browser = pw.chromium.launch(headless=False, slow_mo=1000)
page = browser.new_page()
page.goto("https://www.baidu.com/")

page.locator("//*[@id='s-top-left']/a[1]").filter(has_text="新闻").click()
# 根据div元素定位到多个a标签,继续缩小范围,按文本过滤
page.locator("#s-top-left>a").filter(has_text="新闻").click()
page.locator("#s-top-left>a", has_text="新闻").click()
page.wait_for_timeout(1000)

page.get_by_role("listitem").filter(has_text=re.compile("1")).click()
expect(
page.get_by_role("listitem").filter(has_text="1")
).to_have_count(1) # 断言是否能定位到元素
page.get_by_role("listitem").filter(has_text="1").click() # 对于ul-li元素
page.wait_for_timeout(1000) # 可用listitem角色定位方式

locate = page.get_by_role("listitem").filter(has_text="1")
locate.click() # 链式定位
page.wait_for_timeout(1000)

browser.close() # 关闭浏览器对象

(2) get_by_role()

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
<!-- get_by_role.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>get_by_role</title>
<script>
function myFunction() {
var x;
var person = prompt("请输入你的名字:", "");
if (person != null && person != "") {
x = "你好," + person + "!今天感觉如何?";
document.getElementById("demo").innerHTML = x;
}
}
</script>
</head>
<body style="text-align: center;">
<p>点击按钮查看输入的对话框。</p>
<button onclick="myFunction()" style="width: 195px; height: 40px;">点我</button>
<p id="demo"></p>

<!-- page.get_by_role("button", name="点我").click() -->

<!-- # 两次定位之间发生了DOM的重新渲染,定位器将使用最新匹配元素,可避免使用过时的元素 -->
<!-- locator = page.get_by_role("button", name="点我") -->
<!-- # hover悬停在定位器指向的元素上,click点击定位器所指向的元素 -->
<!-- locator.hover() -->
<!-- locator.click() -->

<!-- locator = page.locator("body").get_by_role("button", name="点我") -->
<!-- locator.click() -->

<h3>登录</h3>
<label>
<input type="checkbox" />用户
<!-- <input type="checkbox" />游客 --><!-- 若多个input写一起,则只返回第一个 -->
</label><br/><br/>
<label>
<input type="checkbox" />游客
</label><br/><br/>
<button>提交</button>

<!-- # 验证页面是否正确page.check("input[name='游客']")显示"登录"标题的断言语句 -->
<!-- expect(page.get_by_role("heading", name="登录")).to_be_visible() -->

<!-- # 模拟选择订阅选项(只返回匹配的第一个元素) -->
<!-- page.get_by_role("checkbox", name="游客").check() -->

<!-- # 模拟点击提交按钮,结合正则表达式定位角色为button且名称包含"提交"的元素,然后点击该按钮 -->
<!-- page.get_by_role("button", name=re.compile("提交", re.IGNORECASE)).click() -->
</body>
</html>

(3) get_by_text()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- get_by_text.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>get_by_text</title>
</head>
<body style="text-align: center;">
<span>欢迎访问,张三!</span>
<p>This is a <span style="color: blue;">blue</span> text.</p>

<!-- # 通过字符串匹配 -->
<!-- expect(page.get_by_text("欢迎访问,张三!")).to_be_visible() -->

<!-- # 精确匹配 -->
<!-- expect(page.get_by_text("欢迎访问,张三!", exact=True)).to_be_visible() -->

<!-- # 正则表达式匹配 -->
<!-- expect(page.get_by_text(re.compile("欢迎访问,张三!", re.IGNORECASE))).to_be_visible() -->
</body>
</html>

(4) get_by_title()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- get_by_title.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>get_by_title</title>
</head>
<body style="text-align: center;">
<span>欢迎访问,张三!</span><br><br>
<div title="话题数">27个话题</div>

<!-- # 找到具有匹配title属性的元素 -->
<!-- expect(page.get_by_title("话题数")).to_have_text("27个话题") -->
</body>
</html>

(5) get_by_label()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- get_by_label.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>get_by_label</title>
</head>
<body style="text-align: center;">
<h2>登录</h2><br>
<label>用户 <input type="username" /></label><br><br>
<label>密码 <input type="password" /></label>

<!-- # 通过标签文本定位后填写输入 -->
<!-- page.get_by_label("用户").fill("zhangs") -->
<!-- page.get_by_label("密码").fill("secret") -->
</body>
</html>

(6) get_by_test_id()

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
<!-- get_by_test_id.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>get_by_test_id</title>
<style>
table {
display: flex; justify-content: center;
align-items: center; margin: 0;
}
</style>
</head>
<body style="text-align: center;">
<button data-testid="test_id">行程表</button><br><br>

<!-- # 通过测试ID找到该元素 -->
<!-- page.get_by_test_id("test_id").click() -->

<table>
<tr><th>时间</th><th>&emsp;行程</th></tr>
<tr><td>04:00</td><td>&emsp;爬山看日出</td></tr>
<tr><td>17:30</td><td>&emsp;海滩边烧烤</td></tr>
</table>
</body>
</html>

(7) get_by_alt_text()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- get_by_alt_text.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>get_by_alt_text</title>
</head>
<body style="text-align: center;">
<h2>图片</h2><br>
<img src="http://mtw.so/5NabrQ" alt="baidu_logo">

<!-- # 通过文本选项找到图像后单击 -->
<!-- page.get_by_alt_text("baidu_logo").click() -->
</body>
</html>

(8) get_by_placeholder()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- get_by_placeholder.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>get_by_placeholder</title>
</head>
<body style="text-align: center;">
<h2>邮箱</h2>
<input type="email" placeholder="name@example.com" required>

<!-- # 通过占位符文本定位后填充输入 -->
<!-- page.get_by_placeholder("name@example.com").fill("playwright@microsoft.com") -->
</body>
</html>

2-3 输入文字

  • 输入文字
    • locator.fill()是填写表单字段的最简单方法,适用于<input><textarea>[contenteditable]
    • 大多时候,locator.fill()会正常工作,如果页面上有特殊的键盘处理,只需要使用locator.type()
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
<!-- input_text.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>input_text</title>
</head>
<body style="text-align: center;">
<h3>输入文本</h3>

<label for="dateInput">&emsp;&emsp;&emsp;</label>
<input type="date" id="dateInput" name="dateInput" style="width: 180px;">
<br><br>

<label for="timeInput">&emsp;&emsp;&emsp;</label>
<input type="time" id="timeInput" name="timeInput" style="width: 180px;">
<br><br>

<label for="datetimeInput">日期时间&emsp;</label>
<input type="datetime-local" id="datetimeInput" name="datetimeInput" style="width: 180px;">
<br><br>

<label>固定文本&emsp;</label>
<input type="textbox" style="width: 180px;">
<br><br>

<label for="textareaElement">伸缩文本&emsp;</label>
<textarea id="textareaElement" name="textareaElement" rows="3" cols="26"></textarea>
<br><br>

<label for="contenteditableElement">可编辑元素&emsp;</label>
<div id="contenteditableElement" contenteditable="true"></div>
</body>
</html>

(1) 同步输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=1000) # 启动chromium浏览器
page = browser.new_page() # 打开一个标签页
page.goto(r".../input_text.html") # input_text.html文件路径
print(page.title()) # 打印当前页面title

page.fill("#dateInput", "2023-02-03")
page.fill("#timeInput", "21:21")
page.fill("#datetimeInput", "2023-02-03T21:21")
page.fill("input[type='textbox']", "固定文本输入内容")
page.fill("#textareaElement", "伸缩文本输入内容")
page.type("#contenteditableElement", "可编辑元素输入内容")
page.wait_for_timeout(3000)

browser.close() # 关闭浏览器对象

(2) 异步输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import 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(r".../input_text.html") # input_text.html文件路径
print(await page.title()) # 打印当前页面title
await page.wait_for_timeout(3000)

await page.fill("#dateInput", "2023-02-03")
await page.fill("#timeInput", "21:21")
await page.fill("#datetimeInput", "2023-02-03T21:21")
await page.fill("input[type='textbox']", "固定文本输入内容")
await page.fill("#textareaElement", "伸缩文本输入内容")
await page.type("#contenteditableElement", "可编辑元素输入内容")
await page.wait_for_timeout(3000)

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

asyncio.run(main())

2-4 鼠标点击*

  • 鼠标点击
    • locator.click()(单击)、locator.dbclick()(双击)、locator.click(button="right")(右击)。
    • locator.click(modifiers=["Shift"])(Shift+鼠标左键单击操作)、locator.hover()(鼠标悬停)。
    • locator.click(position={ "x": 0, "y": 0})(模拟在指定坐标位置处,进行鼠标左键点击的操作)。
    • 相比Selenium,不用自定义轮询等待,不用判断元素是否隐藏,元素在视图中也不需要手动设置滚动。
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
from playwright.sync_api import sync_playwright                 # sync同步运行

with sync_playwright() as p:
browser = p.chromium.launch(headless=False) # 启动chromium浏览器
page = browser.new_page() # 打开一个标签页
page.goto("https://www.baidu.com") # 打开百度地址
print(page.title()) # 打印当前页面title
page.get_by_text("换一换").click()
page.wait_for_timeout(1000) # 单击
page.get_by_text("换一换").dblclick()
page.wait_for_timeout(1000) # 双击
page.get_by_text("换一换").click(button="right")
page.wait_for_timeout(1000) # 右击

page.locator("//*[@id='s-top-left']/div/a").hover() # 鼠标悬停在“更多”上
page.wait_for_timeout(1000)
element = page.wait_for_selector("//*[@id='s-usersetting-top']")
bounding_box = element.bounding_box() # 获取元素的边界框信息
x = bounding_box["x"] + bounding_box["width"] // 2 # 计算鼠标点击的x和y坐标
y = bounding_box["y"] + bounding_box["height"] // 2
print("x坐标:", x, "\ny坐标:", y)
# element.click(position={"x": x, "y": y}) # 鼠标点击元素的指定位置,报错
page.wait_for_timeout(1000)
page.click("text=更多", modifiers=["Shift"])
page.wait_for_timeout(3000) # Shift+鼠标左键单击

# 【滚动到元素位置】以下操作没进行页面切换,需用户手动切换才能看到效果(点击百度更多跳到新标签页了)
page.goto("https://www.runoob.com/")
page.get_by_text("【学习 W3C】").click()
page.wait_for_timeout(5000) # click()点击时自动滚动
page.goto("https://www.runoob.com/")
page.get_by_text("【学习 Docker】").scroll_into_view_if_needed()
page.wait_for_timeout(5000) # 滚动元素到屏幕可视窗口
page.goto("https://www.runoob.com/")
page.get_by_text("【学习 JavaScript】").hover()
page.wait_for_timeout(5000) # hover()方法将鼠标放到元素上

browser.close() # 关闭浏览器对象

2-5 拖放操作*

  • 拖放操作
    • 使用locator.drag_to()执行拖放操作,需要进行如下步骤。
      • 将鼠标悬停在要拖动的元素上,按住鼠标左键不放。
      • 将鼠标移动到将接收放置的元素上,松开鼠标左键。
    • 如果想要精确地控制拖放操作,需使用如下较低级别的方法。
      • page.locator(selector).hover():悬停在页面元素上。
      • page.mouse.down()按下鼠标,button="middle"按住鼠标的中间位置,page.mouse.up()释放鼠标。
      • page.mouse.move()把鼠标放到指定的x和y坐标位置(即绝对坐标位置,selenium使用相对偏移位置)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=1000) # 启动chromium浏览器
page = browser.new_page() # 打开一个标签页
page.goto("https://www.baidu.com/") # 打开百度首页
print(page.title()) # 打印当前页面title

# page.locator("#u1 > div.ai-entry-right.ai-entry-right-nologin > img").drag_to(
# page.locator("#kw")
# ) # 会报错,该怎么操作
page.locator("//*[@id='s-hotsearch-wrapper']/div/a[1]/div/i[1]").hover()
page.mouse.down() # 按下鼠标
# page.mouse.down(button="middle")
page.locator("#kw").hover()
page.mouse.up() # 释放鼠标
page.wait_for_timeout(3000)

browser.close() # 关闭浏览器对象

2-6 iframe定位

  • iframe定位
    • iframe即一个html嵌套了另一个html,最简单的识别方法就是看定位元素外层有没iframe标签名。
    • frame与iframe都用于表示页面框架,iframe特指嵌入式框架,而frame可以表示主框架或子框架。
      • page.main_frame:获取page对象本身的frame对象。
      • frame.child_frames:获取frame下的子frame对象。
      • page.frames:获取page对象全部iframes,包含page本身的frame对象。
    • page.frame_locator(selector):仅查找页面中,第一个匹配的frame对象。
      • frame_locator().first:定位多个时,匹配第一个。
      • frame_locator().last:定位多个时,返回最后一个。
      • frame_locator().nth(index):定位多个时进行索引。
    • page.locator(selector).frame_locator(selector):允许在指定frame内查找元素,可操作多个frame。
    • page.frame(name, url):通过page对象直接定位iframe对象,传入name参数或传入url参数,说明如下。
      • name属性不能模糊匹配,只能精确匹配字符串。
      • iframe没有name属性时,可以使用id属性来定位。
      • url属性值,即页面上看到的src属性,支持模糊匹配。
      • iframe上的动态id非固定,可使用css的正则匹配元素属性,也可使用xpath的contains包含元素属性。
        • $('[name^="value"]'):匹配name以value开头的元素。
        • $('[name$="value"]'):匹配name以value结尾的元素。
        • $('[class*="value"]'):匹配class属性包含value的元素。
      • page.frame():返回的对象能直接使用fill()click方法。
      • page.frame_locator(selector):返回的对象只能用locator()方法定位元素,然后click()等操作。
    • page.query_selector(selector).content_frame():通过query_selector定位元素,再切换到iframe对象上。
    • 多层iframe需要一层一层定位,若需要在iframe上执行js代码,必须使用page.frame()方法定位到iframe对象。
    • 才有iframe.evaluate(js)方法执行js,而page.frame_locator()方法只能定位操作元素,没有执行js的方法。
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
from playwright.sync_api import sync_playwright


def dump_frame_tree(frame, indent): # 打印浏览器中的帧树结构
print(indent + frame.name + "@" + frame.url) # 打印当前帧的名称和URL
for child in frame.child_frames: # indent用来确定缩进级别
dump_frame_tree(child, indent + " ")


with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=1000) # 启动chromium浏览器
page = browser.new_page() # 打开一个标签页
page.goto("https://account.cnblogs.com/signin")

dump_frame_tree(page.main_frame, "") # 看层级结构,这里有三层
print(page.main_frame) # 获取page对象本身的frame对象
print(page.frames) # 获取page对象全部的frames
print(page.main_frame.child_frames) # 获取page对象的子frame对象

page.goto("https://mail.163.com/")
print(page.frames)
for f in page.frames:
print(f) # 获取全部的iframes
print(f.name)
print(f.url)
page.wait_for_timeout(3000)

# iframe = page.frame_locator("iframe[id^=x-URS-iframe]")
iframe = page.frame_locator("//iframe[contains(@id, 'x-URS-iframe')]")
iframe.locator("[name='email']").fill("Tester") # xpath模糊匹配
iframe.locator("[name='password']").fill("123456")
iframe.locator("#dologin").click()
page.wait_for_timeout(3000)

print(iframe.first) # 匹配第一个
print(iframe.last) # 返回最后一个
print(iframe.nth(1)) # 索引第二个

page.goto("https://mail.qq.com/")
frame = page.frame(name="ptlogin_iframe") # 返回的对象能直接fill()或click()
frame.click("#switcher_plogin")
frame.fill("#u", "Tester")
frame.fill("#p", "12345")
frame.click("#login_button")
page.wait_for_timeout(3000)

browser.close() # 关闭浏览器对象

2-7 下拉框选择

  • 下拉框选择
    • 使用locator.select_option()选择元素中的一个选项或者多个选项,可以指定value或label来选择。
    • 先定位select元素再定位选项(根据选项名称、index索引、label标签定位)、通过page对象直接调用。

(1) select_option.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=1000) # 启动chromium浏览器
page = browser.new_page() # 打开一个标签页
page.goto(r".../select_option.html") # select_option.html文件路径
print(page.title())

page.locator("#sid").select_option("百度")
page.wait_for_timeout(3000) # 根据选项名称定位
page.locator("#sid").select_option(label="搜狗")
page.wait_for_timeout(1000) # 根据label标签定位
page.select_option("select#city", "厦门")
page.wait_for_timeout(1000) # 通过page对象直接调用
page.locator("#color").select_option(value=["3", "5", "7"])
page.wait_for_timeout(3000) # 根据value标签定位

browser.close() # 关闭浏览器对象

(2) select_option.html

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
<!-- select_option.html -->
<!DOCTYPE html>
<html lang="en">
<html>
<head>
<meta charset="UTF-8">
<title>select_option</title>
</head>
<body>
<script>
// 搜索引擎:select单选
function oc() {
alert(document.f1.t1.value());
}
// 水果拼盘、选择城市:multiple可多选
(function() {
selectMultip = {
register: function(id) {
// 思路:为下拉选创建一个隐藏的子选项
// 每次单选之后将单选的值追加到隐藏的子选项中,并将子选项选中显示
// 全局查找所有标记multip的select
document.querySelectorAll("[multip]").forEach(function(e) {
render(e);
})
},
reload: function(id, data, setData) {
var htm = "";
for (var i = 0; i < data.length; i++) {
htm += "<option value='' + data[i].value + ''>" + data[i].text + "</option>"
}
var e = document.getElementById(id);
e.innerHTML = htm;
render(e);
this.setVal(id, setData);
},
setVal: function(id, str) {
var type = Object.prototype.toString.call(str);
switch (type) {
case "[object String]":
document.getElementById(id).val = str;
break;
case "[object Array]":
document.getElementById(id).val = str.toString();
break;
default:
break;
}
},
getVal: function(id) {
return document.getElementById(id).val;
},
}
function render(e) {
e.param = {
arr: [],
valarr: [],
opts: []
};
var choosevalue = "",
op;
for (var i = 0; i < e.length; i++) {
op = e.item(i);
e.param.opts.push(op);
if (op.hasAttribute("choose")) {
if (choosevalue == "") {
choosevalue = op.value
} else {
choosevalue += "," + op.value;
}
}
}
// 创建一个隐藏的option标签用来存储多选的值,其中的值为一个数组
var option = document.createElement("option");
option.hidden = true;
e.appendChild(option);
e.removeEventListener("input", selchange);
e.addEventListener("input", selchange);
// 重新定义标签基础属性的get和set方法,实现取值和赋值的功能
Object.defineProperty(e, "val", {
get: function() {
return this.querySelector("[hidden]").value;
},
set: function(value) {
e.param.valarr = [];
var valrealarr = value == "" ? [] : value.split(",");
e.param.arr = [];
e.param.opts.filter(function(o) {
o.style = "";
});
if (valrealarr.toString()) {
for (var i = 0; i < valrealarr.length; i++) {
e.param.opts.filter(function(o) {
if (o.value == valrealarr[i]) {
o.style = "color: blue;";
e.param.arr.push(o.text);
e.param.valarr.push(o.value)
}
});
}
this.options[e.length - 1].text = e.param.arr.toString();
this.options[e.length - 1].value = e.param.valarr.toString();
this.options[e.length - 1].selected = true;
} else {
this.options[0].selected = true;
}
},
configurable: true
})
// 添加属性choose,此属性添加到option中用来指定默认值
e.val = choosevalue;
// 添加属性tip,此属性添加到select标签上
if (e.hasAttribute("tip") && !e.tiped) {
e.tiped = true;
e.insertAdjacentHTML("afterend", "<i style='color: red; \
font-size: 12px'>*可多选*\</i>");
}
}
function selchange() {
var text = this.options[this.selectedIndex].text;
var value = this.options[this.selectedIndex].value;
this.options[this.selectedIndex].style = "color: blue;";
var ind = this.param.arr.indexOf(text);
if (ind > -1) {
this.param.arr.splice(ind, 1);
this.param.valarr.splice(ind, 1);
this.param.opts.filter(function(o) {
if (o.value == value) {
o.style = "";
}
});
} else {
this.param.arr.push(text);
this.param.valarr.push(value);
}
this.options[this.length - 1].text = this.param.arr.toString();
this.options[this.length - 1].value = this.param.valarr.toString();
if (this.param.arr.length > 0) {
this.options[this.length - 1].selected = true;
} else {
this.options[0].selected = true;
}
}
})();
</script><br>
<form>
搜索引擎:
<select id="sid" style="width: 300px; height: 30px;">
<option value="">请选择</option>
<option value="o1" id="id1">谷歌</option>
<option value="o2" id="id2">必应</option>
<option value="o3" id="id3">搜狗</option>
<option value="o4" id="id4">百度</option>
<option value="o4" id="id5">火狐</option>
</select>
</form>
<form>
水果拼盘:
<select multip id="fruits" style="width: 300px; height: 30px;" tip>
<option value="">请选择</option>
<option value="1">桃子</option>
<option value="2">苹果</option>
<option value="3">香蕉</option>
<option value="4">樱桃</option>
<option value="5">草莓</option>
<option value="6">杨桃</option>
<option value="7">蜜柚</option>
</select>
<script>
selectMultip.register();
</script>
</form>
<form>
选择城市:
<select multip id="city" style="width: 300px; height: 30px;">
<option value="">请选择</option>
<option value="gz">广州</option>
<option value="wh">武汉</option>
<option value="bj">北京</option>
<option value="xm">厦门</option>
<option value="hz">杭州</option>
<option value="sz">深圳</option>
<option value="sz">苏州</option>
<option value="nj">南京</option>
<option value="cq">重庆</option>
</select>
</form>
<form>
颜色选择:
<select multiple id="color" style="width: 300px; height: 132px;">
<option value="1">红色</option>
<option value="2">橙色</option>
<option value="3">黄色</option>
<option value="4">绿色</option>
<option value="5">青色</option>
<option value="6">蓝色</option>
<option value="7">紫色</option>
</select>
</form>
</body>
</html>

2-8 单选与复选

  • 单选与复选
    • locator.set_checked()选中和取消选中单复选框的最简单方法。
    • locator.check()选中,locator.uncheck()不选。
    • locator.click()点击,locator.is_checked()断言是否被选中。
    • 上述方法都适用于input[type=checkbox]input[type=radio]

(1) set_checked.py

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
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=1000) # 启动chromium浏览器
page = browser.new_page() # 打开一个标签页
page.goto(r".../set_checked.html") # set_checked.html文件路径
print(page.title())

page.get_by_label("葡萄").click()
page.wait_for_timeout(3000) # 点击
page.get_by_label("橘子").check()
page.wait_for_timeout(3000) # 选中
assert page.get_by_label("汽车").is_checked() is False
page.wait_for_timeout(3000) # 断言是否被勾选
page.locator("#qc").set_checked(True) # 单选
page.locator("#gw").check() # 选中
page.wait_for_timeout(3000)
page.locator("#gw").uncheck() # 不选
page.wait_for_timeout(3000)
box = page.locator("[type='checkbox']")
for item in box.all():
item.check() # 定位全部CheckBox批量选中
page.wait_for_timeout(3000)

browser.close() # 关闭浏览器对象

(2) set_checked.html

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
<!-- set_checked.html -->
<!DOCTYPE html>
<html lang="en">
<html>
<head>
<meta charset="UTF-8">
<title>set_checked</title>
</head>
<body>
<fieldset style="width: 355px; height: 45px;">
<legend>单选按钮Radio</legend>
<form action="">
<label><input type="radio" name="fruit" value="pg" id="pg">苹果</label>
<label><input type="radio" name="fruit" value="jz" id="jz">橘子</label>
<label><input type="radio" name="fruit" value="xj" id="xj">香蕉</label>
<label><input type="radio" name="fruit" value="pt" id="li" checked="">葡萄</label>
<label><input type="radio" name="fruit" value="xg" id="xg" checked="">西瓜</label>
</form>
</fieldset>
<br>
<fieldset style="width: 355px; height: 130px;">
<legend>多选按钮Checkbox</legend>
<form action="">
<label>
<input type="checkbox" name="checkbox" value="汽车" id="qc">汽车
</label><br>
<label>
<input type="checkbox" name="checkbox" value="购物" id="gw">购物
</label><br>
<label>
<input type="checkbox" name="checkbox" value="购物" id="ms">美食
</label><br>
<label>
<input type="checkbox" name="checkbox" value="旅游" id="ly" readonly="">旅游
</label><br>
<label>
<input type="checkbox" name="checkbox" value="音乐" id="yy" readonly="">音乐
</label><br>
</form>
</fieldset>
</body>
</html>

2-9 聚焦给定元素

  • 聚焦给定元素
    • focus()主要用于模拟用户与网页元素的交互。
    • 确保正确的元素处于活动状态,以便执行相应操作。
    • 通常用于交互性测试、表单输入,或者键盘事件上。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=1000) # 启动chromium浏览器
page = browser.new_page() # 打开一个标签页
page.goto("https://www.baidu.com/")
print(page.title())

input_element = page.locator("#kw")
input_element.focus() # 将焦点设置到输入框上
input_element.type("Playwright") # 模拟键盘输入
page.wait_for_timeout(3000)

browser.close() # 关闭浏览器对象

3 等待和定时器

  • 等待和定时器
    • 等待
      • locator.wait_for():在延迟加载的页面中等待元素可见,可等待页面加载完成后再执行操作。
      • chromium.launch(headless=False, slow_mo=50):可能会增加执行时间。
        • 将减慢浏览器的执行速度,slow_mo的单位为毫秒,作用范围是全局的。
        • 从启动浏览器到操作元素每个动作都会有等待间隔,方便查看操作情况。
      • locator.click()locator.fill():自动等待元素可见,可简化代码,但可能导致超时异常。
      • page.wait_for_timeout(5000):等待一定时间后再执行操作,设置时间过长可能影响执行效率。
    • 定时器:使用Python的asyncio库来创建异步定时任务。
    • timeout等待超时,默认30秒,传递0将超时时间设为无限大,指不会主动超时,会一直等待直到条件满足。
      • page.set_fault_navigation_timeoutbrowser_context.set_fault_timeout()
      • page.set_fault_timeout()browser_context.set_fault_navigation_timeout()

3-1 等待元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=1000) # 启动chromium浏览器
page = browser.new_page() # 打开一个标签页
page.goto("https://www.baidu.com") # 打开百度地址

page.locator(
"//*[@id='s-hotsearch-wrapper']/div/a[1]/div"
).wait_for() # 等待元素
page.locator(
"//*[@id='s-hotsearch-wrapper']/div/a[1]/div"
).click() # 自动等待元素,再点击
page.wait_for_timeout(1000) # 点击后等待1秒

browser.close() # 关闭浏览器对象

3-2 自定义等待

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

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=1000) # 启动chromium浏览器
page = browser.new_page() # 打开一个标签页
page.goto("https://www.baidu.com") # 打开百度地址

page.fill("#kw", "Playwright")
page.click("#su")
page.wait_for_load_state("networkidle") # 自定义等待

browser.close() # 关闭浏览器对象

3-3 定时器操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import 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()
url = "https://www.baidu.com/" # 要访问的网页URL

interval_seconds = 10 # 设置定时器的间隔时间(以秒为单位)
try:
while True:
await page.goto(url) # 执行自定义操作,例如访问网页
await asyncio.sleep(interval_seconds) # 设置定时器的间隔时间
print("间隔10秒刷新一次页面!")

except KeyboardInterrupt:
pass

finally:
await browser.close() # 关闭浏览器对象

asyncio.run(main()) # 运行主函数

3-4 timeout等待超时

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

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=1000) # 启动chromium浏览器
page = browser.new_page() # 打开一个标签页
page.goto("https://www.baidu.com") # 打开百度地址

page.fill("#kw", "Playwright")
page.click("#su")
page.set_default_timeout(0) # 不会主动超时

browser.close() # 关闭浏览器对象

4 弹窗或对话框

  • 弹窗或对话框
    • Playwright支持监听dialog事件,JavaScript对话框包括alert、beforeunload、confirm、prompt。
      • 侦听器必须使用dialog.accept()dialog.dismiss()操作对话框。
      • 否则页面将冻结等待对话框,并且无法进行单击、搜索等下一步操作。
    • 部分场景应用时,可能弹出一些麦克风或摄像头的权限框,通过监听alert没法捕获到。
      • 可以给浏览器配置默认允许麦克风和摄像头等权限,不让弹窗出来。
      • 使用context的grant_permissions()方法添加麦克风或摄像头权限。

4-1 alert_box.py

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
from playwright.sync_api import sync_playwright


def handle_dialog(dialog): # 监听后的处理
print("Type: " + dialog.type) # 返回对话框的类型
print("Mess: " + dialog.message) # 获取对话框中显示的消息
print("Return: " + dialog.default_value) # 返回默认提示值,否则返回空字符串
dialog.dismiss() # 关闭对话框


def run(playwright):
chromium = playwright.chromium
browser = chromium.launch(headless=False, slow_mo=3000)
page = browser.new_page()
page.goto(r".../alert_box.html") # alert_box.html文件路径

page.on("dialog", handle_dialog)
page.locator("body > button:nth-child(5)").click()
page.evaluate("alert('Test')") # 没监听时,它自动会关闭
page.wait_for_timeout(1000)

browser.close() # 关闭浏览器对象


with sync_playwright() as playwright:
run(playwright)

4-2 alert_box.html

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
<!-- alert_box.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>alert_box</title>
</head>
<body style="text-align: center;">
<p>点击按钮查看对话框</p>
<button onclick="myFunction()" style="width: 195px; height: 40px;">点我</button>
<p id="demo"></p>

<button onclick="showAlert()" style="width: 195px; height: 40px;">Alert</button>
<button onclick="showPrompt()" style="width: 195px; height: 40px;">Prompt</button>
<button onclick="showConfirm()" style="width: 195px; height: 40px;">Confirm</button>

<script>
function myFunction() {
var x;
var person = prompt("请输入你的名字:", "");
if (person != null && person != "") {
x = "你好," + person + "!今天感觉如何?";
document.getElementById("demo").innerHTML = x;
}
}

// 显示Alert弹窗
function showAlert() {
alert("这是一个Alert弹窗!");
}

// 显示Prompt弹窗
function showPrompt() {
const result = prompt("请输入您的姓名:", "默认值");
if (result !== null) {
alert("您输入的姓名是: " + result);
} else {
alert("您取消了输入。");
}
}

// 显示Confirm弹窗
function showConfirm() {
const result = confirm("您确定要执行此操作吗?");
if (result) {
alert("您点击了确认按钮。");
} else {
alert("您点击了取消按钮。");
}
}
</script>
</body>
</html>

5 模拟键盘操作

  • 模拟键盘操作
    • locator.press()方法:可以聚焦所选元素,并产生单个击键。
    • 接受在键盘事件的keyboardEvent.key属性中发出的逻辑键名称。
      • Backquote、Minus、Equal、Backslash、Backspace、Tab、Delete、Escape、ArrowDown、End、Enter。
      • Home、Insert、PageDown、PageUp、ArrowRight、ArrowUp、F1-F12、Digit 0-9、Key A-Z、Key a-z。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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() # 打开标签页
page.goto("https://www.baidu.com/")

page.locator("//*[@id='kw']").press("$") # 按键$
page.wait_for_timeout(1000)
page.locator("//*[@id='kw']").press("Shift+A") # 按键A
page.wait_for_timeout(1000)
page.locator("//*[@id='kw']").press("Shift+a") # 按键a
page.wait_for_timeout(1000)
page.locator("//*[@id='kw']").press("Shift+ArrowLeft") # 光标左选一位
page.wait_for_timeout(1000)
page.locator("//*[@id='kw']").press("Control+V") # 粘贴剪切板的内容
page.wait_for_timeout(1000)
page.locator("//*[@id='su']").press("Enter") # 回车
page.wait_for_timeout(1000)

browser.close() # 关闭浏览器对象

6 文件上传和下载

  • 文件上传和下载
    • 文件上传
      • 文件上传是input输入框,并且类型是type="file"情况时,使用locator.set_input_files()
      • 如果不是input输入框且必须点开文件框的情况下,使用page.expect_file_chooser()进行监听。
      • 点击选择文件按钮时,自动触发操作,则使用page.on("filechooser", )监听filechooser事件。
    • 文件下载:当浏览器上下文关闭时,所有属于浏览器上下文的下载文件都会被删除。

6-1 文件上传

  • 文件上传
    • locator.set_input_files():用于设置文件输入框的文件路径。
    • 模拟向文件输入框中填充文件路径,以实现文件上传的自动化操作。
    • 该方法接受一个文件路径参数,指定一个或多个文件路径进行上传。
    • 操作方法
      • file_chooser.page:返回此文件选择器所属的页面。
      • file_chooser.element:返回与此文件选择器关联的输入元素。
      • file_chooser.is_multiple():判断此文件选择器是否接受多个文件。

(1) upload_file.py

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
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False) # 启动chromium浏览器
page = browser.new_page() # 打开一个标签页
page.goto(r".../upload_file.html") # upload_file.html文件路径
print(page.title())

page.locator(
"body > fieldset > form:nth-child(2) > input[type=file]"
).set_input_files("aaa.py") # 上传一个文件,需要文件路径
page.wait_for_timeout(1000)
page.locator(
"body > fieldset > form:nth-child(3) > input[type=file]"
).set_input_files(["xxx.py", "yyy.py", "zzz.py"]) # 上传三个文件,当前路径为./xxx.py
page.wait_for_timeout(1000)
page.locator(
"body > fieldset > form:nth-child(2) > input[type=file]"
).set_input_files([]) # 空数组清除所选文件
page.wait_for_timeout(1000)

with page.expect_file_chooser() as fc_info: # 监听到弹出框
page.locator("body > fieldset > form:nth-child(2) > input[type=file]").click()
page.pause()
file_chooser = fc_info.value

print(file_chooser.page) # 返回此文件选择器所属的页面
print(file_chooser.element) # 返回与此文件选择器关联的输入元素
print(file_chooser.is_multiple()) # 判断此文件选择器是否接受多个文件

file_chooser.set_files(r"bbb.py") # 弹出文件框手动点击继续操作
page.wait_for_timeout(1000)

page.on("filechooser", lambda file_chooser: file_chooser.set_files(
r"ccc.py"
)) # 事件监听filechooser
page.locator("body > fieldset > form:nth-child(2) > input[type=file]").click()
page.wait_for_timeout(1000)

browser.close() # 关闭浏览器对象

(2) upload_file.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- upload_file.html -->
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>upload_file</title>
</head>
<body style="text-align: center;">
<fieldset>
<legend>上传文件</legend>
<form>单个文件
<input type="file" name="one_file">
</form>
<form>多个文件
<input type="file" name="more_file" multiple>
</form>
</fieldset>
</body>
</html>

6-2 文件下载

  • 文件下载
    • download.cancel():取消下载。
    • download.delete():删除下载文件,如有必要,将等待下载完成。
    • download.failure():返回下载错误,如有必要,将等待下载完成。
    • download.path():下载成功将返回文件的路径,如有必要,将等待下载完成。
    • download.suggested_filename:获取建议文件名,下载的文件名是随机GUID。

(1) download_file.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from playwright.sync_api import sync_playwright


def run(playwright):
browser = playwright.chromium.launch(headless=False, slow_mo=3000)
page = browser.new_page()
page.goto(r".../download_file.html") # download_file.html文件路径

with page.expect_download() as download_info:
page.get_by_text("Pycharm下载").click()
download = download_info.value
print(download.url) # 获取下载的url地址
print(download.page) # 获取下载所属的页面
print(download.path()) # 生成一个随机uuid值保存
download.save_as(download.suggested_filename) # 最终用save_as保存到本地
browser.close() # 关闭浏览器对象


with sync_playwright() as playwright:
run(playwright)

(2) download_file.html

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- download_file.html -->
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>download_file</title>
</head>
<body style="text-align: center;"><br>
<a href="https://download.jetbrains.com.cn/python/pycharm-professional-2023.2.1.exe">
Pycharm下载
</a>
</body>
</html>

7 错误处理和调试

  • 错误处理和调试
    • 异常处理:一种常见的错误处理机制,使用try-except代码块捕获和处理可能出现的异常。
    • 控制台调试定位
      • 使用page.pause()进入断点状态,浏览器F12开发者工具打开Console页面调试定位。
      • 还可以使用Playwright Inspector工具,来开启录制,页面操作生成对应的元素和步骤。
      • Playwright Inspector可使用命令行启动,如:playwright codegen http://xxx.com/
      • Playwright Inspector的Pick locator可在浏览器上选需定位的元素,生成对应的locator。
      • 录制相关命令
        • playwright codegen --viewport-size=800,600 http://xxx.com/
        • playwright codegen --lang="it-IT" http://xxx.com/:模拟语言。
        • playwright codegen --color-scheme=dark http://xxx.com/:模拟配色。
        • playwright codegen --timezone="Europe/Rome" http://xxx.com/:模拟时区。
        • playwright codegen --geolocation="41.890221,12.492348" http://xxx.com/:模拟地理位置。
        • playwright codegen --device="iPhone 11":模拟移动设备iPhone11,device值必须双引号且区分大小写。
        • playwright open --load-storage=auth.json:打开Playwright上下文并加载存储数据文件以保留会话状态。
        • playwright codegen --load-storage=auth.json:在生成代码时加载存储数据,以保持与之前会话的连续性。
        • playwright codegen --save-storage=auth.json http://xxx.com/:用于生成代码时将存储数据保存到文件中。
      • console页面语法
        • playwright.selector(element):为给定元素生成选择器。
        • playwright.$$(selector):类似于playwright.$,但是会返回全部的匹配元素。
        • playwright.$(selector):使用实际的Playwright查询引擎查询Playwright选择器。
        • playwright.locator(selector):使用实际的Playwright查询引擎查询Playwright元素。
        • playwright.inspect(selector):元素面板中显示元素(需相应浏览器的DevTools支持)。
    • 高亮定位与调试:为清楚当前定位方式在页面上找到哪些元素,可以使用highlight()高亮定位元素。

7-1 异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=1000)
page = browser.new_page()
page.goto("https://www.baidu.com/")

try:
element = page.locator("#myElement") # 尝试查找ID为myElement的元素
if element:
element.click() # 找到则点击
else: # 否则抛出异常
raise Exception("没找到对应元素")
except Exception as e: # 抛出异常后打印异常信息
print(f"产生了一个错误: {str(e)}")
finally:
browser.close() # 最后无论是否异常,都关闭浏览器

7-2 控制台调试定位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=1000) # 启动chromium浏览器
page = browser.new_page() # 打开一个标签页
page.goto("https://mail.163.com/")
print(page.frames)

iframe = page.frame_locator("//iframe[contains(@id, 'x-URS-iframe')]")
iframe.locator("[name='email']").fill("Tester") # xpath模糊匹配
iframe.locator("[name='password']").fill("123456")
page.pause()
iframe.locator("#dologin").click()
page.wait_for_timeout(3000)

browser.close() # 关闭浏览器对象

7-3 高亮定位与调试

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

with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=2000)
context = browser.new_context() # 创建上下文
page = context.new_page() # 打开标签页

page.goto("https://www.baidu.com")
page.get_by_text("新闻").highlight()
page.wait_for_timeout(20000) # 高亮定位

browser.close() # 关闭浏览器对象

8 console事件监听

  • console事件监听
    • page.on("console", handler):监听特定页面的Console事件,传入处理函数handler。
    • cdpSession.on("Console.messageAdded", handler):监听CDP会话上的Console事件。
    • 后者可在不同页面间共享同一个Chrome DevTools Protocol(CDP)会话,并监听该会话下的Console事件。
    • 监听Console事件后,可执行以下操作。
      • page.on("console", handler)cdpSession.on("Console.messageAdded", handler)添加相应事件监听器。
      • page.remove_listener("event", print_request_finished)可用来删除已添加的特定页Console事件监听器。
      • 目前没有删除CDP会话的特定事件监听器方法,使用创建新的CDP会话事件监听器来替代旧的,以达删除目的。
    • 如果某个事件需要处理一次,可使用page.once("console", handler)添加一次性事件,该方法针对page页面对象。
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
from playwright.sync_api import sync_playwright


def console_msg(msg):
values = []
for arg in msg.args:
print(msg)
values.append(f"{arg.json_value()}")

with open("console.log", "a", encoding="utf-8") as fp:
fp.write("".join(values) + "\n")


with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=100)
context = browser.new_context() # 创建上下文
page = context.new_page() # 打开标签页
with page.expect_request("**/*logo*.png") as first: # 等待具有指定url的请求
page.goto("https://www.hao123.com/")
print(first.value.url)

with page.expect_popup() as popup: # 等待弹出窗口
page.locator("//*[@id='footer']/div/div[1]/a[3]").click()
page.wait_for_timeout(1000) # 弹出全站地图窗口页
popup.value.goto("https://www.hao123.com/") # 窗口页打开hao123页面
page.wait_for_timeout(1000) # 在指定的时间间隔后继续执行后续代码

page.on("console", console_msg) # 监听console内容
page.goto("https://www.baidu.com/")
page.wait_for_load_state("networkidle") # 等待页面在网络空闲状态时停止加载

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("https://www.sogou.com/")
page.wait_for_timeout(1000)

page.remove_listener("requestfinished", print_request_finished)
page.goto("https://www.sogou.com/")
page.wait_for_timeout(1000)

def handle_dialog(dialog):
dialog.accept("2021")
page.goto("https://www.runoob.com/try/try.php?filename=ionic_ionicpopup")
iframe = page.frame_locator("//*[@id='iframeResult']")
iframe.get_by_role("button", name="弹窗显示").click()
# page.once("dialog", lambda dialog: dialog.accept("2021"))
page.once("dialog", handle_dialog) # 注册对话框处理程序
page.evaluate("prompt('Enter a number:')") # 触发提示框
page.wait_for_timeout(3000)

browser.close() # 关闭浏览器对象

PlayWright 工具(一)
https://stitch-top.github.io/2023/09/09/zi-dong-hua/at05-playwright/at01-playwright-gong-ju-yi/
作者
Dr.626
发布于
2023年9月9日 22:22:50
许可协议