Selenium 装驱动太麻烦?Playwright 入门指南:Python 网页自动化新标准
一、开头痛点:驱动、等待、玄学报错
做过网页自动化的同学,大概率被这三件事折磨过:
- 装驱动:
chromedriver的版本必须和本机 Chrome 严丝合缝,浏览器一自动升级,脚本第二天就SessionNotCreatedException。 - 等元素:页面还没渲染完就
find_element,报NoSuchElementException;于是满屏time.sleep(3),慢且不稳。 - 换浏览器:Selenium 要分别配 Chrome、Firefox、Edge 的驱动,环境一换全得重来。
Playwright 把这三点一次性解决了:自带浏览器(一条命令下载,版本永远匹配)、操作自带自动等待(不用手写 sleep)、一套 API 通吃 Chromium / Firefox / WebKit。它由微软团队维护,Python 版 API 设计得很干净,写起来比 Selenium 顺手不少。
二、环境准备:两条命令
Playwright 支持 Python 3.8+。同样建议用虚拟环境隔离:
python-mvenv .venvsource.venv/bin/activate# Windows 用 .venv\Scripts\activate# 第一步:装 Python 包pipinstallplaywright# 第二步:下载浏览器内核(约 100~300MB,只需执行一次)playwrightinstallchromium第二步是很多新手卡住的地方——它下载的是 Playwright 自带的浏览器,不是你桌面上那个 Chrome,所以不存在版本对不上的问题。国内下载慢可以走镜像:
# 使用国内镜像加速下载PLAYWRIGHT_DOWNLOAD_HOST=https://cdn.npmmirror.com/binaries/playwright playwrightinstallchromium验证安装:
# check_env.pyfromplaywright.sync_apiimportsync_playwrightwithsync_playwright()asp:browser=p.chromium.launch(headless=True)page=browser.new_page()page.goto("https://example.com")print("页面标题:",page.title())browser.close()能打印出Example Domain就说明环境没问题。
三、最小可运行示例:打开页面并截图
Playwright 有同步和异步两套 API,日常脚本用同步版最省心:
# shot.pyfromplaywright.sync_apiimportsync_playwrightwithsync_playwright()asp:# headless=True 表示无界面运行;调试时改成 False 能看到真实浏览器browser=p.chromium.launch(headless=True)page=browser.new_page(viewport={"width":1280,"height":800})page.goto("https://www.python.org/")# full_page=True 会截取整页长图,不只是可视区域page.screenshot(path="python_org.png",full_page=True)browser.close()对比 Selenium,同样的功能少了驱动配置、少了显式等待,代码块从十几行压到十行以内。
四、核心概念:Locator、自动等待、上下文
4.1 Locator:定位元素的新写法
Playwright 推荐用locator()拿到元素句柄,所有操作都基于它:
# 下面几种写法等价,选你顺手的page.locator("button#submit").click()page.locator("text=登录").click()page.get_by_role("button",name="登录").click()# 按语义角色,最稳page.get_by_placeholder("请输入用户名").fill("admin")page.get_by_text("下一步").click()Locator 是惰性的:写page.locator(...)时不会真的去查 DOM,只有执行.click()、.fill()时才查找,并且会自动等到元素可见、可点击、不被遮挡。这就是「自动等待」。
4.2 自动等待:和 time.sleep 说再见
fromplaywright.sync_apiimportexpect# 断言元素可见(默认超时 5 秒,期间反复重试)expect(page.get_by_text("提交成功")).to_be_visible(timeout=10000)# 断言 URL 变化expect(page).to_have_url("https://example.com/dashboard")expect是 Playwright 官方的断言工具,比assert page.title() == ...更可靠,因为它会轮询等待而不是一次性判断。需要改全局超时可以这样:
page.set_default_timeout(15000)# 所有操作默认最多等 15 秒4.3 上下文:多开互不干扰的会话
context相当于一个独立的浏览器会话(独立的 Cookie、缓存),比开多个 browser 省资源:
withsync_playwright()asp:browser=p.chromium.launch()# 模拟移动端 + 中文环境ctx=browser.new_context(viewport={"width":390,"height":844},user_agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15",locale="zh-CN",)page=ctx.new_page()page.goto("https://example.com")ctx.close()# 关上下文,Cookie 一起清掉browser.close()五、进阶用法:表单、上传、拦截
5.1 填表单与键盘鼠标
page.get_by_label("用户名").fill("zhangsan")page.get_by_label("密码").fill("secret123")page.get_by_label("记住我").check()page.get_by_role("combobox").select_option("beijing")page.keyboard.press("Enter")5.2 文件上传与下载
# 上传:直接给本地路径,不用再找系统文件框page.set_input_files("input[type=file]","report.xlsx")# 下载:必须用 expect_download 包住触发动作withpage.expect_download()asdl:page.get_by_text("导出报表").click()download=dl.value download.save_as("data/export.xlsx")5.3 拦截请求:屏蔽图片提速
抓数据时图片、字体往往没用,直接拦掉能明显加速:
defblock_images(route):ifroute.request.resource_typein("image","font","media"):route.abort()else:route.continue_()page.route("**/*",block_images)page.goto("https://news.example.com")六、实战场景:自动登录并抓取列表(完整代码)
下面这个例子把上面几点串起来,带登录态抓取分页数据,并保存截图:
# crawl_demo.pyfromplaywright.sync_apiimportsync_playwright,expect BASE="https://example.com"defmain():withsync_playwright()asp:browser=p.chromium.launch(headless=True)ctx=browser.new_context(locale="zh-CN")page=ctx.new_page()page.set_default_timeout(15000)# 1. 登录page.goto(f"{BASE}/login")page.get_by_label("账号").fill("your_account")page.get_by_label("密码").fill("your_password")page.get_by_role("button",name="登录").click()# 等到跳转完成,不要用 sleepexpect(page).to_have_url(f"{BASE}/dashboard")# 2. 抓多页列表rows=[]forpage_noinrange(1,4):page.goto(f"{BASE}/items?page={page_no}")expect(page.locator(".item-row").first).to_be_visible()items=page.locator(".item-row").all()foritinitems:rows.append({"title":it.locator(".title").inner_text(),"price":it.locator(".price").inner_text(),})print(f"第{page_no}页抓到{len(items)}条")# 3. 保存登录态,下次直接复用,省掉重复登录ctx.storage_state(path="auth.json")print("共抓取",len(rows),"条")browser.close()if__name__=="__main__":main()下次运行时直接加载已保存的登录态:
ctx=browser.new_context(storage_state="auth.json")七、和 Selenium 怎么选
| 维度 | Playwright | Selenium |
|---|---|---|
| 浏览器驱动 | 自带,一条命令安装 | 需手动下载、版本必须匹配 |
| 等待机制 | 操作自带自动等待 | 需自己写 WebDriverWait 或 sleep |
| 多浏览器 | 一套 API 支持 Chromium/Firefox/WebKit | 每个浏览器各自适配 |
| 录制脚本 | playwright codegen官方支持 | 依赖第三方插件 |
| 网络拦截 | 原生page.route | 需借助代理 |
| 生态与资料 | 较新,中文资料相对少 | 十年积累,资料最多、岗位要求常见 |
结论:新项目优先 Playwright,效率和稳定性都更好;老项目或需要兼容既有 Selenium 资产的继续用 Selenium,两者并不互斥。
八、常见坑与解决办法
1)playwright install卡住或超时
换国内镜像,见第二节。公司网络有代理时设置HTTPS_PROXY环境变量再执行。
2)locator.click()超时
先确认是不是在 iframe 里。跨框架要先切进去:
frame=page.frame_locator("iframe#login-frame")frame.get_by_role("button",name="登录").click()3)被网站识别为自动化
默认 headless 会带自动化特征,可以加启动参数缓解:
browser=p.chromium.launch(headless=True,args=["--disable-blink-features=AutomationControlled"],)4)抓到的中文乱码
确认new_context(locale="zh-CN"),并且写入文件时指定编码encoding="utf-8",Windows 上尤其容易踩。
5)调试时看不到过程
打开录制,事后回看每一步:
ctx.tracing.start(screenshots=True,snapshots=True)# ... 你的操作 ...ctx.tracing.stop(path="trace.zip")用playwright show-trace trace.zip可以在浏览器里逐步回放。
九、总结与下一步
Playwright 的三个核心优势:自带浏览器(告别驱动地狱)、自动等待(告别满屏 sleep)、统一 API(一套代码跑三种内核)。如果你之前被 Selenium 的驱动和等待折腾过,换过来基本是纯收益。
下一步建议:
- 用
playwright codegen https://你的目标站点录一遍,看看官方推荐的选择器怎么写; - 把现有脚本里的
time.sleep逐步替换为expect(...)断言; - 需要并发时改用异步 API(
from playwright.async_api import async_playwright)配合asyncio.gather。
官方文档:https://playwright.dev/python/docs/intro