Playwright 全方位教程(理论、架构、执行原理与实战)

Playwright 全方位教程(理论、架构、执行原理与实战)

适用于 1.42+ 版本,示例以 Python 与 Node.js 为主,聚焦核心概念与最佳实践。

1. Playwright 是什么?

  • 多浏览器统一控制层:一次 API 调用同时支持 Chromium / Firefox / WebKit。
  • 自动化 + 端到端测试:内置测试框架(@playwright/test)、录制、截图/视频、追踪。
  • 反检测更友好:默认隐藏 navigator.webdriver、提供伪装器、支持持久化上下文。
  • 强一致性:同一 API 在不同浏览器呈现一致语义,减少跨浏览器差异。

2. 核心概念与对象模型

  • BrowserTypeBrowserBrowserContextPageFrameElementHandle
  • BrowserContext:独立的 Cookie/Storage 作用域,类似无痕窗口;推荐“一测一上下文”。
  • Page:标签页;所有交互(点击、输入、评估脚本)都在 Page 层执行。
  • Selectors:内置 CSS / XPath / Text / Role / ID / 自定义 Locator;locator() 懒求值,天然重试。
  • Network 控制:拦截/修改请求、模拟响应、注入 headers/cookies、路由 mock。
  • StorageState:导出/导入会话,复用登录态。

3. 内部架构与运行原理

  1. 多进程模型
    • Playwright Client(用户代码) → Node/Python/… binding → Playwright driver → 浏览器进程。
    • 通过 WebSocket/CDP/自定义协议驱动浏览器(Chromium: CDP;Firefox/WebKit: PW 自协议)。
  2. 自动等待 (auto-wait)
    • Action(click/fill/press)会等待元素可见、可操作、无遮挡;断言/expect 会轮询直至满足或超时。
  3. 事件循环与隔离
    • Playwright 在单独的执行器中维护与浏览器的连接,避免用户脚本阻塞协议层。
  4. 定位与重试机制
    • locator 在执行时解析,失败自动重试;page.wait_for_* 精准等待特定事件/状态。
  5. 录制与追踪
    • Trace 记录网络、控制台、DOM 变化、截图;可用 trace viewer 回放。

4. 典型执行流程

  1. 启动:chromium.launch()launch_persistent_context()(持久化用户数据)。
  2. 创建上下文:配置 UA、视口、代理、时区、地理位置、权限、存储状态。
  3. 新建 Page:context.new_page()
  4. 导航与等待:page.goto(url, wait_until="networkidle") + 显式等待目标元素。
  5. 交互:点击、输入、上传、拖拽、滚动、执行 JS。
  6. 断言/提取:locator.text_content()expect(locator).to_have_text()
  7. 资源:截图、PDF、下载、网络拦截、trace 采集。
  8. 关闭:context.close()browser.close()

5. 配置要点(常用参数速查)

  • launchheadlessargs(附加 flags)、proxyslow_motimeout
  • contextviewportuser_agentdevice_scale_factorlocale/timezone/geolocationpermissionsstorage_staterecord_video/screenshotbypass_csp
  • page.gotowait_until(load/domcontentloaded/networkidle/commit)、timeoutreferer
  • locatorlocator(selector, has_text=..., has=...),组合筛选;nth() 选取序号。
  • 网络page.route 拦截、context.set_extra_http_headerspage.wait_for_request/response

6. 反爬与稳定性实践

  • 指纹:自定义 UA、时区、语言、视口;必要时用持久化上下文携带真实 Profile。
  • 资源节流:禁用无关资源(图片/字体)、route 拦截第三方域名,降低干扰。
  • 等待策略:优先语义等待(locator.wait_forexpect),少用固定 sleep
  • 重试与超时:设置全局 set_default_timeout,在关键操作链外层加重试。
  • 代理与并发:为 IP 敏感站点设置住宅代理,控制并发与访问节奏。

7. Python 实战示例

import asyncio from playwright.async_api import async_playwright asyncdefmain():asyncwith async_playwright()as p: browser =await p.chromium.launch(headless=True) context =await browser.new_context( viewport={"width":1366,"height":768}, user_agent="MyBot/1.0", timezone_id="America/Los_Angeles",) page =await context.new_page()await page.goto("https://example.com", wait_until="networkidle") title =await page.title()await page.screenshot(path="example.png", full_page=True)print("title=", title)await browser.close() asyncio.run(main())

8. Node.js 实战示例(带测试断言)

import{ test, expect, chromium }from'@playwright/test';test('example.com title',async()=>{const browser =await chromium.launch({ headless:true});const context =await browser.newContext({ viewport:{ width:1280, height:720}});const page =await context.newPage();await page.goto('https://example.com',{ waitUntil:'networkidle'});awaitexpect(page).toHaveTitle(/Example Domain/);await page.screenshot({ path:'example.png', fullPage:true});await browser.close();});

9. 进阶能力

  • 录制脚本npx playwright codegen https://example.com 自动生成用例。
  • Trace / Video / Screenshotcontext.tracing.start()stop({ path })record_video 生成 MP4。
  • 网络 Mockpage.route('**/api/**', route => route.fulfill({ status: 200, body: 'ok' }))
  • 下载与上传:监听 page.on("download"),上传用 locator.set_input_files()
  • 多上下文/多用户:一个 Browser 内同时创建多个 Context,模拟不同登录态并行执行。

10. 常见问题排查

  • 超时:提升 page.goto timeout,或改用 networkidle/精确等待元素。
  • 元素不可点击:检查遮挡、滚动、force 参数慎用;优先用 Locator + expect。
  • 验证码/登录:导入 storage_state 或使用持久化上下文;必要时人工打码/第三方服务。
  • 证书/HTTPSignore_https_errors=True(Context 级);或信任自签证书。
  • 字体/渲染差异:在容器中安装所需字体,固定 locale 和 deviceScaleFactor。

11. 深挖关键技术点(高频问答版)

  1. Driver 做了什么:Playwright Driver 作为中间层进程,接收语言 SDK 调用→序列化协议→通过 WebSocket/CDP 发送给浏览器;同时接收浏览器事件→反序列化→回调用户代码,并管理会话、超时、自动等待、selector 解析、事件订阅等。
  2. Driver 与浏览器内核的通信机制:Chromium 走 CDP,Firefox/WebKit 走 PW 自协议;双向异步消息通道,包含指令、事件与结果,Driver 维护消息队列和回调映射。
  3. Browser / Context / Page 的真实含义:Browser=进程;Context=独立无痕窗口(隔离 Cookie/Storage/权限);Page=标签页,具体交互对象。设计目的是并行多用户/多会话且互不污染。
  4. 自动等待(Auto-wait)真相:交互 API 内置等待“存在+可见+可操作+无遮挡+稳定”,失败自动重试到超时;locator/expect 也轮询,减少显式 sleep。
  5. 为什么更反爬友好:默认隐藏 webdriver 痕迹,提供 stealth/undetected 适配;易配指纹(UA/时区/语言/viewport/持久化上下文);精细网络控制与脚本注入;可切换多内核(Chromium/Firefox/WebKit)。
  6. 核心设计哲学:一致性(跨浏览器同语义)、隔离性(Context)、可观测性(trace/video/screenshot/log)、默认可靠(auto-wait/locator)、安全可控(权限/超时/拦截/持久化)。
  7. 面试常问(示例回答)

网络拦截怎么做:基于 CDP/PW 协议的 Fetch/Route,在请求发出前拦截,可 continue/fulfill/abort/修改;响应也可拦截,常用于 mock、限流、去噪。 continue_:改写请求 fulfill:自定义响应 abort:强制终止示例:等待特定 URL 响应并解析 JSON(Python async)

import asyncio from playwright.async_api import async_playwright TARGET ="https://api.example.com/data"asyncdefmain():asyncwith async_playwright()as p: browser =await p.chromium.launch(headless=True) page =await browser.new_page()# 方式一:用 wait_for_response(等待并解析指定响应) wait_resp = page.wait_for_response(lambda r: r.url.startswith(TARGET))await page.goto("https://example.com") resp =await wait_resp data =await resp.json()print("resp status", resp.status,"data keys",list(data.keys()))# 方式二:route 拦截——查看/改写请求后放行asyncdefhandle_route(route): req = route.request if req.url.startswith(TARGET):print("hit target", req.method, req.url)await route.continue_( headers={**req.headers,"X-Debug":"1"})else:await route.continue_()await page.route(TARGET, handle_route)# 方式三:route 拦截 + fulfill 自定义响应(mock 接口)asyncdefmock_route(route): req = route.request if req.url.startswith("https://api.example.com/mock"):await route.fulfill( status=200, content_type="application/json", body='{"ok":true,"data":[1,2,3]}')else:await route.continue_()await page.route("**/mock", mock_route)# 方式四:拦截并强制终止(用于屏蔽第三方资源)await page.route("**/*.png",lambda route: route.abort())await page.goto("https://example.com")# 再等一遍真正的目标响应以便解析 resp =await page.wait_for_response(lambda r: r.url.startswith(TARGET))print("status:", resp.status,"body:",await resp.text())await browser.close() asyncio.run(main())

Selector 引擎:内置 CSS/XPath/Text/Role/ID/nth/has/has-text;locator() 懒求值+重试;支持自定义 selector 引擎。常用 selector 示例(Python)

page.locator('css=div.article h1')# CSS page.locator('xpath=//button[@type="submit"]') page.locator('text=登录')# 文本匹配(精确) page.locator('text=/Log.?n/i')# 文本正则 page.get_by_role('button', name='Submit')# ARIA Role + 可见名 page.get_by_test_id('submit-btn')# data-testid 或 test id 属性 page.locator('#search').nth(0)# ID + nth page.locator('div.list >> nth=3')# nth 语法 page.locator('section:has(div.card)')# :has 子选择 page.locator('div.item', has_text='已支付')# has_text 过滤 page.locator('div.list >> text=详情')# 链式 text 过滤

自定义 selector(Node.js,示意)

// 注册自定义选择器 engine(示例:按 data-qa 查找)const{ selectors, chromium }=require('playwright'); selectors.register('dataqa',{create(root, target){return root.querySelector(`[data-qa="${target}"]`);}});(async()=>{const browser =await chromium.launch();const page =await(await browser.newContext()).newPage();await page.goto('https://example.com');await page.locator('dataqa=my-button').click();await browser.close();})();

为什么并发强:单 Browser 多 Context 多 Page;通信异步非阻塞;自动等待减少 sleep;可禁用多余资源降低成本,并可水平扩多个 Browser 进程。示例:单 Browser + 多 Context + 多 Page 并发抓取(Python async)

import asyncio from playwright.async_api import async_playwright URLS =[f"https://example.com/?q={i}"for i inrange(30)] BROWSER_HEADLESS =True CONTEXTS =3# 并行上下文数 PAGES_PER_CTX =5# 每个上下文的并发页数(总并发=CONTEXTS*PAGES_PER_CTX)asyncdeffetch_page(page, url):await page.goto(url, wait_until="networkidle") title =await page.title()return url, title asyncdefworker(ctx, urls): results =[] sem = asyncio.Semaphore(PAGES_PER_CTX)asyncdefrun(url):asyncwith sem: page =await ctx.new_page()try: results.append(await fetch_page(page, url))finally:await page.close()await asyncio.gather(*(run(u)for u in urls))return results asyncdefmain():asyncwith async_playwright()as p: browser =await p.chromium.launch(headless=BROWSER_HEADLESS) contexts =[await browser.new_context()for _ inrange(CONTEXTS)]# 均分 URL 到多个上下文 chunks =[URLS[i::CONTEXTS]for i inrange(CONTEXTS)] tasks =[asyncio.create_task(worker(ctx, chunk))for ctx, chunk inzip(contexts, chunks)] all_results =[]for t in tasks: all_results.extend(await t)for ctx in contexts:await ctx.close()await browser.close()for u, title in all_results:print(u,"->", title) asyncio.run(main())
  • Playwright vs Selenium:Playwright 提供多浏览器一致 API、内建自动等待、Context 隔离、多核支持(Chromium/Firefox/WebKit)、更易反检测;Selenium 兼容性广但等待/指纹需手工处理,跨浏览器差异更大。
  • Context 的作用:独立的无痕窗口/会话容器,隔离 Cookie/Storage/权限;可并行多用户场景,避免跨用例污染,支持 storage_state 导入导出。
  • Auto-wait 机制:交互动作会等待元素存在、可见、可操作、无遮挡并稳定;Locator/Expect 轮询直至超时,减少显式 sleep,降低 flaky。
  • 网络 mock 实现page.route/context.route 拦截请求,在回调中 fulfill/continue/abort;可改 headers/body/status 或返回自定义响应;配合 wait_for_request/response 做验证。
  • 什么是 CDP 协议:Chrome DevTools Protocol,Chromium 暴露的远程调试与控制接口(WebSocket JSON-RPC);支持 DOM 操作、网络拦截、性能剖析、截图等。Playwright 在 Chromium 下通过 CDP 驱动浏览器(Firefox/WebKit 用自协议)。
  • 反爬与指纹伪装:设置 UA/语言/时区/视口/地理位置,使用持久化 Context 或 storage_state 复用真实会话,开启 stealth/undetected 适配,合理代理与并发节流,拦截第三方资源减少噪音。
  • 排查 flaky:开启 trace/video/screenshot,收集 console/network 日志;改用语义等待(locator/expect),提升/分级超时;隔离上下文、禁用不稳定资源,必要时重试。
  • 并发模型与优化:一个 Browser 多 Context 多 Page;控制 max_concurrency、禁图/字体、开启 headless、分片运行;必要时多 Browser 进程分布式执行。
  • 登录态复用:使用 storage_state 导出/导入,或 launch_persistent_context 复用用户数据目录;上下文级别设置 cookies/localStorage 后再打开页面。

11. 参考与工具


掌握以上内容后,可快速从“写脚本”进阶到“稳定可维护的自动化/爬取/测试”实践:

  • 用 Context 隔离状态,Locator 做可靠等待。
  • 通过拦截与指纹配置应对反爬。
  • 结合 Trace/Video/Screenshot 复现并定位问题。

Read more

安装 启动 使用 Neo4j的超详细教程

安装 启动 使用 Neo4j的超详细教程

最近在做一个基于知识图谱的智能生成项目。需要用到Neo4j图数据库。写这篇文章记录一下Neo4j的安装及其使用。 一.Neo4j的安装 1.首先安装JDK,配环境变量。(参照网上教程,很多) Neo4j是基于Java的图形数据库,运行Neo4j需要启动JVM进程,因此必须安装JAVA SE的JDK。从Oracle官方网站下载 Java SE JDK。我使用的版本是JDK1.8 2.官网上安装neo4j。 官方网址:https://neo4j.com/deployment-center/  在官网上下载对应版本。Neo4j应用程序有如下主要的目录结构: bin目录:用于存储Neo4j的可执行程序; conf目录:用于控制Neo4j启动的配置文件; data目录:用于存储核心数据库文件; plugins目录:用于存储Neo4j的插件; 3.配置环境变量 创建主目录环境变量NEO4J_HOME,并把主目录设置为变量值。复制具体的neo4j文件地址作为变量值。 配置文档存储在conf目录下,Neo4j通过配置文件neo4j.conf控制服务器的工作。默认情况下,不需

企业微信群机器人Webhook配置全攻略:从创建到发送消息的完整流程

企业微信群机器人Webhook配置全攻略:从创建到发送消息的完整流程 在数字化办公日益普及的今天,企业微信作为国内领先的企业级通讯工具,其群机器人功能为团队协作带来了极大的便利。本文将手把手教你如何从零开始配置企业微信群机器人Webhook,实现自动化消息推送,提升团队沟通效率。 1. 准备工作与环境配置 在开始创建机器人之前,需要确保满足以下基本条件: * 企业微信账号:拥有有效的企业微信管理员或成员账号 * 群聊条件:至少包含3名成员的群聊(这是创建机器人的最低人数要求) * 网络环境:能够正常访问企业微信服务器 提示:如果是企业管理员,建议先在"企业微信管理后台"确认机器人功能是否已对企业开放。某些企业可能出于安全考虑会限制此功能。 2. 创建群机器人 2.1 添加机器人到群聊 1. 打开企业微信客户端,进入目标群聊 2. 点击右上角的群菜单按钮(通常显示为"..."或"⋮") 3. 选择"添加群机器人"选项 4.

Flowise物联网融合:与智能家居设备联动的应用设想

Flowise物联网融合:与智能家居设备联动的应用设想 1. Flowise:让AI工作流变得像搭积木一样简单 Flowise 是一个真正把“AI平民化”落地的工具。它不像传统开发那样需要写几十行 LangChain 代码、配置向量库、调试提示词模板,而是把所有这些能力打包成一个个可拖拽的节点——就像小时候玩乐高,你不需要懂塑料怎么合成,只要知道哪块该拼在哪,就能搭出一座城堡。 它诞生于2023年,短短一年就收获了45.6k GitHub Stars,MIT协议开源,意味着你可以放心把它用在公司内部系统里,甚至嵌入到客户交付的产品中,完全不用担心授权问题。最打动人的不是它的技术多炫酷,而是它真的“不挑人”:产品经理能搭出知识库问答机器人,运营同学能配出自动抓取竞品文案的Agent,连刚学Python两周的实习生,也能在5分钟内跑通一个本地大模型的RAG流程。 它的核心逻辑很朴素:把LangChain里那些抽象概念——比如LLM调用、文档切分、向量检索、工具调用——变成画布上看得见、摸得着的方块。你拖一个“Ollama LLM”节点,再拖一个“Chroma Vector

OpenClaw配置Bot接入飞书机器人+Kimi2.5

OpenClaw配置Bot接入飞书机器人+Kimi2.5

上一篇文章写了Ubuntu_24.04下安装OpenClaw的过程,这篇文档记录一下接入飞书机器+Kimi2.5。 准备工作 飞书 创建飞书机器人 访问飞书开放平台:https://open.feishu.cn/app,点击创建应用: 填写应用名称和描述后就直接创建: 复制App ID 和 App Secret 创建成功后,在“凭证与基础信息”中找到 App ID 和 App Secret,把这2个信息复制记录下来,后面需要配置到openclaw中 配置权限 点击【权限管理】→【开通权限】 或使用【批量导入/导出权限】,选择导入,输入以下内容,如下图 点击【下一步,确认新增权限】即可开通所需要的权限。 配置事件与回调 说明:这一步的配置需要先讲AppId和AppSecret配置到openclaw成功之后再设置订阅方式,