很多网站的内容早就不再是“请求一个 HTML 就完事”了:页面是前端框架渲染的、数据来自异步接口、列表需要滚动才加载,甚至还会针对无头浏览器、异常指纹、频繁访问做风控。这时用 requests + bs4 往往抓不到关键内容;而传统的 Selenium 又常常因为等待、定位、稳定性和调试成本而让人崩溃。
Playwright 是微软开源的浏览器自动化框架,它能像真实用户一样驱动 Chromium / Firefox / WebKit 浏览器:执行 JavaScript、等待页面渲染、处理登录与 Cookie、模拟滚动/点击、拦截网络请求、截图与录制 trace 复现问题。对爬虫来说,Playwright 的价值不只是“能打开网页”,而是能让你在动态渲染、复杂交互和一定程度的反爬环境下,依然稳定地拿到数据。
这篇文章会围绕“用 Playwright 做采集”展开,从最小可用脚本开始,逐步讲清楚:如何选择抓 DOM 还是抓接口、如何处理无限滚动与分页、如何保存登录态与复用会话、如何降低被封风险、以及如何把脚本整理成可维护的采集工程。目标是让你读完就能把代码套到自己的目标站点上。
下面以 Windows/macOS/Linux 通用的方式介绍部署。建议使用虚拟环境,避免污染系统 Python。
要求:Python 3.9+(越新越好)
# 进入你的项目目录
mkdir pw-crawler && cd pw-crawler
# 创建并激活虚拟环境(Windows PowerShell)
python -m venv .venv
.\.venv\Scripts\Activate.ps1
# macOS / Linux
# python3 -m venv .venv
# source .venv/bin/activate
# 安装 Playwright
pip install -U playwright
# 安装所有内核(Chromium / Firefox / WebKit)
playwright install
# 只安装 Chromium(爬虫最常用,更轻量)
playwright install chromium
新建 smoke_test.py:
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto("https://example.com", wait_until="domcontentloaded")
print(page.title())
browser.close()
运行:
python smoke_test.py
正常情况(示例):终端会打印出页面标题,然后程序退出。
Example Domain
# 调试阶段:有头模式(能看到浏览器)+ 慢动作
browser = p.chromium.launch(headless=False, slow_mo=200)
# 正式跑采集:无头模式(服务器/CI 友好)
browser = p.chromium.launch(headless=True)
# 可选:打开 Playwright Inspector(适合调试定位/等待)
page = browser.new_page()
page.goto("https://example.com")
page.pause()
下面示例默认使用
playwright.sync_api(同步写法更适合入门与脚本采集)。
若你用异步,把sync_playwright()换成async_playwright(),并在调用前加await即可。
术语约定:
- Browser:浏览器进程(Chromium/Firefox/WebKit)
- Context:浏览器上下文(相当于一个“全新用户环境”,隔离 cookie/缓存)
- Page:一个标签页
- Locator:元素定位器(不是元素本身,而是“随时能找到元素的一种引用”)
sync_playwright() / browser / context / pagesync_playwright()
启动 Playwright 的运行环境(同步版入口)。你一般在 with sync_playwright() as p: 里拿到 p。
p.chromium / p.firefox / p.webkit
选择要驱动的浏览器内核。爬虫常用 chromium。
browser = p.chromium.launch(...)headless=True/False:无头/有头slow_mo=...:慢放(调试用)proxy=...:代理context = browser.new_context(...)storage_state(见下)page = context.new_page() 或 browser.new_page()context.new_page()(更可控、更工程化)。page.goto() / wait_untilpage.goto(url, wait_until=...)domcontentloaded:DOM 构建完成(爬虫最常用)load:页面 load 事件触发(更慢)networkidle:网络基本空闲(有的网站永远不会 idle,不建议默认)page.url / page.title() / page.content()
page.url:当前地址page.title():标题page.content():当前页面 HTML(渲染后的 DOM 字符串)locator() 是什么?为什么它是爬虫/自动化的关键page.locator(selector)“按这个 selector 去找元素(可能是 0 个、1 个、很多个),并且在你真正执行动作时再去匹配、自动等待元素可用。”
Locator 的特点:
locator.first / locator.nth(i) / locator.last
当匹配到多个元素时取第一个/第 i 个/最后一个。
locator.count()locator.inner_text()
取 可见文本(更贴近你在浏览器看到的内容)。
locator.text_content()
取 节点文本内容(可能包含不可见部分、格式差异更大)。
locator.get_attribute(name)
取属性值(例如 href、src、data-*)。
locator.inner_html() / locator.evaluate(...)
inner_html():取元素内部 HTMLevaluate:在浏览器里执行 JS 来取更复杂的数据(爬虫里很常用,但要慎用,尽量先用上面的简洁 API)。locator.click() / page.click(...)
点击元素。推荐 locator.click()(更稳、更易组合等待)。
locator.fill(text)
填输入框(清空后输入)。
locator.type(text, delay=...)
模拟逐字输入(更像人类,偶尔对反爬/前端校验更友好)。
page.keyboard.press("Enter")
键盘按键。常用:回车提交、ESC 关闭弹窗等。
page.mouse.wheel(dx, dy)
滚动(列表懒加载常用)。
locator.wait_for()
等这个元素满足条件(默认等到出现在 DOM 并可用)。
爬虫里:列表懒加载、点击后等待详情区域出现,都用它。
page.wait_for_url(pattern)
等地址变成某种形式(翻页/跳转后确认到位)。
page.wait_for_response(predicate)
等某个网络响应(抓接口 JSON 时非常关键)。
你可以按 URL 关键字、状态码筛选。
page.wait_for_timeout(ms)
强制等待(最后兜底,不建议常用)。
page.on("request", handler) / page.on("response", handler)page.route(pattern, handler)storage_statecontext.storage_state(path=...)
把当前 context 的 cookie/localStorage 等状态保存到文件。
browser.new_context(storage_state=...)
新建 context 时直接加载登录态(爬虫里特别常用:避免每次都登录)。
page.screenshot(...)
保存截图(失败时第一证据)。
page.content()
保存渲染后的 HTML(对照选择器很有用)。
context.tracing.start(...) / context.tracing.stop(...)
录制 trace(可回放脚本每一步发生了什么,定位 flaky/反爬拦截非常强)。
p(Playwright 引擎)→ browser(浏览器)→ context(隔离会话)→ page(标签页)→ locator(定位元素)→ 读取/交互/等待官方文档链接:Playwright Docs
动画演示链接(个人制作):Playwright Demo
提示:鼠标移动会产生拖尾并影响周围粒子;按住 Alt 可切换吸引(Alt)/排斥(默认)效果。