做浏览器模拟访问的时候,最头疼的往往不是打开页面,而是页面里到底有哪些按钮能点、点了之后又会跳到哪里去。我把它拆成两个问题:识别按钮,以及处理点击后的跳转。
现代网页里,"按钮"不一定真的是 <button>。常见的有四种形态:
<button>提交</button>
<a href="/next">下一步</a>
<div role="button">点我</div>
<input type="submit" value="登录">
所以写选择器时,最好一次性把这几种都罩住:
buttons = await page.locator(
'button, a[href], [role="button"], input[type="submit"]'
).all()
再狠一点,把带 onclick 的旧式元素也加进来:
'button, a[href], [role="button"], input[type="submit"], [onclick]'
最直接,上面那段就是。优点是快,缺点是容易碰到隐藏元素、被弹窗挡住的元素,或者前端框架渲染出来的假按钮。
这是更稳的办法。Hermes 的 browser_snapshot 和 agent-browser 底层都走 Chrome DevTools Protocol 的 Accessibility.getFullAXTree,只返回真正可交互的元素,并给每个元素一个 @eN 引用:
agent-browser snapshot https://example.com
agent-browser click @e12
好处是过滤掉了一大批装饰性 div,不用自己写复杂选择器。
如果按钮是 canvas、图片,或者被反爬手段故意打乱结构,就只能截图 + OCR,或者上视觉模型来找坐标。这是最后的兜底方案,成本高,但有时候不得不用。
点按钮之后发生的事情不一定相同。可能是:
Playwright 里推荐用 expect_navigation 包一层:
async with page.expect_navigation(wait_until="networkidle"):
await page.locator('button[type="submit"]').click()
如果是 Vue/React 这种 SPA,跳转不会触发导航事件,要改用 URL 等待:
await page.wait_for_url("**/success/**")
下面这段代码会把页面上所有按钮列出来,逐一点击,记录跳转前后的 URL,然后返回继续下一个。
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://example.com")
buttons = await page.locator(
'button, a[href], [role="button"], input[type="submit"], [onclick]'
).all()
for i, btn in enumerate(buttons):
text = await btn.text_content() or await btn.get_attribute("value") or "无文字"
print(f"[{i}] {text.strip()[:20]}")
try:
before = page.url
async with page.expect_navigation(timeout=5000, wait_until="domcontentloaded"):
await btn.click()
after = page.url
print(f" -> {before} -> {after}")
await page.go_back()
except Exception as e:
print(f" -> 未跳转或失败: {e}")
await browser.close()
asyncio.run(main())
按钮不在视口。 有些页面懒加载,按钮渲染了但还没滚到屏幕里。点之前先 scroll_into_view_if_needed()。
被弹窗或遮罩挡住。 点按钮之前先检查页面上有没有 cookie 弹窗、广告层、loading 遮罩,关掉再点。
下载或新标签。 如果点击触发的是下载或 target="_blank",不会在当前页跳转,要监听 download 或 popup 事件。
反爬检测。 连续快速点击、完全一样的鼠标轨迹,很容易被识别成机器人。适当加随机延迟,让鼠标路径有点人味儿。
SPA 路由。 没有 load 事件,别傻等 networkidle,用 wait_for_url 或 wait_for_response。
浏览器自动化的核心其实就两步:先找到元素,再等待状态变化。识别按钮时,建议 DOM 查询 + 可访问性树结合用;处理跳转时,先判断页面是 MPA 还是 SPA,再决定等导航事件还是等 URL 变化。把这层逻辑写扎实了,后面做爬虫、自动化测试、批量操作都会顺手很多。