浏览器自动化里,怎么把页面按钮一网打尽并跟进跳转

Published on with 0 views and 0 comments

浏览器自动化里,怎么把页面按钮一网打尽并跟进跳转

做浏览器模拟访问的时候,最头疼的往往不是打开页面,而是页面里到底有哪些按钮能点、点了之后又会跳到哪里去。我把它拆成两个问题:识别按钮,以及处理点击后的跳转。


按钮长什么样

现代网页里,"按钮"不一定真的是 <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]'

识别按钮的三种路子

1. DOM 查询

最直接,上面那段就是。优点是快,缺点是容易碰到隐藏元素、被弹窗挡住的元素,或者前端框架渲染出来的假按钮。

2. 可访问性树

这是更稳的办法。Hermes 的 browser_snapshotagent-browser 底层都走 Chrome DevTools Protocol 的 Accessibility.getFullAXTree,只返回真正可交互的元素,并给每个元素一个 @eN 引用:

agent-browser snapshot https://example.com
agent-browser click @e12

好处是过滤掉了一大批装饰性 div,不用自己写复杂选择器。

3. 视觉定位

如果按钮是 canvas、图片,或者被反爬手段故意打乱结构,就只能截图 + OCR,或者上视觉模型来找坐标。这是最后的兜底方案,成本高,但有时候不得不用。


点击之后怎么跟进跳转

点按钮之后发生的事情不一定相同。可能是:

  • 同步跳转,整页刷新
  • 前端路由切换,URL 变了但页面没刷新
  • 打开新标签或新窗口
  • 只是展开菜单、弹个窗,根本不跳转

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",不会在当前页跳转,要监听 downloadpopup 事件。

反爬检测。 连续快速点击、完全一样的鼠标轨迹,很容易被识别成机器人。适当加随机延迟,让鼠标路径有点人味儿。

SPA 路由。 没有 load 事件,别傻等 networkidle,用 wait_for_urlwait_for_response


写在最后

浏览器自动化的核心其实就两步:先找到元素,再等待状态变化。识别按钮时,建议 DOM 查询 + 可访问性树结合用;处理跳转时,先判断页面是 MPA 还是 SPA,再决定等导航事件还是等 URL 变化。把这层逻辑写扎实了,后面做爬虫、自动化测试、批量操作都会顺手很多。