|
|
一、安装
- uv add playwright
- uv add pytest-playwright
- playwright install
- #只安装指定浏览器示例
- playwright install chromium
复制代码
二、最小示例(同步 API)
sync_api适合简单脚本;项目推荐异步API性能更好。
- from playwright.sync_api import sync_playwright
- def run():
- with sync_playwright() as p:
- # 启动浏览器,headless=False显示浏览器窗口
- browser = p.chromium.launch(headless=False)
- page = browser.new_page()
- page.goto("https://www.baidu.com")
- print(page.title())
- page.screenshot(path="baidu.png")
- browser.close()
- if __name__ == "__main__":
- run()
复制代码
三、异步API(推荐生产使用)
- 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("https://www.baidu.com")
- print(await page.title())
- await page.screenshot(path="baidu_async.png")
- await browser.close()
- asyncio.run(main())
复制代码
四、浏览器启动常用参数
- browser = p.chromium.launch(
- headless=False, # True无头模式不弹出窗口
- slow_mo=500, # 每个动作延迟ms,方便调试
- args=[
- "--no-sandbox",
- "--disable-setuid-sandbox",
- "--user-agent=Mozilla/5.0 xxx"
- ],
- channel="chrome", # 使用本机安装的Chrome,不用内置chromium
- )
复制代码
五、页面Page对象核心API
访问页面
- await page.goto(
- url,
- timeout=30000, # 超时毫秒
- wait_until="networkidle" # load|domcontentloaded|networkidle
- )
复制代码
networkidle:网络空闲,适合等待 js 渲染完毕。
等待机制(非常重要,不要写 time.sleep)
playwright 自带自动等待,元素操作前自动等待元素可交互。
手动等待:
- # 等待选择器出现
- await page.wait_for_selector("#search-input", timeout=10000)
- # 等待页面跳转完成
- await page.wait_for_url("**/result*")
- # 等待函数条件返回True
- await page.wait_for_function("() => document.title.length > 0")
复制代码
定位元素 Locator(官方推荐,代替旧的 page.query_selector)
Locator 是惰性定位,不会立刻查询 DOM,动作执行时才查找。
- # css选择器
- loc = page.locator("#kw")
- # xpath
- loc = page.locator("//input[@id='kw']")
- # 文本匹配
- loc = page.locator("text=百度一下")
- # 精确文本
- loc = page.locator("text='百度一下'")
- # 多个元素
- all_items = page.locator(".item").all()
- count = await page.locator(".item").count()
复制代码
元素操作
- # 输入文本
- await page.locator("#kw").fill("playwright教程")
- # 点击
- await page.locator("#su").click()
- # 双击
- await loc.dblclick()
- # 悬停
- await loc.hover()
- # 获取文本
- text = await page.locator("#result").text_content()
- # 获取属性
- attr = await page.locator("a").get_attribute("href")
- # 获取inner_html
- html = await page.locator("#main").inner_html()
- # 勾选复选框
- await page.locator("#agree").check()
- # 下拉选择
- await page.locator("#select").select_option("value1")
- # 键盘按键
- await page.keyboard.press("Enter")
- await page.keyboard.type("hello")
复制代码
六、截图、PDF
- # 可视区域截图
- await page.screenshot(path="screen.png")
- # 完整长页面截图
- await page.screenshot(path="full.png", full_page=True)
- # 导出PDF(仅chromium headless)
- await page.pdf(path="out.pdf", format="A4")
复制代码
七、处理弹窗、对话框
- page.on("dialog", lambda dialog: dialog.accept())
- await page.evaluate("alert('hello')")
复制代码
八、网络拦截、请求监听
监听请求响应
- def handle_request(req):
- print(f"req:{req.method} {req.url}")
- def handle_response(resp):
- if resp.status == 200:
- print(resp.url)
- page.on("request", handle_request)
- page.on("response", handle_response)
复制代码
拦截修改请求,屏蔽图片 /css 加速爬虫
- await page.route("**/*.{png,jpg,jpeg,gif}", lambda route: route.abort())
- # 修改post请求
- async def modify_req(route):
- headers = route.request.headers.copy()
- headers["token"] = "xxxx"
- await route.continue_(headers=headers)
- await page.route("**/api/login", modify_req)
复制代码
九、执行 JS 脚本
- # 无参数
- res = await page.evaluate("() => document.title")
- # 传参数
- res = await page.evaluate("(a,b)=> a+b", 10,20)
- # 在元素上执行js
- loc = page.locator("#id")
- val = await loc.evaluate("el => el.value")
复制代码
十、多页面、多标签
- # 新建tab
- page2 = await browser.new_page()
- await page2.goto("https://github.com")
- # 监听页面弹出
- new_page_coro = page.wait_for_event("page")
- await page.locator("text=打开新窗口").click()
- new_tab = await new_page_coro
复制代码
十一、上下文 Context:模拟浏览器环境,保存 cookie、本地存储
不要每次 new_page,优先使用 browser_context,隔离会话。
- context = await browser.new_context(
- viewport={"width":1280,"height":720},
- user_agent="Mozilla/5.0 ...",
- locale="zh-CN",
- geolocation={"latitude":22.54,"longitude":114.06},
- permissions=["geolocation"]
- )
- # 加载cookie
- context.add_cookies([
- {"name":"sessionid","value":"xxx","domain":".baidu.com","path":"/"}
- ])
- page = await context.new_page()
- # 保存cookie
- cookies = await context.cookies()
复制代码
持久化上下文(保存登录状态,下次免登录)
- context = await browser.new_context(storage_state="state.json")
- # 执行登录操作
- await context.storage_state(path="state.json")
复制代码
下次运行直接加载storage_state="state.json",cookie/localStorage 直接恢复。
十二、iframe 处理
- # 通过frame定位
- frame = page.frame_locator("iframe#myframe")
- await frame.locator("#input-inside-iframe").fill("test")
复制代码
十三、文件上传下载
文件上传
- # input type="file"
- await page.locator("input[type='file']").set_input_files(["a.jpg","b.jpg"])
复制代码
文件下载
- async with page.expect_download() as download_info:
- await page.locator("text=下载文件").click()
- download = await download_info.value
- await download.save_as("./down.zip")
复制代码
十四、反爬相关配置
1. 关闭检测特征
- context = await browser.new_context(
- extra_http_headers={"Accept-Language":"zh-CN,zh;q=0.9"}
- )
- # 隐藏webdriver标记
- await page.add_init_script("""
- Object.defineProperty(navigator, 'webdriver', {get: () => undefined})
- """)
复制代码
2. 使用slow_mo减慢操作速度,模拟真人
3. 尽量使用locator,不要大量 evaluate 硬写 js
4. 代理设置
- browser = await p.chromium.launch(proxy={
- "server":"http://127.0.0.1:7890"
- })
复制代码
十五、异常与调试技巧
1. 开启 trace 录制,生成可视化调试报告
- await context.tracing.start(screenshots=True, snapshots=True, sources=True)
- # 自动化操作......
- await context.tracing.stop(path="trace.zip")
复制代码
运行:playwright show-trace trace.zip打开网页调试,可以回放每一步操作。
2. 断点暂停
执行会打开 playwright inspector 调试器,可以直接复制 locator 选择器。
3. 常见报错
- TimeoutError:元素没出现,检查选择器、检查 wait_until,确认页面是否动态渲染。
- 沙箱报错 linux 环境:启动参数加args=["--no-sandbox"]
十六、实战小案例:异步爬虫抓取百度搜索结果
- import asyncio
- from playwright.async_api import async_playwright
- async def search_baidu(keyword):
- async with async_playwright() as p:
- browser = await p.chromium.launch(headless=True, args=["--no‑sandbox"])
- context = await browser.new_context()
- page = await context.new_page()
- await page.goto("https://www.baidu.com", wait_until="networkidle")
- await page.locator("#kw").fill(keyword)
- await page.locator("#su").click()
- await page.wait_for_selector(".result")
- items = await page.locator(".result").all()
- res = []
- for item in items:
- title = await item.locator("h3").text_content()
- href = await item.locator("a").get_attribute("href")
- res.append({"title":title,"href":href})
- await browser.close()
- return res
- if __name__ == "__main__":
- data = asyncio.run(search_baidu("playwright python教程"))
- for d in data:
- print(d)
复制代码
十七、单元测试 pytest-playwright
直接写测试用例,fixture 自动提供 page 对象。
- def test_baidu(page):
- page.goto("https://www.baidu.com")
- assert "百度" in page.title()
复制代码
运行:
- pytest test_demo.py --browser chromium --headed
复制代码
十八、核心最佳实践总结
1. 优先使用 Locator,不要用query_selector,locator 自动重试,更稳定。
2. 禁止time.sleep(),使用 playwright 内置 wait_for_* 等待。
3. 使用BrowserContext隔离会话,方便持久化登录状态 storage_state。
4. 爬虫场景:拦截图片、css 资源减少带宽;必要配置代理。
5. 调试优先用page.pause()或者 trace 录制。
6. 大规模并发使用异步 API,不要同步。 |
|