目录
1.安装
pip install playwright -i https://pypi.tuna.tsinghua.edu.cn/simple
2.初始化
playwright install
3.基本使用
Playwright 支持两种模式:同步、异步。
同步模式:
import time
from playwright.sync_api import sync_playwright
if __name__ == '__main__':
# 浏览器上下文管理器
with sync_playwright() as p:
# 启动谷歌浏览器(关闭无头模式)
driver = p.chromium.launch(headless=False)
# 新建页面
page = driver.new_page()
# 访问网址
page.goto('https://www.baidu.com')
print(page.title())
page.fill('xpath=//input[@id="kw"]', '杜兰特')
page.click('xpath=//input[@id="su"]')
# 关闭驱动
time.sleep(5)
driver.close()
异步模式:
import asyncio
from playwright.async_api import async_playwright
async def main():
# 浏览器上下文管理器
async with async_playwright() as p:
# 启动谷歌浏览器(关闭无头模式)
driver = await p.chromium.launch(headless=False)
# 新建页面
page = await driver.new_page()
# 访问网址
await page.goto('https://www.baidu.com')
title = await page.title()
print(title)
# 关闭驱动
await driver.close()
asyncio.run(main())
注意:with 用于上下文对象的管理,它可以返回一个上下文管理器,也就对应一个 PlaywrightContextManager 对象,无论运行期间是否抛出异常,它能够帮助我们自动分配并且释放 Playwright 的资源。
4.代码生成
Terminal执行:
playwright codegen -o script.py
操作浏览器后,右边会生产相应的代码预览
关闭浏览器后,会在同级目录生成script.py代码 ,可以直接执行。
5.选择器
import time
from playwright.sync_api import sync_playwright
if __name__ == '__main__':
# 浏览器上下文管理器
with sync_playwright() as p:
# 启动谷歌浏览器(关闭无头模式)
driver = p.chromium.launch(headless=False)
# 新建页面
page = driver.new_page()
# 访问网址
page.goto('https://www.baidu.com')
print(page.title())
# xpath选择器(推荐,不同的模拟器有不同的写法,但是xpath都是相通的,一招鲜吃遍天)
page.fill('xpath=//input[@id="kw"]', '杜兰特')
# ID选择器
page.click('#su')
time.sleep(2)
# class选择器
page.fill('.s_ipt', '欧文')
# 文本选择器
page.click('text=百度一下')
# 关闭驱动
time.sleep(2)
driver.close()
6.事件监听
Page 对象提供了一个 on 方法,它可以用来监听页面中发生的各个事件,比如 close、console、load、request、response 等等。一个典型的用法就是用来监听页面的ajax请求响应:
import time
import cchardet
from playwright.sync_api import sync_playwright, Response
# 监听Response回调函数
def handle_response(response: Response):
detect = cchardet.detect(response.body())
if detect['encoding']:
print(response.body().decode(detect['encoding']))
if __name__ == '__main__':
# 浏览器上下文管理器
with sync_playwright() as p:
# 启动谷歌浏览器(关闭无头模式)
driver = p.chromium.launch(headless=False)
# 新建页面
page = driver.new_page()
# 访问网址
page.goto('https://spa6.scrape.center/')
print(page.title())
# 监听网络请求的Response
page.on('response', handle_response)
# 等待网络请求加载完毕
page.wait_for_load_state('networkidle')
time.sleep(2)
driver.close()
6.正则表达式
很多时候,点击的url后面会附带参数,这个参数是变动的,这是后需要用正则去匹配
最典型的情况是等待一个页面加载:page.wait_for_url(http://xxxxx?id=123456)
从源码可以看出我们是可以传入正则表达式的:![](https://img-blog.csdnimg.cn/89b3463f9e3945568d005ad6c61bf83f.png)
使用示例:
page.wait_for_url(re.compile("https://www.abcd.xyz/web/index[\s\S]*"))
7.滑动翻页
page.locator("body").press("PageDown")
8.并发
import asyncio
import time
from playwright.async_api import async_playwright
async def run(url):
async with async_playwright() as playwright:
# create a chromium browser instance
chromium = playwright.chromium
# browser = await chromium.launch()
# create a bowser instance which headless is true
browser = await chromium.launch(headless=False)
# create two isolated browser contexts
user_context = await browser.new_context()
page = await user_context.new_page()
await page.goto(url)
await page.fill('xpath=//input[@id="kw"]', '杜兰特')
await page.click('xpath=//input[@id="su"]')
time.sleep(10)
# await page2.goto('https://www.baidu.com')
async def main():
tasks = []
urls = ['https://www.baidu.com', 'https://www.baidu.com', 'https://www.baidu.com']
for url in urls:
task = asyncio.ensure_future(run(url))
tasks.append(task)
await asyncio.gather(*tasks)
if __name__ == '__main__':
asyncio.run(main())
9.实例
"""
同步模式
"""
import time
from playwright.sync_api import sync_playwright, Page, Locator
def handle_business(page: Page, url):
"""
逻辑处理
:param page: 页面实例
:param url: 访问地址
:return:
"""
page.goto(url)
# 定位输入框
"""
定位!
1.xpath定位:【搜索框】locator_input = page.locator('xpath=//input[@id="kw"]')
2.元素属性定位:【搜索框】locator_input = page.locator('[name="wd"]')后者locator_input = page.locator('[class="s_ipt"]')
3.本文定位:【搜索按钮】locator_submit_button = page.locator('text="百度一下"')
4.id定位:【搜索按钮】locator_submit_button = page.locator('#su')
5.CSS定位:略
6.伪类①:is【如果定位不到text="百度一下",则定位#su】locator_submit_button = page.locator(':is([text="百度一下"]),#su')
7.伪类②:has
8.伪类③:hans-text
"""
locator_input = page.locator('[class="s_ipt"]', ).filter()
"""
页面操作!
1.page.goto("https://example.com") # 前往页面
2.page.go_back(**kwargs) #前进
3.page.go_forward(**kwargs) # 后退
4.page.screenshot(path="screenshot.png") 截图
5.print(page.title()) # 打印当前页标题
6.print(page.url()) # 打印当前页URL
7.page.reload(**kwargs) # 重新加载当前页面
"""
# 填写
locator_input.fill('SpringBoot')
# 定位提交按钮
locator_submit_button: Locator = page.locator(':is([text="百度一下"]),#su')
# 鼠标单击
locator_submit_button.click()
"""
等待元素!
1.等待元素:page.wait_for_selector(selector="[class='page-item_M4MDr pc']", strict=False, state="visible", timeout=5000)
state:
- `attached` - 等待元素出现在DOM中
- `detached` - 等待元素消失在DOM中
- `visible` - 等待元素可访问
- `hidden` - 等待元素隐藏
strict:断言选择器元素唯一。
2.等待固定时间:page.wait_for_timeout(1000)
3.等待状态:page.wait_for_load_state(state="networkidle")
state:
- `domcontentloaded` - 等待DOM加载完毕
- `load` - 等待加载事件完毕
- `networkidle` - 等待网络闲置至少500ms(即网络加载完毕)
"""
page.wait_for_load_state(state="networkidle")
"""
元素操作!
1.文本输入:locator_input.fill('SpringBoot')
2.获取节点属性:attribute = page.get_attribute("#su", "type")
3.点击:locator_submit_button.click()或者page.click("#su")
"""
time.sleep(50)
if __name__ == '__main__':
# 浏览器上下文管理器
with sync_playwright() as p:
# 创建浏览器驱动
browser = p.chromium.launch(headless=False)
# 创建浏览器实例
context = browser.new_context()
# 创建页面实例
page = context.new_page()
handle_business(page, 'https://www.baidu.com/')
10.使用模板
import time
from playwright.sync_api import sync_playwright
if __name__ == '__main__':
# 浏览器上下文管理器
with sync_playwright() as p:
# 启动谷歌浏览器(关闭无头模式)
driver = p.chromium.launch(headless=False, args=["--start-maximized"])
# 全屏
context = driver.new_context(no_viewport=True)
# 新建页面
page = context.new_page()
# 这里使用生成器生成!!!!TODO
# 关闭驱动
time.sleep(10)
context.close()
driver.close()
11.使用经验
①最好不要去手写,太影响效率,最好是使用生成器,然后复制进模板进行修改。
②生成器生成的代码,往往无法直接使用,因为很多定位器是动态的,无法使用,我们需要把动态的定位器换成固定的定位器。
③有些元素只有悬停才会出现,我们可以使用hover()
page1.get_by_role("link", name="系统维护").hover()
④有些元素找不到、定位不到,可能是加载太慢了,我们可以先用xpath定位,等待元素出现
# 先等待元素
page1.wait_for_selector(
selector='//div[@class="am-modal-dialog" and @style="width: 540px; height: 150px;"]//span[@class="am-modal-btn"]')
# 再获取元素
selector = page1.query_selector(
selector='//div[@class="am-modal-dialog" and @style="width: 540px; height: 150px;"]//span[@class="am-modal-btn"]')
# 最后操作元素
selector.click()
⑤在循环里面做好异常捕获,并在捕获中将界面打回新一轮的起点。
⑥有时候跑得太快,下拉框没有选择进去,可以多执行几次。
page1.frame_locator("iframe[style=\"min-height:322px\"]").locator(
"select[name=\"orgCategory\"]").select_option(
f"{number}")
page1.frame_locator("iframe[style=\"min-height:322px\"]").locator(
"select[name=\"orgCategory\"]").select_option(
f"{number}")
page1.frame_locator("iframe[style=\"min-height:322px\"]").locator(
"select[name=\"orgCategory\"]").select_option(
f"{number}")
page1.get_by_text("确定").click()
12.饶过检测
正常使用如下:
import time
from playwright.sync_api import sync_playwright
if __name__ == '__main__':
# 浏览器上下文管理器
with sync_playwright() as p:
# 启动谷歌浏览器(关闭无头模式)
driver = p.chromium.launch(headless=False)
# 新建页面
page = driver.new_page()
# 访问网址
page.goto('https://bot.sannysoft.com/')
# 关闭驱动
time.sleep(60)
# driver.close()
可以看到明显的报红
使用内核与使用浏览器始终会有点区别。为了解决这个问题,可以在启动之前执行一下JS文件。
如果有梯子则直接执行下边的代码,如果没有梯子,则要去下载到本地加载执行。
import time
import requests
from playwright.sync_api import sync_playwright
def getBypassJs():
# 最好是在线获取,因为是实时更新的。但是前提是要有梯子。否则在线可能获取不到该文件。
response = requests.get("https://raw.githubusercontent.com/requireCool/stealth.min.js/main/stealth.min.js")
js = response.content.decode('utf-8')
# 如果没有梯子,则去下载下来,从本地读取。
# with open('./stealth.min.js', 'r') as f:
# js = f.read()
return js
if __name__ == "__main__":
js: str = getBypassJs()
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.add_init_script(js)
page.goto("https://bot.sannysoft.com/")
time.sleep(10)
page.close()
13.定位iframe嵌套
page.goto("https://qzone.qq.com/")
page.wait_for_load_state()
# 获取第一层iframe元素页面
iframe = page.frame_locator('#login_frame')
# 获取所需元素
selector = iframe.locator('xpath=//a[@id="switcher_plogin"]')
selector.click()
time.sleep(1)
iframe.locator('input[id="u"]').fill("16234895@qq.com")
time.sleep(1)
iframe.locator('input[id="p"]').fill("1234567890")
time.sleep(1)
iframe.locator('input[id="login_button"]').click()
time.sleep(5)
# 定位第二次嵌套的iframe页面
iframe_captcha = iframe.frame_locator('#tcaptcha_iframe_dy')
# 获取所需元素
condition = iframe_captcha.locator('span[id="instructionText"]').text_content()
gb_div = iframe_captcha.locator('div[id="slideBg"]')
# 获取元素的边界信息
element_box = gb_div.bounding_box()
ab_path_dir = os.path.join(os.getcwd(), f"screenshot{uuid.UUID}.png")
print(f"截图存放路径{ab_path_dir}")
# 指定截图区域
screenshot = page.screenshot(
path=ab_path_dir, # 保存截图的文件路径
clip={
'x': element_box['x'],
'y': element_box['y'],
'width': element_box['width'],
'height': element_box['height']
}
)
14.元素减法与排除
场景:无法准确定位到所需元素,但是能够定位到全部元素集合和非需元素集合,需要将二者相减才能得到所需元素。相减原理:利用元素的坐标点坐标是否相同,来判断是不是同一个元素。
# 获取元素下所有按钮
buttons_all = choice_locator.query_selector_all('xpath=//button[@type="button"]')
# 获取元素下所有干扰按钮
buttons_Interference = choice_locator.query_selector_all('xpath=//div[string-length(@data-id) = 11 and translate(@data-id, "0123456789", "") = ""]//button[@type="button"]')
# 获取所有干扰元素的坐标点
interference_point = [(b.bounding_box()['x'], b.bounding_box()['y']) for b in
buttons_Interference]
判断根据坐标点是否相同来排除干扰元素。
# 真正元素下的按钮
buttons = []
for button in buttons_all:
# 对比每个按钮的坐标点位置来判断
# 获取元素的边界信息
element_box = button.bounding_box()
x = element_box['x']
y = element_box['y']
if (x, y) not in interference_point:
buttons.append(button)
15.获取元素的父级节点、祖先节点。
element_handle与locator对象的相互转化。
# 从 query_selector 到 locator
element_handle = page.query_selector('h1')
if element_handle:
locator = page.locator('h1')
text = locator.text_content()
print(f"Text from ElementHandle to Locator: {text}")
# 从 locator 到 query_selector
locator = page.locator('h1')
element_handle = locator.element_handle()
if element_handle:
text = element_handle.text_content()
print(f"Text from Locator to ElementHandle: {text}")
获取父级、祖级元素
i:Locator
i.element_handle().query_selector("xpath=..").text_content() # 父级
i.element_handle().query_selector("xpath=../..").text_content() # 父父级
i.element_handle().query_selector("xpath=../../..").text_content() # 父父父级
i:ElementHandle
i.query_selector("xpath=../../..").text_content()
16.多个定位表达式进行或操作
selector_all = self.page.query_selector_all(
'button[type="button"]:has-text("去完成"), button[type="button"]:has-text("已完成")')
17.多属性条件表达式
page.query_selector_all('div[itemprop="author"][itemtype="http://schema.org/Person"].AuthorInfo')