玩命加载中🤣🤣🤣

playwright-python


👍👍 快速上手 - 白月黑羽

快速上手

安装

原理

image-20250613093652732

可以先安装第三方库

# 使用清华大学源
pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple

# 如果你在公司使用代理上网,pip安装可能出现SSL证书校验错误,可以加上信任参数 --trusted-host ,如下所示
pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple  --trusted-host pypi.tuna.tsinghua.edu.cn

pip install playwright

playwright install chromium

quick start

from playwright.sync_api import sync_playwright

# 启动 playwright driver 进程
p = sync_playwright().start()

# 启动浏览器,返回 Browser 类型对象
browser = p.chromium.launch(headless=False, executable_path='C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe')

# 创建新页面,返回 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()      # 点击查询

page.wait_for_timeout(2000)

# 打印所有搜索内容
lcs = page.locator(".result-item").all()
for lc in lcs:
    print(lc.inner_text())

input('close....')
# 关闭浏览器
browser.close()
input('4....')
# 关闭 playwright driver 进程
p.stop()

如果是自定义浏览器路径

browser = p.chromium.launch(headless=False, executable_path='C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe')

with的一种写发

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()

    page.wait_for_timeout(2000)

    # 打印所有搜索内容
    lcs = page.locator(".result-item").all()
    for lc in lcs:
        print(lc.inner_text())

    input('close...')
    browser.close()

自动化编程

在CMD中执行

# 可能需要全路径
playwright.exe codegen

image-20250613093709144

接下来所有的操作都会被控制台记录下来,如下:

import re
from playwright.sync_api import Playwright, sync_playwright, expect


def run(playwright: Playwright) -> None:
    browser = playwright.chromium.launch(headless=False)
    context = browser.new_context()
    page = context.new_page()
    page.goto("https://www.byhy.net/cdn2/files/selenium/stock1.html")
    page.get_by_role("textbox", name="股票名称").click()
    page.get_by_role("textbox", name="股票名称").fill("通讯")
    page.get_by_role("button", name="查询").click()

    # ---------------------
    context.close()
    browser.close()


with sync_playwright() as playwright:
    run(playwright)

跟踪功能

代码示例如下:

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()

核心api(打开快照):

context.tracing.start(snapshots=True, sources=True, screenshots=True)

执行代码后,会发现同级目录下存在 trace.zip 文件,查看该文件有2中方法:

image-20250613093718990

元素的定位

CSS选择器

# 根据id定位
page.locator('#kw')

# 根据class定位
page.locator(".result-item")

注意:如果一个标签有多个属性,那么只能选择一个

<span class="chinese student">张三</span>

指定任意 class 属性

page.locator('.chinese')
## 或者
page.locator('.student')
## 或者
page.locator('.chinese.student')

## 不能这样写
page.locator('.chinese student')

元素的验证

xpath 的验证一样

匹配多个元素

如果存在多个元素需要进行选择

from playwright.sync_api import sync_playwright

p = sync_playwright().start()
browser = p.chromium.launch(headless=False)
context = browser.new_context()
page = context.new_page()

page.goto('https://www.byhy.net/cdn2/files/selenium/sample1.html')

lct = page.locator('.animal')
print('第一个', lct.first.inner_text())
print('第二个', lct.nth(1).inner_text())
print('最后一个', lct.last.inner_text())


input('即将关闭浏览器...')
browser.close()
p.stop()

Locator 内部定位

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.byhy.net/cdn2/files/selenium/sample1.html')
    # 定位到bottom
    lct = page.locator('#bottom')
    print(lct.locator('span').all_inner_texts())

    input('浏览器即将退出...')
    page.close()

控制台效果

PS E:\code\ecoding_test\20250608_playwright> & C:/Python/Python313/python.exe e:/code/ecoding_test/20250608_playwright/3_元素定位_匹配内部元素.py
['版权', '发布日期:2018-03-03', '备案号 苏ICP备88885574号']
浏览器即将退出...

可以看到存到了数组中

子元素和后代元素

# 子元素
#bottom > div.footer2 > span

# 后代元素(中间可能隔了一些元素)
#bottom span

比如刚才的代码的改造

lct = page.locator('#bottom')
print(lct.locator('span').all_inner_texts())

## 可以改造成
lct = page.locator('#bottom span')
print(lct.all_inner_texts())

缺省等待时间

playwright 会默认等待 30s,也可以自行定义

print(element.inner_text(timeout=10000))

如果响整体修改缺省时间

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    context = browser.new_context()
    # 整体设置等待缺省时间 10s
    context.set_default_timeout(10000)
    page = context.new_page()

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

    page.locator('#kw').fill('python')
    page.click('#su')
    input('浏览器即将关闭...')
    browser.close()

界面操作

元素通用操作

获取隐藏文本

lct = page.locator('#source')
lct.inner_text() # 获取文本内容(不包括隐藏的内容)
lct.text_context() #获取文本内容(包括隐藏的内容)

获取属性

lct = page.locator('#source')
lct.get_attribute('href') # 获取属性

获取元素内部 Html

lct = page.locator('#source')
lct.inner_html() # 获取内部 html

点击

lct = page.locator('#source')
lct.click() # 单击
lct.dbclick() # 双击
lct.hover() # 悬停

等待

page.locator('#dynamic').wait_for()
# 然后抓取
lc = page.locator('#dynamic + div')
print(lc.inner_text())

判断元素是否可见

page.locator('#source').is_visible()
## 注意:is_visible 不会等待,立刻返回界面是否存在该元素,wait_for 会等待时间

输入框操作

输入&清空

page.locator('#text').clear()
page.locator('#text').fill('xxx')

获取输入框里面的文字

page.locator('#text').input_value()

文件输入框

# 定位
lc = page.locator('input[type=file]')

# 单选一个文件
lc.set_input_files('d:/1.png')

# 或者 多选 文件
lc.set_input_files(['d:/1.png', 'd:/2.jpg'])

radio,如下面的 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>
# 先打印所有老师的名字
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()
# 取消选择
page.locator('#s_radio input[value="小雷老师"]').uncheck()
# 判断是否选中
page.locator('#s_radio input[value="小雷老师"]').is_checked()

checkbox,同 radio 一样

select单选框

不管原来选的是什么,直接使用 select 方法选择即可

page.locator('#ss_single').select_option('小江老师')
# 这里 select_option 参数是 选项 option 元素 的 value 或者 选项文本,要完全匹配

也可以使用关键字参数 index , value , label 指定分别根据 索引,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多选框

# 根据 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选中选项

page.locator('#ss_multi').select_option(['小江老师','小雷老师'])

lcs = page.locator('#ss_multi option:checked').all_inner_texts()
print(lcs)

网页操作

打开网址/刷新/前进/后退 , 可以分别调用 Page 对象的 goto/reload/go_back/go_forward 方法

获取 title,可以调用 page 对象的 title 方法

frame/tab 切换等技巧

iframe 切换

# 产生一个  FrameLocator 对象
frame = page.frame_locator("iframe[src='sample1.html']")

# 再 在其内部进行定位
lcs = frame.locator('.plant').all()
for lc in lcs:
    print(lc.inner_text(timeout=1000))

对比 selenium,playwright无需嵌套进入,直接一键进入

窗口切换

窗口切换

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    context = browser.new_context()
    # 整体设置等待缺省时间 10s
    context.set_default_timeout(10000)
    page = context.new_page()

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

    page.locator('#kw').fill('python')
    # 点击搜索
    page.click('#su')
    # 点击 第一个搜索标题
    page.click('//h3[1]//a')
    # 等2s
    page.wait_for_timeout(2000)
    # 新窗口的page
    newPage = context.pages[1]
    # 打印新窗口的窗口标题
    print(newPage.title())
    
    input('浏览器即将关闭...')
    browser.close()

如果确定不了序号,那么就通过 title 来选择

for pg in  context.pages:
    # 得到该窗口的标题栏字符串,判断是不是我们要操作的那个窗口
    if 'python' in pg.title():
        break

print(pg.title())   

brint_to_front

如果当前界面有很多窗口,要把某个窗口作为当前活动窗口显示出来,可以调用该窗口对应的Page对象的 bring_to_front 方法。

close

page.close()

冻结界面

在浏览器控制台输入

setTimeout(function(){debugger}, 5000)

截屏

# 截屏当前页面可见内容,保存到当前工作目录下的ss1.png文件中
page.screenshot(path='ss1.png')

# 截屏 完整页面,页面内容长度超过窗口高度时,包括不可见部分。👍👍
page.screenshot(path='ss1.png', full_page=True)

完整demo

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('#kw').fill('python')
    # 点击搜索
    page.click('#su')
     # 等待网络空闲状态
    page.wait_for_load_state('networkidle')
    # 截屏
    page.screenshot(path='ss1.png')
    page.screenshot(path='ss2.png', full_page=True)

    input('浏览器即将关闭...')
    browser.close()

拖拽

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')

    input('输入回车继续...')
    # 选中百度热搜的文本
    page.locator('.title-content-title').first.select_text()
    input('输入回车继续...')
    # 拖拽到搜索框中
    page.drag_and_drop('.title-content-title', '#kw')

    input('浏览器即将关闭...')
    browser.close()

或者使用 locator 中的方法

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')

    input('输入回车继续...')
    # 选中百度热搜的文本
    lc = page.locator('.title-content-title').first
    # 选中文本
    lc.select_text()
    input('输入回车继续...')
    # 拖拽到搜索框中
    lc.drag_to(page.locator('#kw'))

    input('浏览器即将关闭...')
    browser.close()

弹出对话框

alert

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 对象

confirm

accept 方法作用等同于点击确定按钮

dismiss 方法作用等同于点击取消按钮

message 属性就是对话框界面的提示信息字符串

dialog.dismiss() # 取消

prompt

dialog.accept('hello world') # 输入文本

其他技巧

窗口最大化及debug开发

使用pycharm并使用debug启动

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(args=['--start-maximized'], headless=False)
    context = browser.new_context(no_viewport=True)
    page = context.new_page()
    page.goto('https://www.baidu.com')
    # 先进行高亮显示, 再进行debug计算编写
    page.locator('#kw').highlight()

wait_for_load_state() 的状态

  • “load” 等待页面所有资源加载完成
  • “domcontentloaded” 等待DOM内容加载完成
  • “networkidle” 等待网络空闲
page.wait_for_load_state('load')
page.wait_for_load_state('domcontentloaded')
page.wait_for_load_state('networkidle')

文章作者: 👑Dee👑
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 👑Dee👑 !
  目录