Showing Posts From

Playwright

Playwright 接管本地 Chrome:四种方式对比

Playwright 默认下载并使用自带的 Chromium。想用本机装的 Google Chrome(自动化登录时保留 cookie、复用插件),有四种方式,各自适用场景不同。 方式一:channel = "chrome" 最省事——channel: "chrome" 告诉 Playwright 用系统装的稳定版 Chrome: from playwright.sync_api import sync_playwrightwith sync_playwright() as p: browser = p.chromium.launch(channel="chrome", headless=False) page = browser.new_page() page.goto("https://example.com") input("回车退出") browser.close()优点:跨平台一行搞定 局限:不会用你日常那个 profile,不带 cookie、不带插件其它可选 channel:msedge、chrome-beta、chrome-dev、msedge-beta。 方式二:executable_path 指定路径 Chrome 装在非默认路径,或者想用 Chromium 兼容分发(Brave、Edge、Vivaldi): Windows: browser = p.chromium.launch( executable_path=r"C:\Program Files\Google\Chrome\Application\chrome.exe", headless=False, )macOS: browser = p.chromium.launch( executable_path="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", headless=False, )Linux: browser = p.chromium.launch( executable_path="/usr/bin/google-chrome", headless=False, )跟方式一一样,不带 cookie / 插件——只是换了浏览器可执行文件。 方式三:launchPersistentContext 复用 profile 想让 Playwright 用日常那个 Chrome 用户目录(复用登录态、书签、扩展): browser = p.chromium.launch_persistent_context( user_data_dir=r"C:\Users\<你>\AppData\Local\Google\Chrome\User Data", channel="chrome", headless=False, ) page = browser.pages[0] if browser.pages else browser.new_page() page.goto("https://example.com")注意:这个方式要求 Chrome 没在运行,否则报: Failed to launch: browser closed. Profile is already in use解法:关掉所有 Chrome 窗口再跑 或者复制一份 profile 出来单独用:xcopy "C:\...\User Data" "D:\pw-profile" /E /I想只用某个 Profile(不是 Default): browser = p.chromium.launch_persistent_context( user_data_dir=r"D:\pw-profile", args=["--profile-directory=Profile 1"], channel="chrome", headless=False, )方式四:CDP 连接已启动的 Chrome(推荐) 方式三最大的痛点是"Chrome 必须关闭"。想在 Chrome 正常开着的情况下让 Playwright 接管它,走 CDP(Chrome DevTools Protocol)。 第一步:以 debug 模式启动 Chrome,指定端口: "C:\Program Files\Google\Chrome\Application\chrome.exe" ^ --remote-debugging-port=9222 ^ --user-data-dir="D:\pw-chrome-profile"--user-data-dir 指向一个专用目录,避免和日常 Chrome 抢占用。 第二步:Playwright 连上去: from playwright.sync_api import sync_playwrightwith sync_playwright() as p: browser = p.chromium.connect_over_cdp("http://127.0.0.1:9222") ctx = browser.contexts[0] page = ctx.pages[0] if ctx.pages else ctx.new_page() page.goto("https://example.com")优势:Chrome 一直开着,做完自动化 Playwright 断开就行 保留登录态、插件、cookie 可以边脚本操作边人工插手(做半自动化)生产爬虫、账号自动登录场景基本用这个。 四种方式对比方式 Chrome 需关闭 保留 profile 使用场景channel="chrome" 否 否 简单自动化,无需登录态executable_path=... 否 否 用其它 Chromium 分发launch_persistent_context 是 是 一次性用现有 profileconnect_over_cdp (CDP) 否 是 推荐:常驻 Chrome + 脚本联动顺带:无痕、隐身、下载路径 无痕模式: browser = p.chromium.launch(channel="chrome", args=["--incognito"])指定下载目录: context = browser.new_context(accept_downloads=True) page = context.new_page() async with page.expect_download() as info: await page.click("a#download") download = await info.value await download.save_as("D:/downloads/file.zip")忽略证书错误(内网自签证书): context = browser.new_context(ignore_https_errors=True)一句话总结 普通自动化用 channel="chrome"、要复用登录态用 CDP 连接(先手动启动 Chrome + --remote-debugging-port=9222)。launch_persistent_context 只在 Chrome 关闭时才能用。

Playwright 使用本地已安装的 Chrome:channel、executablePath 与 CDP 连接

Playwright 默认使用自带的 Chromium 浏览器。以下四种方式可以改用本地安装的 Google Chrome。 方法一:channel='chrome'(最简单) 需要本机已安装 Google Chrome。 Python: from playwright.sync_api import sync_playwrightwith sync_playwright() as p: browser = p.chromium.launch(channel="chrome", headless=False) page = browser.new_page() page.goto("https://example.com") browser.close()JavaScript: const { chromium } = require('playwright');const browser = await chromium.launch({ channel: 'chrome', headless: false }); const page = await browser.newPage(); await page.goto('https://example.com'); await browser.close();方法二:指定 executablePath Python(各平台路径): # Windows executable_path = r"C:\Program Files\Google\Chrome\Application\chrome.exe"# macOS executable_path = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"# Linux executable_path = "/usr/bin/google-chrome"browser = p.chromium.launch(executable_path=executable_path, headless=False)方法三:launchPersistentContext(复用登录状态) 使用已登录账号、Cookie、插件的 Chrome 用户数据目录: Python: from playwright.sync_api import sync_playwrightwith sync_playwright() as p: context = p.chromium.launch_persistent_context( r"C:\Users\用户名\AppData\Local\Google\Chrome\User Data", channel="chrome", headless=False, ) page = context.pages[0] or context.new_page() page.goto("https://example.com")注意:Chrome 运行时会独占用户目录,使用此方法前需要关闭所有 Chrome 窗口,否则报错: Profile is already in use方法四:connectOverCDP(连接已启动的 Chrome) 先启动 Chrome 并开启远程调试端口(不影响正常使用): # Windows "C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222# macOS /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222然后 Playwright 连接: Python: browser = p.chromium.connect_over_cdp("http://127.0.0.1:9222") page = browser.contexts[0].pages[0] page.goto("https://example.com")JavaScript: const browser = await chromium.connectOverCDP('http://127.0.0.1:9222'); const page = browser.contexts()[0].pages()[0];这种方式完全复用当前 Chrome 的登录状态、Cookie 和扩展,且不独占用户目录。适合需要手动操作与自动化混合使用的场景。 方案对比方法 复用登录 Chrome 需关闭 保留插件channel='chrome' ✗ 否 ✗executablePath ✗ 否 ✗launchPersistentContext ✓ 是 ✓connectOverCDP ✓ 否 ✓

Playwright 定位网页元素:getByText / getByRole 比 CSS Selector 稳在哪

用 Playwright 自动化网页时,很多人习惯用 CSS selector(page.locator(".btn-submit")),但这类 selector 抗变化能力差——前端一改 class 名就失效。Playwright 提供了一套语义化定位器,稳定性高很多。 语义化定位器 getByText 按可见文本定位,最常用: await page.getByText('登录').click(); await page.getByText('提交订单').click();支持正则: await page.getByText(/确认.*订单/).click();getByRole 按 ARIA role 定位,最语义化: await page.getByRole('button', { name: '提交' }).click(); await page.getByRole('link', { name: '首页' }).click(); await page.getByRole('textbox', { name: '搜索' }).fill('hello');常用 role:button、link、textbox、checkbox、listbox、dialog、heading getByPlaceholder 按 input placeholder 定位: await page.getByPlaceholder('请输入手机号').fill('138xxxx'); await page.getByPlaceholder('密码').fill('password123');getByLabel 按 <label> 文本定位关联的 input: await page.getByLabel('邮箱').fill('test@example.com'); await page.getByLabel('记住我').check();getByTestId 按 data-testid 属性,测试环境最稳定的方案: // HTML: <button data-testid="submit-btn">提交</button> await page.getByTestId('submit-btn').click();为什么比 CSS Selector 稳定位方式 变化敏感度 说明.btn-primary-v2 高 class 名经常随版本变#submit > div > button 高 DOM 结构一变就失效getByText('提交') 低 文本变了才失效getByRole('button', {name: '提交'}) 低 语义稳定getByTestId('submit') 最低 开发者明确标记处理复杂场景 多个相同文本时,用 nth 或组合定位: // 第二个"删除"按钮 await page.getByText('删除').nth(1).click();// 在某个容器内查找 await page.locator('.order-list').getByRole('button', { name: '删除' }).first().click();等待元素可交互: await page.getByRole('button', { name: '提交' }).waitFor({ state: 'visible' }); await page.getByRole('button', { name: '提交' }).click();真正难处理的场景 语义化定位器解决不了的几种情况,才需要 AI Vision Agent:Canvas 元素(无 DOM 结构) Shadow DOM 内的元素 动态随机 class 名(某些 React 框架) 无文字的图标按钮 iframe 跨域内容对于这些场景,可以用 Stagehand 等 AI 驱动的自动化工具: await page.act("点击右上角的关闭按钮");让大模型看截图决定点击位置,不依赖 selector。 实际推荐策略优先用 getByRole + getByText,覆盖 80% 常规场景 次选 getByPlaceholder / getByLabel(表单场景) 开发阶段加 data-testid,测试最稳定 Shadow DOM / Canvas 等场景才用 AI Vision不要一开始就上 AI Vision,定位器够用的情况下更快、更稳、Token 消耗为零。