之前有见同事用过playwright进行浏览器模拟操作,但是没有仔细了解,今天去详细看了下,发现playwright着实比selenium牛逼多了
-
Playwright 相对于selenium优点
1、自动下载chromnium, 无需担心chrome升级对应版本问题;
2、支持录屏操作,然后直接生成python代码;
3、支持网路拦截、事件监听,纵使我们是模拟浏览器,但是依旧可以在request请求上作文章,这个就很牛逼;
4、代码简洁性更高;
5、支持异步处理; -
安装使用
#下载第三方库 pip3 install playwright #安装好后,进行初始化操作,它会自动安装chromium, Firefox , webkit浏览器并配置一些驱动 playwright install
-
录屏操作
1、使用控制台(terminal,cmd, powershell),进入到项目目录下;
2、查看所有的录屏相关命令:playwright codegen -h
3、启动示例:#-o 非必须,输出代码到指定文件,如果不指定,也可以在界面中复制代码就好 #-b 非必须,指定浏览器, 默认是chromnium #--device #非必须, 指定机器型号 #www.baidu.com #非必须, 指定访问的url playwright codegen -o luping.py -b chromnium --device="iphone 13" www.baidu.com #--save-storage 保存浏览器缓存,可以自定义指定项目名称 playwright codegen -b cr --save-storage=saiying www.saleyee.cn #--load-storage 获取已有的浏览器缓存,指定好项目名称就好 playwright codegen -b cr --load-storage=saiying www.saleyee.cn
4、录屏时,直接人工操作就好了,也可以暂停录屏,至于如何获取元素内容,需要自己探究;
5、退出录屏,只要在控制台ctrl+C
6、然后把代码改一改,就可以使用了,这个还是很方便的;- 使用代码示例
事件监听: "close"
:页面关闭事件。"console"
:页面上发生控制台日志事件。"crash"
:页面崩溃事件。"dialog"
:页面弹出对话框事件。"domcontentloaded"
:DOM 内容加载完成事件。"download"
:页面上触发下载事件。"filechooser"
:页面弹出文件选择对话框事件。"frameattached"
:子框架附加到页面事件。"framedetached"
:子框架从页面分离事件。"framenavigated"
:子框架导航事件。"load"
:页面加载完成事件。"pageerror"
:页面上发生错误事件。"popup"
:页面弹出弹出窗口事件。"request"
:页面发出网络请求事件。"requestfailed"
:页面上的请求失败事件。"requestfinished"
:页面上的请求完成事件。"response"
:页面接收到网络响应事件。"websocket"
:页面上发生 WebSocket 事件。"worker"
:页面上发生 Worker 事件。
- 使用代码示例
# -*- coding: utf-8 -*-
import asyncio
import time
from urllib import parse
from playwright.async_api import async_playwright
from playwright.sync_api import sync_playwright
#!!!!!!!!!!!!!!响应事件response监听, 数据采集的逻辑可以在这里做
def on_response(response):
if 'api/movie/' in response.url and response.status == 200:
# print(f'Statue {response.status}: {response.url}')
print(response.json())
#请求事件request监听,只能获取相关的请求参数,用处不大,
def on_request(request):
print(request.url)
#!!!下载事件监听,比如店铺后台的表格下载就可以在此处理
def on_download(download):
print(f"Download started: {download.url}")
#!!!!!!!!!!!!!网络劫持
#可以实现网络劫持和修改操作,比如非加密,通过相应参数修改,获取自定义返回的内容,从而采集
#它就相当于一个网络钩子,可以修改请求信息, 还可以设置保持浏览器上url不变的情况下,跳转其他站点页面
def handle_request(route, request):
url = request.url
if url == "http://example.com":
if route.request.method == "GET":
print(f"GET请求url为:{route.request.url}")
bits = list(parse.urlparse(url))
qs = parse.parse_qs(bits[4])
qs["key1"] = ["GET"] # 此处替换key1的值
bits[4] = parse.urlencode(qs, True)
url = parse.urlunparse(bits)
print(f"改变后的url为: {url}\n")
route.continue_(url=url)
elif route.request.method == "POST":
print(f"POST请求入参为:{route.request.post_data}")
text_list = route.request.post_data.split("&")
for i in range(len(text_list)):
text_item = text_list[i]
if "key1=" in text_item:
text_list[i] = "key1=POST" # 此处替换key1的值
print(f"改变后的数据为: {'&'.join(text_list)}\n")
route.continue_(post_data="&".join(text_list))
# 同步
# 使用with打开服务可以避免中间代码执行报错导致无法正常关闭浏览器进程
with sync_playwright() as p:
# browser_type_list = [p.chromium, p.firefox, p.webkit]
for browser_type in [p.chromium]:
browser = browser_type.launch(headless=False)
#=======!!!可以指定项目名称,例(saiying),这样可以继承上一次浏览器缓存,这样可以规避下重复登录的问题, 但是不清楚没有该项目名称情况下会有什么问题
# 也可以不指定创建一个新的context = browser.new_context()
# 如果使用该种方式,记得流程结束后,要再保留下当前缓存context.storage_state(path="saiying"), 在下面有示例
# context = browser.new_context(storage_state="saiying")
# page = context.new_page()
#直接生成新浏览器执行对象
page = browser.new_page()
#事件监听----start
page.on('response', on_response) #响应事件监听
page.on('request', on_request) #发起请求事件监听
page.on('download', on_download) # 文件下载事件监听
# 事件监听----end
#网络拦截---start
route_url = 'http://127.0.0.1/zentao/testcase-create-1-0-0.html' #可以指定拦截url,或者部分定制url
#route_url = '**' #指定全部的url
#route_url = '**/*.json' #指定部分url
page.route(route_url, handle_request) # 监听添加用例接口
# 网络拦截---end
page.goto('https://spa6.scrape.center/') #请求网页地址
page.wait_for_load_state('networkidle') #等待页面所有请求响应完毕,还有load, domcontentloaded
element = page.query_selector('xpath=//a') #单节点定位
elements = page.query_selector_all('xpath=//a') # 多节点定位
for element in elements:
print(element.inner_html()) #打印该节点下所有标签内容
print(element.get_attribute('href'))
element.query_selector('xpath=.//img')
print(page.title())
#=======!!! 在登录完毕,或者操做完毕后,保留当前缓存,供下次调用
# context.storage_state(path="saiying")
# context.close()
page.close()
browser.close() #关闭浏览器
# #异步, 感觉用处不大
# async def main():
# async with async_playwright() as p:
# # browser_type_list = [p.chromium, p.firefox, p.webkit]
# for browser_type in [p.chromium]:
# browser = await browser_type.launch(headless=False)
# page = await browser.new_page()
# await page.goto('https://www.baidu.com')
# print(await page.title())
# await browser.close()
#
# asyncio.run(main())