Playwright的基本操作和元素定位
原文链接和视频,本文大部分内容来自此,仅整理增补些许内容方便个人理解和学习。
https://www.byhy.net/etc/playwright/01/#h_4
pip install playwright
playwright install
示例代码
from playwright.sync_api import sync_playwright
input('1....')
# 启动 playwright driver 进程
p = sync_playwright().start()
input('2....')
# 启动浏览器,返回 Browser 类型对象
browser = p.chromium.launch(headless=False)
# 创建新页面,返回 Page 类型对象
page = browser.new_page()
page.goto("https://www.byhy.net/cdn2/files/selenium/stock1.html")
print(page.title()) # 打印网页标题栏
# 输入通讯,点击查询。这是定位与操作,是自动化重点,后文详细讲解
page.locator('#kw').fill('通讯') # 输入通讯
page.locator('#go').click() # 点击查询
# 打印所有搜索内容
lcs = page.locator(".result-item").all()
for lc in lcs:
print(lc.inner_text())
input('3....')
# 关闭浏览器
browser.close()
input('4....')
# 关闭 playwright driver 进程
p.stop()
切换浏览器启动
# 启动firefox浏览器
browser = p.firefox.launch(headless=False)
# 启动webkit浏览器
browser = p.webkit.launch(headless=False)
可以通过参数 <font style="color:rgb(45, 132, 172);">executable_path</font> 指定使用安装的其它浏览器
# 加上 executable_path参数
browser = p.chromium.launch(headless=False,
executable_path='c:\Program Files\Google\Chrome\Application\chrome.exe')
可以使用 <font style="color:rgb(45, 132, 172);">with as</font> 会话管理,我们的代码简化,不需要调用 <font style="color:rgb(45, 132, 172);">start()</font> 和 <font style="color:rgb(45, 132, 172);">stop()</font>
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto("https://www.byhy.net/cdn2/files/selenium/stock1.html")
print(page.title())
page.locator('#kw').fill('通讯\n')
page.locator('#go').click()
# 打印所有搜索内容
lcs = page.locator(".result-item").all()
for lc in lcs:
print(lc.inner_text())
browser.close()
页面等待
page.wait_for_timeout(1000)
脚本录制
打开自动化助手
playwright codegen
报错可以尝试
python -m playwright codegen
可以录制操作,但是无法获取信息和做一些后续处理
跟踪功能
Playwright 有个特色功能: 跟踪(tracing)
启用跟踪功能后, 可以在执行自动化后,通过记录的跟踪数据文件, 回看自动化过程中的每个细节。
下面的的代码进行了自动化搜索股票,并打开跟踪功能,保存 跟踪数据文件 为 <font style="color:rgb(45, 132, 172);">trace.zip</font>。
from playwright.sync_api import sync_playwright
p = sync_playwright().start()
browser = p.chromium.launch(headless=False)
# 创建 BrowserContext对象
context = browser.new_context()
# 启动跟踪功能
context.tracing.start(snapshots=True, sources=True, screenshots=True)
page = context.new_page()
page.goto("https://www.byhy.net/cdn2/files/selenium/stock1.html")
# 搜索名称中包含 通讯 的股票
page.locator('#kw').fill('通讯')
page.locator('#go').click()
page.wait_for_timeout(1000) # 等待1秒
lcs = page.locator(".result-item").all()
for lc in lcs:
print(lc.inner_text())
# 搜索名称中包含 软件 的股票
page.locator('#kw').fill('软件')
page.locator('#go').click()
page.wait_for_timeout(1000) # 等待1秒
lcs = page.locator(".result-item").all()
for lc in lcs:
print(lc.inner_text())
# 结束跟踪
context.tracing.stop(path="trace.zip")
browser.close()
p.stop()
执行完以后,我们发现,当前工作目录下面多了 trace.zip 这个跟踪数据文件。
怎么查看这个跟踪文件呢?有2种方法:
- 直接访问 trace.playwright.dev 这个网站,上传 跟踪文件
- 执行命令
<font style="color:rgb(45, 132, 172);">playwright show-trace trace.zip</font>
元素定位
一些常见的操作
- 点击元素,
<font style="color:rgb(45, 132, 172);">click()</font>方法 - 元素内输入文本,
<font style="color:rgb(45, 132, 172);">fill()</font>方法 - 获取元素内部文本,
<font style="color:rgb(45, 132, 172);">inner_text()</font>方法
css定位
Locator对象
Playwright 中,根据 CSS Selector 选择元素,就是使用 Locator 类型的对象
比如,前文中, Page 对象的 locator方法就会创建一个 <font style="color:rgb(45, 132, 172);">Locator</font> 类型对象,参数就可以是 CSS Selector 表达式
page.locator('#kw').fill('通讯')
page.locator('#go').click()
Page对象的 locator 定位到的如果是唯一的 html元素,就可以调用 Locator 对象的 方法,比如 <font style="color:rgb(45, 132, 172);">fill</font> , <font style="color:rgb(45, 132, 172);">click</font> , <font style="color:rgb(45, 132, 172);">inner_text</font> 等等对元素进行操作了。
具体使用方法见下文
根据 tag名、id、class 选择元素
CSS Selector 可以根据 <font style="color:rgb(45, 132, 172);">tag名</font> 、 <font style="color:rgb(45, 132, 172);">id 属性</font> 和 <font style="color:rgb(45, 132, 172);">class属性</font> 来 选择元素,
根据 tag名 选择元素的 CSS Selector 语法非常简单,直接写上tag名即可,
比如 要选择 所有的tag名为div的元素,就可以是这样
locators = page.locator('div').all()
然后可以这样,打印所有的tag名为div的元素的内部可见文本
for one in locators:
print(one.inner_text())
要获取 所有的tag名为div的元素的内部可见文本,也可以直接调用 <font style="color:rgb(45, 132, 172);">all_inner_texts</font>
texts = page.locator('div').all_inner_texts()
注意,如果 locator调用 <font style="color:rgb(45, 132, 172);">匹配的结果是多个元素</font> , 调用 <font style="color:rgb(45, 132, 172);">针对单个元素的方法</font> ,比如 <font style="color:rgb(45, 132, 172);">inner_text</font> ,会有错误抛出:
page.locator('div').inner_text()
根据id属性 选择元素的语法是在id号前面加上一个井号: <font style="color:rgb(45, 132, 172);">#id值</font>
比如 ,有下面这样的元素:
<input type="text" id='searchtext' />
就可以使用 <font style="color:rgb(45, 132, 172);">#searchtext</font> 这样的 CSS Selector 来选择它。
比如,我们想在 <font style="color:rgb(45, 132, 172);">id 为 searchtext</font> 的输入框中输入文本 <font style="color:rgb(45, 132, 172);">你好</font> ,完整的Python代码如下
lct = page.locator('#searchtext')
lct.fill('你好')
根据class属性 选择元素的语法是在 class 值 前面加上一个点: <font style="color:rgb(45, 132, 172);">.class值</font>
要选择 class 属性值为 animal的元素 动物,可以这样写
page.locator('.animal')
一个 学生张三 可以定义有 <font style="color:rgb(45, 132, 172);">多个</font> 类型: <font style="color:rgb(45, 132, 172);">中国人</font> 和 <font style="color:rgb(45, 132, 172);">学生</font> 。
<font style="color:rgb(45, 132, 172);">中国人</font> 和 <font style="color:rgb(45, 132, 172);">学生</font> 都是 张三 的 类型。
元素也可以有 <font style="color:rgb(45, 132, 172);">多个class类型</font> ,多个class类型的值之间用 <font style="color:rgb(45, 132, 172);">空格</font> 隔开,比如
<span class="chinese student">张三</span>
注意,这里 span元素 有两个class属性,分别 是 chinese 和 student, 而不是一个 名为 <font style="color:rgb(45, 132, 172);">chinese student</font> 的属性。
我们要用代码选择这个元素,可以指定任意一个class 属性值,都可以匹配到这个元素,如下
page.locator('.chinese')
或者
page.locator('.student')
而不能这样写
page.locator('.chinese student')
如果要表示同时具有两个class 属性,可以这样写
page.locator('.chinese.student')
验证选择元素
那么我们怎么验证 CSS Selector 的语法是否正确选择了我们要选择的元素呢?
当然可以像下面这样,写出Python代码,运行看看,能否操作成功
page.locator('#searchtext').fill('输入的文本')
如果成功,说明选择元素的语法是正确的。
但是这样做的问题就是:太麻烦了。
当我们进行自动化开发的时候,有大量选择元素的语句,都要这样一个个的验证,就非常耗时间。
由于 CSS Selector 是浏览器直接支持的,可以在浏览器 开发者工具栏 中验证。
比如我们使用Chrome浏览器打开 https://www.byhy.net/cdn2/files/selenium/sample1.html
按F12 打开 开发者工具栏, 点击 Elements 标签后, 同时按 Ctrl 键 和 F 键, 就会出现下图箭头处的 搜索框
我们可以在里面输入任何 CSS Selector 表达式 ,如果能选择到元素, 右边的的红色方框里面就会显示出类似 <font style="color:rgb(45, 132, 172);">2 of 3</font> 这样的内容。
<font style="color:rgb(45, 132, 172);">of 后面</font> 的数字表示这样的表达式 <font style="color:rgb(45, 132, 172);">总共选择到几个元素</font>
<font style="color:rgb(45, 132, 172);">of 前面</font> 的数字表示当前黄色高亮显示的是 <font style="color:rgb(45, 132, 172);">其中第几个元素</font>
上图中的 <font style="color:rgb(45, 132, 172);">2 of 3</font> 就是指当前的 选择语法, 在当前网页上共选择到 3 个元素, 目前高亮显示的是第2个。
如果我们输入 <font style="color:rgb(45, 132, 172);">.plant</font> 就会发现,可以选择到3个元素
匹配多个元素
前面已经说, 如果一个 locator表达式匹配多个元素,要获取所有的元素对应的 locator 对象,使用 <font style="color:rgb(45, 132, 172);">all方法</font>
locators = page.locator('.plant').all()
有时,我们只需要得到某种表达式对应的元素数量 ,可以使用 <font style="color:rgb(45, 132, 172);">count方法</font>,如下
count = page.locator('.plant').count()
返回结果就是匹配的元素数量。 可以根据返回结果是否为0 判断元素是否存在
有时,我们只需要得到某种表达式对应的第一个,或者最后一个元素。
可以使用 <font style="color:rgb(45, 132, 172);">first</font> 和 <font style="color:rgb(45, 132, 172);">last</font> 属性 , 如下
lct = page.locator('.plant')
print(lct.first.inner_text(), lct.last.inner_text())
也可以,通过 <font style="color:rgb(45, 132, 172);">nth</font> 方法,获取指定次序的元素,参数0表达第一个, 1 表示第2个,
比如
lct = page.locator('.plant')
print(lct.nth(1).inner_text())
元素内部定位
前面都是通过 <font style="color:rgb(45, 132, 172);">Page</font> 对象调用的 locator 方法, 定位的范围是整个网页。
如果我们想在某个元素内部定位,可以通过 <font style="color:rgb(45, 132, 172);">Locator</font> 对象 调用 locator 方法。
比如
lct = page.locator('#bottom')
# 在 #bottom 对应元素的范围内 寻找标签名为 span 的元素。
eles = lct.locator('span').all()
for e in eles:
print(e.inner_text())
子元素和后端元素的CSS选择
- 直接子元素
- 父元素直接包裹,中间没有任何元素隔开(亲儿子)。
- 后代元素
- 嵌套在父元素内部所有层级的元素(儿子、孙子、重孙全部)。
- PS:后代元素 包含 直接子元素
选择器语法
子元素选择使用 >
后代元素选择使用 [空格] (选择所有子孙)
属性选择
id、class 都是web元素的 <font style="color:rgb(45, 132, 172);">属性</font> ,因为它们是很常用的属性,所以css选择器专门提供了根据 id、class 选择的语法。
那么其他的属性呢?
比如
<a href="http://www.miitbeian.gov.cn">苏ICP备88885574号</a>
里面根据 href选择,可以用css 选择器吗?
当然可以!
css 选择器支持通过任何属性来选择元素,语法是用一个方括号 <font style="color:rgb(45, 132, 172);">[]</font> 。
比如要选择上面的a元素,就可以使用 <font style="color:rgb(45, 132, 172);">[href="http://www.miitbeian.gov.cn"]</font> 。
这个表达式的意思是,选择 属性href值为 <font style="color:rgb(45, 132, 172);">http://www.miitbeian.gov.cn</font> 的元素。
完整代码如下
from playwright.sync_api import sync_playwright
p = sync_playwright().start()
browser = p.chromium.launch(headless=False, slow_mo=50)
page = browser.new_page()
page.goto("https://www.byhy.net/cdn2/files/selenium/sample1.html")
# 根据属性选择元素
element = page.locator('[href="http://www.miitbeian.gov.cn"]')
# 打印出元素文本
print(element.inner_text())
当然,前面可以加上标签名的限制,比如 <font style="color:rgb(45, 132, 172);">a[href="http://www.miitbeian.gov.cn"]</font> 表示 选择所有 标签名为 <font style="color:rgb(45, 132, 172);">a</font> ,且 属性 href值为 <font style="color:rgb(45, 132, 172);">http://www.miitbeian.gov.cn</font> 的元素。
属性值用单引号,双引号都可以。
根据属性选择,还可以不指定属性值,比如 <font style="color:rgb(45, 132, 172);">[href]</font> , 表示选择 所有 具有 属性名 为href 的元素,不管它们的值是什么。
CSS 还可以选择 属性值 <font style="color:rgb(45, 132, 172);">包含</font> 某个字符串 的元素
比如, 要选择a节点,里面的href属性包含了 miitbeian 字符串,就可以这样写
a[href*="miitbeian"]
还可以 选择 属性值 以某个字符串 <font style="color:rgb(45, 132, 172);">开头</font> 的元素
比如, 要选择a节点,里面的href属性以 http 开头 ,就可以这样写
a[href^="http"]
还可以 选择 属性值 以某个字符串 <font style="color:rgb(45, 132, 172);">结尾</font> 的元素
比如, 要选择a节点,里面的href属性以 gov.cn 结尾 ,就可以这样写
a[href$="gov.cn"]
如果一个元素具有多个属性
<div class="misc" ctype="gun">沙漠之鹰</div>
CSS 选择器 可以指定 选择的元素要 同时具有多个属性的限制,像这样 <font style="color:rgb(45, 132, 172);">div[class=misc][ctype=gun]</font>
混合选择
CSS selector的另一个强大之处在于: 选择语法 可以 <font style="color:rgb(45, 132, 172);">联合使用</font>
比如, 我们要选择 如下网页中 html 中的元素 <font style="color:rgb(45, 132, 172);">版权1</font> 对应的 <font style="color:rgb(45, 132, 172);">span</font>
<div id='bottom'>
<div class='footer1'>
<span class='copyright' name='cp1'>版权1</span>
<span class='copyright' name='cp2'>版权2</span>
<span class='copyright1' name='cp1'>版权3</span>
</div>
<div class='footer2'>
<span class='copyright' name='cp1'>版权4</span>
</div>
</div>
CSS selector 表达式 可以这样写:
.footer1 > .copyright[name=cp1]
组选择
注意,之前我们有学到如下的选择法
.plant .animal
这样说选择都含有两个class的元素
而组选择是含有任意一个都会被选中
如果我们要 同时选择所有class 为 plant <font style="color:rgb(45, 132, 172);">和</font> class 为 animal 的元素。怎么办?
这种情况,css选择器可以 使用 <font style="color:rgb(45, 132, 172);">逗号</font> ,称之为 组选择 ,像这样
.plant , .animal
再比如,我们要同时选择所有tag名为div的元素 <font style="color:rgb(45, 132, 172);">和</font> id为BYHY的元素,就可以像这样写
div,#BYHY
对应的Playwright代码如下
elements = wd.find_elements(By.CSS_SELECTOR, 'div,#BYHY')
for element in elements:
print(element.text)
我们再看一个例子
打开这个网址 请点击打开这个网址
我们要选择所有 唐诗里面的作者和诗名, 也就是选择所有 id 为 t1 里面的 <font style="color:rgb(45, 132, 172);">span 和 p 元素</font>
我们是不是应该这样写呢?
#t1 > span,p
不行哦,这样写的意思是 选择所有 <font style="color:rgb(45, 132, 172);">id 为 t1 里面的 span</font> 和 <font style="color:rgb(45, 132, 172);">所有的 p 元素</font>
只能这样写
#t1 > span , #t1 > p
另外注意:组选择结果列表中,选中元素排序, 不是 组表达式的次序, 而是符合这些表达式的元素,在HTML文档中的出现的次序。
按次序选择节点
对应的html如下,关键信息如下
<body>
<div id='t1'>
<h3> 唐诗 </h3>
<span>李白</span>
<p>静夜思</p>
<span>杜甫</span>
<p>春夜喜雨</p>
</div>
<div id='t2'>
<h3> 宋词 </h3>
<span>苏轼</span>
<p>赤壁怀古</p>
<p>明月几时有</p>
<p>江城子·乙卯正月二十日夜记梦</p>
<p>蝶恋花·春景</p>
<span>辛弃疾</span>
<p>京口北固亭怀古</p>
<p>青玉案·元夕</p>
<p>西江月·夜行黄沙道中</p>
</div>
</body>
父元素的第n个子节点
我们可以指定选择的元素 <font style="color:rgb(45, 132, 172);">是父元素的第几个子节点</font>
使用 <font style="color:rgb(45, 132, 172);">nth-child</font>
比如,
我们要选择 唐诗 和宋词 的第一个 作者,
也就是说 选择的是 第2个子元素,并且是span类型
所以这样可以这样写 <font style="color:rgb(45, 132, 172);">span:nth-child(2)</font> ,
如果你不加节点类型限制,直接这样写 <font style="color:rgb(45, 132, 172);">:nth-child(2)</font>
就是选择所有位置为第2个的所有元素,不管是什么类型
父元素的第几个某类型的子节点🏷️
我们可以指定选择的元素 是父元素的第几个 <font style="color:rgb(45, 132, 172);">某类型的</font> 子节点
使用 <font style="color:rgb(45, 132, 172);">nth-of-type</font>
比如,
我们要选择 唐诗 和宋词 的第一个 作者,
可以像上面那样思考:选择的是 第2个子元素,并且是span类型
所以这样可以这样写 <font style="color:rgb(45, 132, 172);">span:nth-child(2)</font> ,
还可以这样思考,选择的是 <font style="color:rgb(45, 132, 172);">第1个span类型</font> 的子元素
所以也可以这样写 <font style="color:rgb(45, 132, 172);">span:nth-of-type(1)</font>
父元素的倒数第几个某类型的子节点🏷️
当然也可以反过来, 选择父元素的 <font style="color:rgb(45, 132, 172);">倒数第几个某类型</font> 的子节点
使用 <font style="color:rgb(45, 132, 172);">nth-last-of-type</font>
像这样
p:nth-last-of-type(2)
奇数节点和偶数节点
如果要选择的是父元素的 <font style="color:rgb(45, 132, 172);">偶数节点</font>,使用 <font style="color:rgb(45, 132, 172);">nth-child(even)</font>
比如
p:nth-child(even)
如果要选择的是父元素的 <font style="color:rgb(45, 132, 172);">奇数节点</font>,使用 <font style="color:rgb(45, 132, 172);">nth-child(odd)</font>
p:nth-child(odd)
如果要选择的是父元素的 <font style="color:rgb(45, 132, 172);">某类型偶数节点</font>,使用 <font style="color:rgb(45, 132, 172);">nth-of-type(even)</font>
如果要选择的是父元素的 <font style="color:rgb(45, 132, 172);">某类型奇数节点</font>,使用 <font style="color:rgb(45, 132, 172);">nth-of-type(odd)</font>
兄弟元素选择
两种方式,+就是选择紧跟后面的元素,~就是后面的所有元素
Playwright推荐定位方式
以上都是根据网页特征进行定位的方式
在官方的优先推荐的方式是基于用户视觉进行定位的,因为HTML写法很容易就会改变
根据文本内容进行定位
有时我们想获取页面包含某些文字的元素, 这用 css selector 不好选择,可以使用 Page/Locator 对象的 get_by_text 方法
比如,点击打开这个网页,
如果要获取 所有 文本内容包含 <font style="color:rgb(45, 132, 172);">11</font> 的元素,就可以这样
from playwright.sync_api import sync_playwright
p = sync_playwright().start()
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto("https://www.byhy.net/cdn2/files/selenium/stock1.html")
# 根据文本内容选择所有元素
elements = page.get_by_text('11').all()
# 打印出元素文本
for ele in elements:
print(ele.inner_text())
运行发现,打印结果为
600111
600113
600115
如果,你希望包含的内容是以 <font style="color:rgb(45, 132, 172);">11</font> 结尾的,就可以使用正则表达式对象 作为参数,如下
import re
elements = page.get_by_text(re.compile("11$")).all()
正则表达式 <font style="color:rgb(45, 132, 172);">11$</font> 表示以 <font style="color:rgb(45, 132, 172);">11</font> 结尾,通过正则表达式,我们可以进行各种复杂的基于文本模式的定位。
关于详细的 Python 正则表达式 的用法,点击这里学习。
元素Role定位
Playwright 支持根据 元素 <font style="color:rgb(45, 132, 172);">角色 role)</font> 定位。
这是个很让人迷糊的定位方式。
什么叫 元素的 <font style="color:rgb(45, 132, 172);">角色</font> ?
web应用现在有一种标准 称之为: <font style="color:rgb(45, 132, 172);">ARIA (Accessible Rich Internet Applications)</font>。
ARIA 根据web界面元素的用途,为这些元素定义了一套 角色( Role ) 信息,添加到页面中,
从而 让 残疾人士,或者 普通人在 某种环境下(比如夜里,太空中),不方便使用常规方法操作应用,使用辅助技术工具,来操作web应用的。
ARIA 为web元素增加了一些 Role相关的属性定义,方便 <font style="color:rgb(45, 132, 172);">辅助技术工具</font> 识别和操作。
比如:当我们完成系统注册时,弹出提示信息,html通常会这样
<div class="alert-message">
您已成功注册,很快您将收到一封确认电子邮件
</div>
但是,这样写的内容不方便辅助技术识别出这是一个重要的信息,需要读给用户听。
这时,可以加上 ARIA role 属性设置,如下
<div class="alert-message" role="alert">
您已成功注册,很快您将收到一封确认电子邮件
</div>
因为 <font style="color:rgb(45, 132, 172);">role="alert"</font> 是 ARIA规范里面的属性, 辅助系统(比如读屏系统)会特别注意,就会侦测到,并且实时读出来。
所以,直接可以根据如下代码 定位该元素
# 根据 role 定位
lc = page.get_by_role('alert')
# 打印元素文本
print(lc.inner_text())
html元素中,有些 特定语义元素(semantic elements)被ARIA规范认定为自身就包含 ARIA role 信息,并不需要我们明显的加上 ARIA role 属性设置,
比如
<progress value="75" max="100">75 %</progress>
就等于隐含了如下信息
<progress value="75" max="100"
role="progressbar"
aria-valuenow="75"
aria-valuemax="100">75 %</div>
所以,直接可以根据如下代码 定位该元素
# 根据 role 定位
lc = page.get_by_role('progressbar')
# 打印元素属性 value 的值
print(lc.get_attribute('value'))
再比如 <font style="color:rgb(45, 132, 172);">search</font> 类型的输入框,默认就有 <font style="color:rgb(45, 132, 172);">searchbox</font> role,
<input type="search">
所以,直接可以根据如下代码 定位该元素
lc = page.get_by_role('searchbox')
print(lc.fill('白月黑羽'))
ARIA Role
Playwright 支持根据 元素 <font style="color:rgb(45, 132, 172);">角色 role)</font> 定位。
这是个很让人迷糊的定位方式。
什么叫 元素的 <font style="color:rgb(45, 132, 172);">角色</font> ?
web应用现在有一种标准 称之为: <font style="color:rgb(45, 132, 172);">ARIA (Accessible Rich Internet Applications)</font>。
ARIA 根据web界面元素的用途,为这些元素定义了一套 角色( Role ) 信息,添加到页面中,
从而 让 残疾人士,或者 普通人在 某种环境下(比如夜里,太空中),不方便使用常规方法操作应用,使用辅助技术工具,来操作web应用的。
ARIA 为web元素增加了一些 Role相关的属性定义,方便 <font style="color:rgb(45, 132, 172);">辅助技术工具</font> 识别和操作。
比如:当我们完成系统注册时,弹出提示信息,html通常会这样
<div class="alert-message">
您已成功注册,很快您将收到一封确认电子邮件
</div>
但是,这样写的内容不方便辅助技术识别出这是一个重要的信息,需要读给用户听。
这时,可以加上 ARIA role 属性设置,如下
<div class="alert-message" role="alert">
您已成功注册,很快您将收到一封确认电子邮件
</div>
因为 <font style="color:rgb(45, 132, 172);">role="alert"</font> 是 ARIA规范里面的属性, 辅助系统(比如读屏系统)会特别注意,就会侦测到,并且实时读出来。
所以,直接可以根据如下代码 定位该元素
# 根据 role 定位
lc = page.get_by_role('alert')
# 打印元素文本
print(lc.inner_text())
html元素中,有些 特定语义元素(semantic elements)被ARIA规范认定为自身就包含 ARIA role 信息,并不需要我们明显的加上 ARIA role 属性设置,
比如
<progress value="75" max="100">75 %</progress>
就等于隐含了如下信息
<progress value="75" max="100"
role="progressbar"
aria-valuenow="75"
aria-valuemax="100">75 %</div>
所以,直接可以根据如下代码 定位该元素
# 根据 role 定位
lc = page.get_by_role('progressbar')
# 打印元素属性 value 的值
print(lc.get_attribute('value'))
再比如 <font style="color:rgb(45, 132, 172);">search</font> 类型的输入框,默认就有 <font style="color:rgb(45, 132, 172);">searchbox</font> role,
<input type="search">
所以,直接可以根据如下代码 定位该元素
lc = page.get_by_role('searchbox')
print(lc.fill('白月黑羽'))
ARIA Attribute
ARIA规范除了可以给元素添加 <font style="color:rgb(45, 132, 172);">ARIA role</font> ,还可以添加其它 ARIA属性(ARIA attributes) ,比如
<div role="heading" aria-level="1">白月黑羽标题1</div>
<div role="heading" aria-level="2">白月黑羽标题2</div>
<font style="color:rgb(45, 132, 172);">aria-level</font> 就是一个 ARIA 属性,表示 role 为 <font style="color:rgb(45, 132, 172);">heading</font> 时的 <font style="color:rgb(45, 132, 172);">等级</font> 信息
上面的定义,其实和下面的 html 元素 h1/h2 等价
<h1>白月黑羽标题1</h1>
<h2>白月黑羽标题2</h2>
<font style="color:rgb(45, 132, 172);">h1</font> 隐含了 <font style="color:rgb(45, 132, 172);">role="heading" aria-level="1"</font> 属性
<font style="color:rgb(45, 132, 172);">h2</font> 隐含了 <font style="color:rgb(45, 132, 172);">role="heading" aria-level="2"</font> 属性
Playwright 对常见的 ARIA 属性 ,有额外的参数对应,
比如 <font style="color:rgb(45, 132, 172);">aria-checked/aria-disabled/aria-expanded/aria-level</font> 等等
上例中,h2 元素,隐含了 <font style="color:rgb(45, 132, 172);">role="heading" aria-level="2"</font>, 所以可以用下面代码定位
lc = page.get_by_role('heading',level=2)
print(lc.inner_text())
Accessible Name
只根据 <font style="color:rgb(45, 132, 172);">ARIA role</font> 和 <font style="color:rgb(45, 132, 172);">ARIA属性</font> 往往并不能唯一定位元素。
role定位最常见的组合是 <font style="color:rgb(45, 132, 172);">ARIA role</font> 和 Accessible Name
因为,<font style="color:rgb(45, 132, 172);">Accessible Name</font> 就像元素的 <font style="color:rgb(45, 132, 172);">名字</font> 一样,往往可以唯一定位。
html 元素标准属性 <font style="color:rgb(45, 132, 172);">name</font> 是浏览器内部的,用户看不到,比如
<a name='link2byhy' href="https://www.byhy.net">白月黑羽教程</a>
<font style="color:rgb(45, 132, 172);">Accessible Name</font> 不一样,它是元素界面可见的文本名,
比如上面的元素,暗含的 <font style="color:rgb(45, 132, 172);">Accessible Name</font> 值就是 <font style="color:rgb(45, 132, 172);">白月黑羽教程</font> , 当然也暗含了 <font style="color:rgb(45, 132, 172);">ARIA role</font> 值为 <font style="color:rgb(45, 132, 172);">link</font>
所以,可以这样定位
lc = page.get_by_role('link',name='白月黑羽教程')
print(lc.click())
上面的写法, 只要 Accessible Name 包含 参数name 的字符串内容即可,而且大小写不分, 并不需要完全一致。
所以,这样也可以定位到
lc = page.get_by_role('link',name='白月黑羽')
如果你需要 Accessible Name 和 参数name 的内容完全一致,可以指定 <font style="color:rgb(45, 132, 172);">exact=True</font> ,如下
lc = page.get_by_role('link',name='白月黑羽', exact=True)
name值还可以通过正则表达式,进行较复杂的匹配规则,比如
lc = page.get_by_role('link',name=re.compile("^白月.*羽"))
那么除了html 元素 a 以外, 哪些元素是自带 Accessible Name 属性的呢?
他们的 Accessible Name 值 又是怎么确定的呢?
w3c有个计算规则文档,很复杂,不容易看懂。
我们这里说一些常见的:
<font style="color:rgb(45, 132, 172);"><a> <td> <button></font> Accessible Name 值 就是其内部的文本内容。
<font style="color:rgb(45, 132, 172);"><textarea> <input></font> 这些输入框,它们的 Accessible Name 值 是和他们关联的 的文本。
比如:
<label>
<input type="checkbox" /> Subscribe
</label>
这个 checkbox 的 Accessible Name 却是 <font style="color:rgb(45, 132, 172);">Subscribe</font> ,应该这样定位
page.get_by_role("checkbox", name="Subscribe")
另外,一些元素 比如 <font style="color:rgb(45, 132, 172);"><img></font> ,它的 Accessible Name 是其html 属性 <font style="color:rgb(45, 132, 172);">alt</font> 的值
比如
<img src="grape.jpg" alt="banana"/>
它的 Accessible Name 值为 <font style="color:rgb(45, 132, 172);">banana</font> ,role 为 <font style="color:rgb(45, 132, 172);">img</font>
其他基于用户视觉的定位
下面的这4种定位,也属于根据用户视觉上的内容定位。
可以通过代码助手产生,其实也完全可以用 css selector 定位替代,了解即可。
根据 元素 placeholder 定位🏷️
<font style="color:rgb(45, 132, 172);">input</font> 元素,通常都有 <font style="color:rgb(45, 132, 172);">placeholder</font> 属性,
可以使用 Page/Locator 对象的 get_by_placeholder 方法,根据 <font style="color:rgb(45, 132, 172);">placeholder</font> 属性值定位。
比如
<input type="text" placeholder="captcha" />
就可以这样定位
page.get_by_placeholder('captcha',exact=True).fill('白月黑羽')
参数 <font style="color:rgb(45, 132, 172);">exact</font> 值为 <font style="color:rgb(45, 132, 172);">True</font> ,表示完全匹配,且区分大小写。如果值为False,就只需包含参数字符串即可,且不区分大小写。
作用类似 get_by_role 里面的 <font style="color:rgb(45, 132, 172);">exact</font> 参数
根据 元素关联的 label 定位🏷️
<font style="color:rgb(45, 132, 172);">input</font> 元素,通常都有关联的 label
可以使用 Page/Locator 对象的 get_by_label 方法,根据 元素关联的 label 定位。
比如
<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")
<font style="color:rgb(45, 132, 172);">get_by_label</font> 也有 <font style="color:rgb(45, 132, 172);">exact</font> 参数,作用和 <font style="color:rgb(45, 132, 172);">get_by_placeholder</font> 里面的 <font style="color:rgb(45, 132, 172);">exact</font> 参数 一样。
根据 元素的 alt text 定位
有些元素,比如 <font style="color:rgb(45, 132, 172);">img</font> 元素,通常都有 <font style="color:rgb(45, 132, 172);">alt</font> 属性
可以使用 Page/Locator 对象的 get_by_alt_text 方法,根据 元素的 <font style="color:rgb(45, 132, 172);">alt</font> 属性值 定位
比如
<img
src="https://doc.qt.io/qtforpython/_images/windows-pushbutton.png"
alt="qt-button">
就可以这样定位
href = page.get_by_alt_text("qt-button").get_attribute('src')
print(href)
<font style="color:rgb(45, 132, 172);">get_by_alt_text</font> 也有 <font style="color:rgb(45, 132, 172);">exact</font> 参数,作用和 <font style="color:rgb(45, 132, 172);">get_by_placeholder</font> 里面的 <font style="color:rgb(45, 132, 172);">exact</font> 参数 一样。
根据 元素 title 定位
有些元素,比如 <font style="color:rgb(45, 132, 172);">span</font>, <font style="color:rgb(45, 132, 172);">a</font> 等等,可能有 <font style="color:rgb(45, 132, 172);">title</font> 属性,当鼠标悬浮在该元素上时,可以显示title属性内在一个提示框里面
可以使用 Page/Locator 对象的 get_by_title 方法,根据 元素的 <font style="color:rgb(45, 132, 172);">title</font> 属性值 定位
比如
<a href="https://www.byhy.net" title="byhy首页">白月黑羽教程</a>
就可以这样定位
page.get_by_title("byhy首页").click()
<font style="color:rgb(45, 132, 172);">get_by_title</font> 也有 <font style="color:rgb(45, 132, 172);">exact</font> 参数,作用和 <font style="color:rgb(45, 132, 172);">get_by_placeholder</font> 里面的 <font style="color:rgb(45, 132, 172);">exact</font> 参数 一样。
等待时间
Playwright 中,当我们定位元素(比如 通过locator/get_by_text 等方法)后,对元素进行操作(比如 click, fill),
如果当时根据定位条件,找不到这个元素, Playwright并不会立即抛出错误, 而是缺省等待元素时间为30秒,在30秒内如果元素出现了,就立即操作成功返回。
比如,点击打开这个网页。
这个网页,我们输入股票名称关键字,点击搜索后, 搜索结果并不是立即返回的,而是有 一定的延时。
前面我们讲 Selenium时,发现如果在定位 id为1的元素不sleep或者设置缺省等待时间, 会报错。
但是,如下代码并没有sleep之类的等待,也没有设置缺省等待时间,为什么在 打印 id为1的元素文本时,不会报错呢?
from playwright.sync_api import sync_playwright
p = sync_playwright().start()
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto("https://www.byhy.net/cdn2/files/selenium/stock1.html")
page.locator('#kw').fill('通讯\n')
page.locator('#go').click()
element = page.locator("[id='1']")
print(element.inner_text())
因为Playwright不需要我们额外设置,本来元素操作时,如果根据定位规则找不到元素,就会等待最多 30秒。
如果我们修改下面的代码
print(element.inner_text())
改为
print(element.inner_text(timeout=10 ))
表示等待元素出现时长修改为10毫秒,再运行,就会有错误了。
如果,我们想修改 <font style="color:rgb(45, 132, 172);">缺省</font> 等待时间, 可以使用 <font style="color:rgb(45, 132, 172);">BrowserContext</font> 对象, 如下
from playwright.sync_api import sync_playwright
p = sync_playwright().start()
browser = p.chromium.launch(headless=False, slow_mo=50)
context = browser.new_context()
context.set_default_timeout(10) #修改缺省等待时间为10毫秒
page = context.new_page() # 通过context 创建Page对象
page.goto("https://www.byhy.net/cdn2/files/selenium/stock1.html")
page.locator('#kw').fill('通讯\n')
page.locator('#go').click()
element = page.locator("[id='1']")
print(element.inner_text())
可以发现缺省等待时间修改为10毫秒,后续操作等待时间无需单独指定,都是10毫秒了。
元素通用操作
获取文本内容
前面我们学过 通过 Locator对象的 inner_text() 方法 可以获取元素的内部文本,
如果Locator选择到的元素是多个,可以使用 all_inner_texts 获取所有匹配的文本,放到列表中返回。
上面者两个方法返回的都是元素内部的 <font style="color:rgb(45, 132, 172);">可见</font> 文本。
html元素内部还可以包括不可见的文本,比如
<p id="source">
<span id='text'>看一下<br>这个内容<br>如何变化</span>
<span style="display:none">隐藏内容</span>
</p>
要获取p元素内部所有内容,包括隐藏内容,可以用 Locator对象的 text_content() 或者 all_text_contents() 方法 获取 <font style="color:rgb(45, 132, 172);">单个</font> 或者 <font style="color:rgb(45, 132, 172);">多个</font> 匹配对象文本
如下所示
lc = page.locator("#source")
print('innerText:', lc.inner_text())
print('--------------')
print('textContent:', lc.text_content())
大家可以对比一下区别
获取元素属性
获取元素的属性值,可以使用 Locator对象的 get_attribute 方法
获取元素内部HTML
获取元素内部的整个html文本, 可以使用 Locator对象的 inner_html 方法
点击
click点击
dbclick双击
悬停
让光标悬停在某个元素上方,可以使用 Locator对象的 hover 方法
等待元素可见
前面讲过,Playwright通过Locator对元素进行操作时,如果元素当前还没有出现,缺省就会等待30秒。
但是,有时我们的代码并不是要操作这个元素,而是要等待这个元素出现后,进行别的操作。
这时,可以使用 Locator对象的 wait_for 方法
比如
page.locator("#source").wait_for()
该方法有个参数 <font style="color:rgb(45, 132, 172);">state</font> ,缺省值为 <font style="color:rgb(45, 132, 172);">'visible'</font> , 就是等待元素可见。
如果值为 <font style="color:rgb(45, 132, 172);">'hidden'</font> 就是等待该元素消失。
等待时长为全局设定的时长, 缺省为 30秒,如果要修改,可以使用参数 <font style="color:rgb(45, 132, 172);">timeout</font>。
超出时长,元素还没有出现在界面上,会抛出错误
判断元素是否可见
有时,我们的自动化代码需要根据当前界面中,<font style="color:rgb(45, 132, 172);">是否存在某些内容</font> ,来决定下一步操作。
这时,可以使用 Locator对象的 is_visible 方法
比如
page.locator("#source").is_visible()
该方法不会等待元素出现,而是立即返回 True 或 False 。
输入框操作
文本框输入
单行文本框 <font style="color:rgb(45, 132, 172);">input</font> 或者 多行文本框 <font style="color:rgb(45, 132, 172);">textarea</font> 都可以使用 Locator对象的 fill 方法进行输入
文本框清空
要清空 单行文本框 <font style="color:rgb(45, 132, 172);">input</font> 或者 多行文本框 <font style="color:rgb(45, 132, 172);">textarea</font> 的内容,都可以使用 Locator对象的 clear 方法
获取输入框里面的文字
如果要获取输入框 <font style="color:rgb(45, 132, 172);"><input></font> , <font style="color:rgb(45, 132, 172);"><textarea></font> 对应的用户输入文本内容,不能用 <font style="color:rgb(45, 132, 172);">inner_text()</font> 方法。
而是应该用 input_value 方法
文件输入框
html中 有文件类型的输入框,用于指定本地文件, 通常用于上传文件
<input type="file" multiple="multiple">
要设置选中的文件,可以使用 Locator 对象的 set_input_files 方法。
比如
# 先定位
lc = page.locator('input[type=file]')
# 单选一个文件
lc.set_input_files('d:/1.png')
# 或者 多选 文件
lc.set_input_files(['d:/1.png', 'd:/2.jpg'])
单选多选框选择
radio单选/checkbox多选
并且按F12,观察HTML的内容
常见的选择框包括: radio框、checkbox框、select框
<font style="color:rgb(45, 132, 172);">radio</font> 是常见的 点选 元素
比如, 下面的 html:
<div id="s_radio">
<input type="radio" name="teacher" value="小江老师">小江老师<br>
<input type="radio" name="teacher" value="小雷老师">小雷老师<br>
<input type="radio" name="teacher" value="小凯老师" checked="checked">小凯老师
</div>
如果要点选 radio框, 可以使用 Locator对象的 check 方法
如果要取消选择 radio框, 可以使用 Locator对象的 uncheck 方法
如果我们要判断 radio框 是否选中,可以使用 Locator对象的 is_checked 方法
假设,我们对上面html中的 radio 输入框
- 先打印当前选中的老师名字
- 再选择 小雷老师
对应的代码如下
# 获取当前选中的元素
lcs = page.locator('#s_radio input[name="teacher"]:checked').all()
teachers = [lc.get_attribute('value') for lc in lcs ]
print('当前选中的是:', ' '.join(teachers))
# 确保点选 小雷老师
page.locator("#s_radio input[value='小雷老师']").check()
<font style="color:rgb(45, 132, 172);">checkbox</font> 是常见的 勾选 元素
比如, 下面的html:
<div id="s_checkbox">
<input type="checkbox" name="teacher" value="小江老师" checked="checked">小江老师<br>
<input type="checkbox" name="teacher" value="小雷老师">小雷老师<br>
<input type="checkbox" name="teacher" value="小凯老师" checked="checked">小凯老师
</div>
和 radio input 一样,
如果要点选 checkbox框, 可以使用 Locator对象的 check 方法
如果要取消选择 checkbox框, 可以使用 Locator对象的 uncheck 方法
如果我们要判断 checkbox 框 是否选中,可以使用 Locator对象的 is_checked 方法
比如, 我们要在前面面的html中
- 先打印当前选中的老师名字
- 再选择 小雷老师
对应的代码如下
# 获取当前选中的元素
lcs = page.locator('#s_checkbox input[name="teacher"]:checked').all()
teachers = [lc.get_attribute('value') for lc in lcs ]
print('当前选中的是:', ' '.join(teachers))
# 点选 小雷老师
page.locator("#s_checkbox input[value='小雷老师']").click()
select元素操作
radio框及checkbox框都是input元素,只是里面的type不同而已。
select框 则是一个新的select标签,大家可以对照浏览器网页内容查看一下
要选择选项,可以使用 <font style="color:rgb(45, 132, 172);">select</font> 元素对应的 Locator对象的 select_option 方法
select单选框
对于 select单选框:
不管原来选的是什么,直接用Select方法选择即可。
例如,选择示例里面的小江老师,示例代码如下
page.locator('#ss_single').select_option('小江老师')
这里 select_option 参数是 选项 <font style="color:rgb(45, 132, 172);">option</font> 元素 的 <font style="color:rgb(45, 132, 172);">value 或者 选项文本</font> , 要完全匹配。
也可以使用关键字参数 <font style="color:rgb(45, 132, 172);">index</font> , <font style="color:rgb(45, 132, 172);">value</font> , <font style="color:rgb(45, 132, 172);">label</font> 指定分别根据 索引,value属性, 选项文本 进行匹配
比如
# 根据 索引 选择, 从0 开始, 但是为0的时候,好像有bug
page.locator('#ss_single').select_option(index=1)
# 根据 value属性 选择
page.locator('#ss_single').select_option(value='小江老师')
# 根据 选项文本 选择
page.locator('#ss_single').select_option(label='小江老师')
# 清空所有选择
page.locator('#ss_single').select_option([])
select多选框
对于select多选框,要选中某几个选项,同样可以使用上面的方法,参数为包含多个值的列表即可
比如
# 根据 value属性 或者 选项文本 多选
page.locator('#ss_multi').select_option(['小江老师', '小雷老师'])
# 根据 value属性 多选
page.locator('#ss_multi').select_option(value=['小江老师', '小雷老师'])
# 根据 选项文本 多选
page.locator('#ss_multi').select_option(label=['小江老师', '小雷老师'])
# 清空所有选择
page.locator('#ss_multi').select_option([])
注意,原来已经选中的选项,没有出现在 参数里面的,自动被清除选择。
获取select选中选项
同样可以通过css selector 表达式 <font style="color:rgb(45, 132, 172);">:checked</font> 伪选择 获取所有选中的 select选项
比如:
page.locator('#ss_multi').select_option(['小江老师','小雷老师'])
lcs = page.locator('#ss_multi option:checked').all_inner_texts()
print(lcs)
网页操作
要 <font style="color:rgb(45, 132, 172);">打开网址/刷新/前进/后退</font> , 可以分别调用 Page 对象的 <font style="color:rgb(45, 132, 172);">goto/reload/go_back/go_forward</font> 方法
要 <font style="color:rgb(45, 132, 172);">获取整个网页对应的HTML</font> , 可以调用 Page 对象的 <font style="color:rgb(45, 132, 172);">content</font> 方法
要 <font style="color:rgb(45, 132, 172);">获取整个网页的标题栏文本</font> , 可以调用 Page 对象的 <font style="color:rgb(45, 132, 172);">title</font> 方法
要 <font style="color:rgb(45, 132, 172);">设置页面大小</font> , 可以调用 Page 对象的 <font style="color:rgb(45, 132, 172);">set_viewport_size</font> 方法
示例代码
# 导入Playwright同步API(新手首选)
from playwright.sync_api import sync_playwright
def main():
# 1. 启动Playwright
with sync_playwright() as p:
# 启动Chromium浏览器,headless=False=显示浏览器窗口(方便看操作)
browser = p.chromium.launch(headless=False)
# 创建新页面
page = browser.new_page()
# ====================== set-viewport-size:设置页面大小(像素) ======================
page.set_viewport_size({"width": 640, "height": 480})
print("✅ 已设置页面大小:640x480像素")
# ====================== goto:打开网址 ======================
page.goto("https://www.baidu.com")
print("✅ 已打开百度首页")
# ====================== title:获取网页标题 ======================
page_title = page.title()
print(f"📝 网页标题:{page_title}")
# ====================== content:获取完整网页HTML ======================
page_html = page.content()
print(f"🌐 网页HTML(前500字符):{page_html[:500]}...")
# ====================== reload:刷新页面 ======================
page.reload()
print("🔄 已刷新页面")
# ====================== 测试 前进/后退 功能 ======================
# 先打开第二个网页(制造历史记录)
page.goto("https://www.sina.com.cn")
print("✅ 已打开新浪首页")
# go_back:后退(回到上一个页面:百度)
page.go_back()
print("⬅️ 已后退到百度首页")
# go_forward:前进(回到下一个页面:新浪)
page.go_forward()
print("➡️ 已前进到新浪首页")
# 等待3秒,观察操作效果
page.wait_for_timeout(3000)
# 关闭浏览器
browser.close()
if __name__ == "__main__":
main()
界面切换
frame切换
如果我们要 选择 下图方框中 所有的 蔬菜,使用css选择,怎么写表达式?
当然,要先查看到它们的html元素特征
大家可能会照旧写出如下代码:
from playwright.sync_api import sync_playwright
p = sync_playwright().start()
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto("https://www.byhy.net/cdn2/files/selenium/sample2.html")
lcs = page.locator('.plant').all()
for lc in lcs:
print(lc.inner_text(timeout=1000))
运行一下,你就会发现,运行结果打印内容为空白,说明没有选择到 class 属性值为 plant 的元素。
为什么呢?
因为仔细看,你可以发现, 这些元素是在一个叫 <font style="color:rgb(45, 132, 172);">iframe</font> 的 元素中的。
这个 iframe 元素非常的特殊, 在html语法中, <font style="color:rgb(45, 132, 172);">frame</font> 元素 或者 <font style="color:rgb(45, 132, 172);">iframe</font> 元素的内部 会包含一个 被嵌入的 另一份html文档。
在我们使用 Playwright 打开一个网页时, 操作范围 缺省是当前的 html , 并不包含被嵌入的html文档里面的内容。
如果我们要 定位/操作 被嵌入的 html 文档 中的元素, 就必须 <font style="color:rgb(45, 132, 172);">切换操作范围</font> 到 被嵌入的文档中。
怎么切换呢?
使用 Page 或者 Locator 对象的 <font style="color:rgb(45, 132, 172);">frame_locator</font> 方法定位到你要操作的frame。
这个 方法会产生一个 <font style="color:rgb(45, 132, 172);">FrameLocator</font> 对象,后续的定位,就使用这个对象,在其内部进行定位。
像这样
# 产生一个 FrameLocator 对象
frame = page.frame_locator("iframe[src='sample1.html']")
# 再 在其内部进行定位
lcs = frame.locator('.plant').all()
for lc in lcs:
print(lc.inner_text(timeout=1000))
其中, <font style="color:rgb(45, 132, 172);">frame_locator</font> 方法的参数是 css 或者 xpath selector
窗口切换
在网页上操作的时候,我们经常遇到,点击一个链接 或者 按钮,就会打开一个 <font style="color:rgb(45, 132, 172);">新窗口</font> 。
在打开的网页中,点击 链接 <font style="color:rgb(45, 132, 172);">访问bing网站</font> , 就会弹出一个新窗口,访问bing网址。
如果我们用 Playwright 写自动化程序 在新窗口里面 打开一个新网址, 并且去自动化操作新窗口里面的元素,会有什么问题呢?
我们可以运行如下代码验证一下
from playwright.sync_api import sync_playwright
p = sync_playwright().start()
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto("https://www.byhy.net/cdn2/files/selenium/sample3.html")
# 点击链接,打开新窗口
page.locator("a").click()
# 打印网页窗口标题
print(page.title())
运行完程序后,最后一行 打印当前窗口的标题栏 文本, 输出内容是
白月黑羽测试网页3
问题就在于,即使新窗口打开了, 这时候,我们的 page 变量对应的 还是老窗口,自动化操作也还是在老窗口进行,
如果我们要到新的窗口里面操作,该怎么做呢?
这时,需要使用 BrowserContext 对象。如下
from playwright.sync_api import sync_playwright
pw = sync_playwright().start()
browser = pw.chromium.launch(headless=False)
# 创建 BrowserContext 对象
context = browser.new_context()
# 通过context 创建page
page = context.new_page()
page.goto("https://www.byhy.net/cdn2/files/selenium/sample3.html")
# 点击链接,打开新窗口
page.locator("a").click()
# 等待2秒, 不能用 time.sleep
page.wait_for_timeout(2000)
# pages属性是 所有窗口对应Page对象的列表
newPage = context.pages[1]
# 打印新网页窗口标题
print(newPage.title())
# 打印老网页窗口标题
print(page.title())
<font style="color:rgb(45, 132, 172);">BrowserContext</font> 对象有个 <font style="color:rgb(45, 132, 172);">pages</font> 属性,这是一个列表,里面依次为所有窗口对应Page对象。
我们就可以通过不同的page对象操作对应的不同窗口了。
如果自动化打开了很多链接窗口,不知道目标窗口的次序号,这时可以根据标题栏定位到要操作的page
那么我们就可以通过 类似下面的代码,
for pg in context.pages:
# 得到该窗口的标题栏字符串,判断是不是我们要操作的那个窗口
if '必应' in pg.title():
break
print(pg.title())
设置当前tab
如果当前界面有很多窗口,要把某个窗口作为当前活动窗口显示出来,可以调用该窗口对应的Page对象的 bring_to_front 方法。
关闭网页窗口
前面我们学过Browser对象有close 方法,那是关闭整个浏览器。
如果只是要关闭某个网页窗口,可以调用该窗口对应的Page对象的 close 方法。
冻结页面操作
有些网站上面的元素, 我们鼠标放在上面,会动态弹出一些内容。
比如,百度首页的导航栏,有个 更多 选项
如果我们把鼠标放在上边,就会弹出 一个更多产品内容的图标页 。
如果我们要用 Playwright 自动化 点击 其中一个产品,比如 <font style="color:rgb(45, 132, 172);">音乐</font> ,就需要 F12 查看这个元素的特征。
但是 当我们的鼠标 从 <font style="color:rgb(45, 132, 172);">音乐</font> 移开, 这个 栏目就整个消失了, 就没法 查看 其对应的 HTML。
怎么办?
可以如下图所示:
在 开发者工具栏 console 里面执行如下js代码
setTimeout(function(){debugger}, 5000)
这句代码什么意思呢?
表示在 5000毫秒后,执行 debugger 命令
执行该命令会 浏览器会进入debug状态。 debug状态有个特性, 界面被冻住, 不管我们怎么点击界面都不会触发事件。
所以,我们可以在输入上面代码并回车 执行后, 立即 鼠标放在界面 右上角 更多产品处。
这时候,就会弹出 下面的 图标。
然后,我们仔细等待 5秒 到了以后, 界面就会因为执行了 debugger 命令而被冻住。
然后,我们就可以点击 开发者工具栏的 查看箭头, 再去 点击 <font style="color:rgb(45, 132, 172);">音乐</font> 图标 ,查看其属性了。
截屏
要 <font style="color:rgb(45, 132, 172);">整个网页</font> 截屏,使用 Page 对象的 screenshot 方法。
比如
# 截屏当前页面可见内容,保存到当前工作目录下的ss1.png文件中
page.screenshot(path='ss1.png')
# 截屏 完整页面,页面内容长度超过窗口高度时,包括不可见部分。
page.screenshot(path='ss1.png', full_page=True)
也可以只对 <font style="color:rgb(45, 132, 172);">某个元素的显示内容</font> 进行截屏,使用 Locator 对象的 screenshot 方法。
比如
page.locator('input[type=file]').screenshot(path='ss2.png')
拖拽
要实现拖拽功能,可以使用Page对象的 drag_and_drop 方法。
比如,对下面这段 html
<span id="t1">t1</span>
<span id="t2">t2</span>
<form >
<div id="captcha">
</div>
<input type="text" placeholder="captcha" />
<button type="submit">Submit</button>
</form>
要选中 <font style="color:rgb(45, 132, 172);">span#t1</font> 文本内容,并且拖拽到 输入框 <font style="color:rgb(45, 132, 172);">[placeholder="captcha"]</font> 里面去,可以使用如下代码:
# 选中 `span#t1` 文本内容
page.locator('#t1').select_text()
# 拖拽到 输入框 `[placeholder="captcha"]` 里面去
page.drag_and_drop('#t1', '[placeholder="captcha"]')
drag_and_drop 方法的:
第1个参数是被拖动元素的 css selector 或者 xpath selector , 如果可以匹配页面多个元素,取第一个匹配到的元素
第1个参数是拖动目标元素的 css selector 或者 xpath selector, 如果可以匹配页面多个元素,取第一个匹配到的元素
如果被拖动元素的Locator对象已经产生,可以直接调用其 drag_to 方法 进行拖动
上例中,代码也可以这样写
# 选中 `span#t1` 文本内容
lc = page.locator('#t1')
lc.select_text()
# 拖拽到 输入框 `[placeholder="captcha"]` 里面去
lc.drag_to(page.locator('[placeholder="captcha"]'))
注意, drag_to 的参数是 目标元素的 Locator 对象 , 而不是 selector 表达式字符串
弹出对话框
有的时候,我们经常会在操作界面的时候,出现一些弹出的对话框。
分别点击界面的3个按钮,你可以发现:
弹出的对话框有三种类型,分别是 <font style="color:rgb(45, 132, 172);">alert(警告信息)</font> 、 <font style="color:rgb(45, 132, 172);">confirm(确认信息)</font> 和 <font style="color:rgb(45, 132, 172);">prompt(提示输入)</font>
Alert
Alert 弹出框,目的就是显示通知信息,只需用户看完信息后,点击 OK(确定) 就可以了。
那么,自动化的时候,代码怎么模拟用户点击 OK 按钮呢?
可以这样
from playwright.sync_api import sync_playwright
pw = sync_playwright().start()
browser = pw.chromium.launch(headless=False)
page = browser.new_page()
page.goto("https://www.byhy.net/cdn2/files/selenium/test4.html")
# 处理 弹出对话框 的 回调函数
def handleDlg(dialog):
# 等待1秒
page.wait_for_timeout(1000)
# 点击确定
dialog.accept()
# 打印 弹出框 提示信息
print(dialog.message)
# 设置弹出对话框事件回调函数
page.on("dialog", handleDlg )
# 点击 alert 按钮
page.locator('#b1').click()
处理函数中被回调时,会传入 Dialog 对象
这个对象的
<font style="color:rgb(45, 132, 172);">accept</font> 方法作用等同于点击确定按钮
<font style="color:rgb(45, 132, 172);">dismiss</font> 方法作用等同于点击取消按钮
<font style="color:rgb(45, 132, 172);">message</font> 属性就是对话框界面的提示信息字符串
注意:
- 注册的处理函数中一定要调用
<font style="color:rgb(45, 132, 172);">accept</font>或者<font style="color:rgb(45, 132, 172);">dismiss</font>方法,让对话框消失。
否则当对话框弹出时,后续任何代码都不会执行,并且会有超时错误。
比如
from playwright.sync_api import sync_playwright
pw = sync_playwright().start()
browser = pw.chromium.launch(headless=False)
page = browser.new_page()
page.goto("https://www.byhy.net/cdn2/files/selenium/test4.html")
# 处理 弹出对话框 的 回调函数
def handleDlg(dialog):
print('不调用accept')
page.on("dialog", handleDlg )
#点击 alert 按钮
page.locator('#b1').click()
print('后续代码')
会发现,点击alert 按钮之后的后续代码不会执行
- Playwright 在界面有弹出框时,发现如果没有任何注册的处理函数,会自动点击取消
比如
# 注释掉 事件回调函数
# page.on("dialog", handleDlg )
#点击 alert 按钮
page.locator('#b1').click()
print('点击1次')
page.wait_for_timeout(1000)
#点击 alert 按钮
page.locator('#b1').click()
print('点击2次')
会发现,对话框自动取消
Confirm
Confirm弹出框,主要是让用户确认是否要进行某个操作。
比如:当管理员在网站上选择删除某个账号时,就可能会弹出 Confirm弹出框, 要求确认是否确定要删除。
Confirm弹出框 有两个选择供用户选择,分别是 <font style="color:rgb(45, 132, 172);">OK</font> 和 <font style="color:rgb(45, 132, 172);">Cancel</font> , 分别代表 <font style="color:rgb(45, 132, 172);">确定</font> 和 <font style="color:rgb(45, 132, 172);">取消</font> 操作。
那么,自动化的时候,代码怎么模拟用户点击 <font style="color:rgb(45, 132, 172);">确定</font> 或者 <font style="color:rgb(45, 132, 172);">取消</font> 按钮呢?
前面说过,处理函数中被回调时,会传入 Dialog 对象
这个对象的
<font style="color:rgb(45, 132, 172);">accept</font> 方法作用等同于点击确定按钮
<font style="color:rgb(45, 132, 172);">dismiss</font> 方法作用等同于点击取消按钮
比如,前面的代码改为
from playwright.sync_api import sync_playwright
pw = sync_playwright().start()
browser = pw.chromium.launch(headless=False)
page = browser.new_page()
page.goto("https://www.byhy.net/cdn2/files/selenium/test4.html")
def handleDlg(dialog):
page.wait_for_timeout(1000)
dialog.dismiss() # 点击取消
page.on("dialog", handleDlg)
# 点击 confirm 按钮
page.locator('#b2').click()
input('....')
会发现打印出来的是 <font style="color:rgb(45, 132, 172);">取消操作</font>
Prompt
出现 Prompt 弹出框 是需要用户输入一些信息,提交上去。
比如:当管理员在网站上选择给某个账号延期时,就可能会弹出 Prompt 弹出框, 要求输入延期多长时间。
怎么办呢?
Dialog 对象,这个对象的 <font style="color:rgb(45, 132, 172);">accept</font> 方法可以输入参数字符串,作为要输入的信息
比如,前面的代码改为
from playwright.sync_api import sync_playwright
pw = sync_playwright().start()
browser = pw.chromium.launch(headless=False)
page = browser.new_page()
page.goto("https://www.byhy.net/cdn2/files/selenium/test4.html")
def handleDlg(dialog):
page.wait_for_timeout(1000)
dialog.accept('你好啊') # 输入信息,并确定
page.on("dialog", handleDlg)
# 点击 confirm 按钮
page.locator('#b3').click()
input('....')

浙公网安备 33010602011771号