Playwright爬虫
安装
pip install playwright
# 安装驱动, 支持的浏览器:cr, chromium, ff, firefox, wk 和 webkit
playwright install
控制台启动录制
playwright codegen [options] [url]
- -o, --output :保存生成脚本
- –target :生成的脚本语言,可以设置javascript, test, python, python-async和csharp,默认为python
- -b, --browser :要使用的浏览器,可以选择cr, chromium, ff, firefox, wk和webkit,默认chromium。
- –channel :chromium版本,比如chrome, chrome-beta, msedge-dev等
- –color-scheme :模拟器的颜色主题,可选择light 或者 dark样式
- –device :模拟的设备
- –save-storage :保存上下文状态,用于保存cookies 和localStorage,可用它来实现重用。例如playwright codegen --save-storage=auth.json
- –load-storage :加载–save-storage 保存的数据,重用认证数据。
- –proxy-server :指定代理服务器
- –timezone
- –geolocation :指定地理位置坐标
- –lang :指定语言/地区,比如中国大陆:zh-CN
- –timeout :超时时间,定位毫秒,默认10000ms
- –user-agent :用户代理
- –viewport-size :浏览器窗口大小
- -h, --help :查看帮助信息
例如:
playwright codegen -o test_playwright.py --target python -b chromium --device="iPhone 12 Pro" https://www.baidu.com/
playwright open https://www.baidu.com/ # 默认使用Chromium打开
playwright wk https://www.baidu.com/ # 使用WebKit打开
playwright open --device="iPhone 12 Pro" https://www.baidu.com/ # 使用iPhone 12 Pro模拟器打开
基本用法
常见配置参数
headless,slow_mo,viewport,locale,timezone,color_scheme,geolocation,user_agent, timeout, proxy
同步模式
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
# 创建一个浏览器实例; headless:是否无头;slow_mo放慢执行速度
# pixel_2 = playwright.devices['Pixel 2'] # Pixel 2 一款安卓手机
proxy_ip = {
'server': 'http://',
'username': '',
'password': '',
}
browser = p.chromium.launch(headless=False, slow_mo=100, proxy=proxy_ip)
context = browser.new_context(
viewport={'width': 1800, 'height': 800}, # 窗口大小
locale='zh-CN', #语言zh-CN/en-EN
timezone='Europe/Rome', #时区
color_scheme='dark', # 颜色
geolocation={"longitude": 48.858455, "latitude": 2.294474} # 地理位置
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36', # 浏览器,
timeout=10000, # 超时
# **pixel_2,
)
# 创建两个浏览器上下文
page = browser.new_page()
page.goto('http://www.baidu.com')
print(page.title)
browser.close()
异步模式
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("http://www.baidu.com")
print(await page.title())
await browser.close()
asyncio.run(main())
防止WebDriver 被检测
js = """
Object.defineProperties(navigator,{webdriver:{get:()=>undefined}};
"""
page.add_init_script(js)
或者
page.add_init_script("""Object.defineProperties(navigator, {webdriver:{get:()=>undefined}});""")
获取源码,文本,属性
获取源码: .content()
page.wait_for_load_state('networkidle')
html = page.content()
获取文本: .text_content()
# ul->li下
brand = element.query_selector('text=品牌:').text_content()
name = ele_items.query_selector('section > div._3KXtu._3jY37 > a').text_content()
获取属性: .get_attribute()
# ul->li下
link = element.query_selector('h5 a').get_attribute('href')
wait_for_load_state
"commit ": 接收到网络响应且文档开始加载时(仅显示了页面默认窗口视图下的元素)
"domcontentloaded": 认为在 DOMContentLoaded 事件完成时(显示了完整页面)
"load": 在 load 事件完成时操作完成(含了所有图片资源)
"networkidle": 至少 500 毫秒内没有网络连接时操作完成
页面加载的整个状态变化
Commit -> DOMContentLoaded -> load -> networkidle
监听 response 事件:page.on()
from playwright.sync_api import sync_playwright
def on_response(response):
if '/api/movie/' in response.url and response.status == 200:
print(response.json())
print(f'Statue {response.status}:{response.url}')
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
# Page 就是单独的一个浏览器 tab 标签
page = browser.new_page()
page.on('response', on_response)
# page.on('response', lambda response: on_response(response, product_data, id))
page.goto('https://spa6.scrape.center/')
page.wait_for_load_state('networkidle')
browser.close()
# 监听弹窗
with page.expect_popup() as popup:
page.evaluate('window.open()')
popup.value.goto('http://www.baidu.com')
# 监听请求
with page.expect_request('**/*login*.png') as first:
page.goto('http://www.baidu.com')
print(first.value.url)
传参监听
page.on('response', lambda response: on_response(response, id))
for (url, id) in urls:
pass
滚动
下拉滚动条
page.evaluate("var q=document.documentElement.scrollTop=15000")
鼠标滚动
page.mouse.wheel(0,7000)
截图
with sync_playwright() as p:
browser = p.chromium.launch(headless=False, slow_mo=50)
page = browser.new_page()
page.goto("http://www.baidu.com")
page.screenshot(path="example.png")
browser.close()
获取cookie
browser = playwright.chromium.launch(headless=False)
context = browser.new_context()
cookies = context.storage_state()
cookie = '; '.join([f'{key["name"]}={key["value"]}' for key in cookies['cookies']])
CSS,xpath选择器
t伪类选择器
has-text():检测包含(返回找到的所有元素)
text():检测等于(返回第一个找到的元素)
# 选择文本是 Log in 的节点,并点击
page.click("text=Log in",timeout=5000)
page.click("text=你好,请登录")
page.locator(':has-text("All products")').click()
page.locator("#nav-bar :text('Contact us')").click()
page.locator('[data-test=login-button]').click()
page.locator("[aria-label='Sign in']").click()
# 选择 id 为 nav-bar 子孙节点 class 属性值为 contact-us-item,并点击
page.click("#nav-bar .contact-us-item")
# 选择文本中包含 Playwright 的 article 节点
page.click("article:has-text('Playwright')")
# 选择 id 为 nav-bar 节点中文本值等于 Contact us 的节点
page.click("#nav-bar :text('Contact us')")
# 选择 class 为 item-description 的节点,且该节点还要包含 class 为 item-promo-banner 的子节点
page.click(".item-description:has(.item-promo-banner)")
# 择的就是一个 input 节点,并且该 input 节点要位于文本值为 Username 的节点的右侧
page.click("input:right-of(:text('Username'))")
# xpath
page.click("xpath=//button")
get_by_xxx定位器
●page.get_by_text(文本,**kwargs)按文本内容定位。
●page.get_by_role(角色,**kwargs)按角色属性
●page.get_by_label(文本,**kwargs)通过关联标签的文本查找表单控件
● page.get_by_test_id(test_id)根据元素的属性定位元素(可以配置其他属性)
page.get_by_placeholder(文本,**kwargs)按占位符查找输入
● 通过其文本替代来定位元素,通常是图像。
●page.get_by_title(文本,**kwargs)按标题定位元素。
page.get_by_label("Password").fill("secret-password")
page.get_by_role("option", name="全部企业").click()
page.get_by_role("button", name="Sign in").click()
# 关闭详情弹窗
page.frame_locator("internal:attr=[title=\"详情页\"i]").locator(
"#enterprise-details-close").click()
# 文本内容
page.get_by_text(str(select_text)).click()
# 正则匹配定位
page.get_by_role("tab", name=re.compile("风险信息", re.IGNORECASE)).click()
循环遍历ul: query_selector_all()
uls = page.query_selector_all('//*[@id="YZhV9-anchor"]//table[@class="ant-table-fixed"]/tbody/tr')
for ele_items in uls:
title = ele_items.query_selector('section > div._3KXtu._3jY37 > a').text_content()
同级第几个:.nth(2)
点击最后一个按钮
page.click("button >> nth=-1")
page.get_by_placeholder("请输入手机号码").nth(1).click()
文本输入:.fill()
# 标签定位输入
page.locator('text=First Name').fill('Peter')
page.get_by_placeholder("请输入手机号码").nth(1).fill('12345678901')
定位器过滤器:filter
page.locator("a").filter(has_text="密码登录").click()
刷新,前进,后退
page.reload(**kwargs) # 刷新
page.go_back(**kwargs) # 后退
page.go_forward(**kwargs) # 前进
等待
# 等待直到title元素被加载完全
page.locator("title").wait_for()
# 会自动等待按钮加载好再执行点击
page.locator("button", has_text="sign up").click()
# Playwright 会等待 #search 元素出现在 DOM 中
page.fill('#search', 'query')
# Playwright 会等待元素停止动画并接受点击
page.click('#search')
# 等待 #search 出现在 DOM 中
page.wait_for_selector('#search', state='attached')
# 等待 #promo 可见, 例如具有 `visibility:visible`
page.wait_for_selector('#promo')
# 等待 #details 变得不可见, 例如通过 `display:none`.
page.wait_for_selector('#details', state='hidden')
# 等待 #promo 从 DOM 中移除
page.wait_for_selector('#promo', state='detached')
# 随机等待
page.wait_for_timeout(random.uniform(2500, 4500))
点击
- 左键点击:page.click(“id=su”)
- 点击元素左上角:page.click(‘id=su’, position={‘x’: 0, ‘y’: 0})
- Shift + click:page.click(“id=su”, modifiers=[‘Shift’])
- 强制点击:page.click(“id=su”, force=True)
- 右键点击:page.click(“id=su”, button=‘right’)
- 双击:page.dblclick(“id=su”)
- 悬停在元素上:page.hover(‘id=su’)
模拟键盘输入
page.press("id=kw", 'Control+A'):Control+A
page.press('id=kw', 'Enter'):点击回车
# 一个字符一个字符的输入
page.type("id=kw", "playwright", delay=100): 每个字符延迟100ms输入
参考:
https://huaweicloud.csdn.net/63802f5edacf622b8df864ec.html#devmenu22
https://blog.csdn.net/u010698107/article/details/121070336