使用 Selenium-Wire 捕获和分析前端网络请求

聖光之護
发布: 2025-11-07 11:51:01
原创
480人浏览过

使用 Selenium-Wire 捕获和分析前端网络请求

标准 selenium 并非为直接捕获 api 请求设计,但 `selenium-wire` 扩展了其功能,允许开发者在自动化测试中拦截、检查和分析浏览器与服务器之间的所有网络流量。本文将详细介绍如何利用 `selenium-wire` 捕获特定 api 请求及其 json 响应数据,从而在不改变页面 url 的情况下,获取前端后端交互的深层信息,适用于需要监控或验证 api 通信的自动化场景。

理解 Selenium 在 API 捕获中的局限性

Selenium 主要是一个浏览器自动化工具,用于模拟用户与网页的交互,如点击、输入、导航等。它关注的是用户可见的界面行为和页面状态变化。虽然可以通过 WebDriver 的日志(如 performance 日志)或执行 CDP(Chrome DevTools Protocol)命令来尝试获取网络请求信息,但这些方法通常较为复杂、不够直观,且可能依赖于特定浏览器或版本,维护成本较高。

当需求是捕获由前端行为(例如点击按钮)触发的后端 API 请求及其响应数据,特别是当页面 URL 不变而仅有 API 端点发生交互时,标准 Selenium 的能力显得捉襟见肘。此时,我们需要一个更专业的工具来直接监听和解析网络流量。

引入 Selenium-Wire:网络流量的监听器

selenium-wire 是一个 Python 库,它通过在 Selenium WebDriver 和浏览器之间设置一个代理(Proxy),从而能够拦截和检查所有流经浏览器的网络请求和响应。这使得开发者可以轻松地访问请求头、请求体、响应头和响应体,并对它们进行过滤和分析。

Selenium-Wire 的核心优势:

  • 透明代理: 无需额外配置浏览器代理,selenium-wire 会自动处理。
  • 全面捕获: 能够捕获所有类型的网络请求,包括 XHR/Fetch 请求、图片、CSS、JS 等。
  • 易于访问: 提供简洁的 API 来访问请求和响应的各个部分。
  • 灵活过滤: 可以根据 URL、方法、头信息等条件过滤请求。

安装与配置

首先,需要安装 selenium-wire 库。

千面视频动捕
千面视频动捕

千面视频动捕是一个AI视频动捕解决方案,专注于将视频中的人体关节二维信息转化为三维模型动作。

千面视频动捕 27
查看详情 千面视频动捕

立即学习前端免费学习笔记(深入)”;

pip install selenium-wire
登录后复制

安装完成后,可以在 Python 代码中引入并配置 selenium-wire 的 WebDriver。

from selenium_wire import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import json
import time
import logging

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

def capture_api_response(start_url, element_id_to_click, api_url_part):
    """
    使用 selenium-wire 捕获点击元素后触发的 API 请求及其 JSON 响应。

    Args:
        start_url (str): 浏览器起始访问的 URL。
        element_id_to_click (str): 需要点击的元素的 ID。
        api_url_part (str): 目标 API URL 中包含的特定字符串,用于过滤请求。

    Returns:
        dict: 如果找到目标 API 的 JSON 响应,则返回解析后的字典;否则返回 None。
    """
    # 配置 WebDriver 选项
    options = webdriver.ChromeOptions()
    # 可以根据需要添加其他选项,例如无头模式
    # options.add_argument('--headless')
    # options.add_argument('--disable-gpu')

    # 配置 selenium-wire 的 WebDriver
    # service = Service(executable_path='/path/to/chromedriver') # 如果 chromedriver 不在 PATH 中,需要指定路径
    driver = webdriver.Chrome(options=options)
    driver.set_page_load_timeout(30) # 设置页面加载超时时间

    found_data = None

    try:
        logger.info(f"正在访问 URL: {start_url}")
        driver.get(start_url)

        # 清除所有历史请求,确保只捕获本次操作产生的流量
        driver.delete_all_requests()
        logger.info("已清除所有历史网络请求。")

        # 等待元素可点击并执行点击操作
        wait = WebDriverWait(driver, 10)
        element_to_click = wait.until(EC.element_to_be_clickable((By.ID, element_id_to_click)))
        logger.info(f"找到元素 '{element_id_to_click}',准备点击。")
        element_to_click.click()
        logger.info(f"已点击元素 '{element_id_to_click}'。")

        # 等待一段时间,让 API 请求有足够的时间完成并响应
        # 实际项目中,可能需要更智能的等待机制,例如等待某个特定的 DOM 元素出现或消失
        time.sleep(5)
        logger.info("等待 5 秒以捕获网络请求。")

        # 遍历所有捕获到的请求
        for request in driver.requests:
            # 确保请求有响应,并且响应 URL 包含目标 API 的部分
            if request.response and api_url_part in request.url:
                logger.info(f"捕获到潜在目标API请求: {request.url}")
                # 检查响应内容类型是否为 JSON
                content_type = request.response.headers.get('Content-Type', '')
                if 'application/json' in content_type:
                    try:
                        # 获取响应体并解码为 UTF-8 字符串,然后解析为 JSON
                        body = request.response.body.decode('utf-8')
                        json_data = json.loads(body)
                        logger.info(f"成功捕获并解析目标API JSON响应: {request.url}")
                        logger.info(f"响应数据示例: {json_data}")
                        found_data = json_data
                        break # 找到目标数据后即可停止遍历
                    except json.JSONDecodeError as e:
                        logger.warning(f"警告: 捕获到 JSON 类型响应但解析失败,URL: {request.url}, 错误: {e}")
                    except Exception as e:
                        logger.error(f"处理响应体时发生未知错误,URL: {request.url}, 错误: {e}")
                else:
                    logger.info(f"捕获到非 JSON 响应,URL: {request.url}, Content-Type: {content_type}")

        if not found_data:
            logger.info(f"未捕获到包含 '{api_url_part}' 的目标API请求及其 JSON 响应。")

    except Exception as e:
        logger.error(f"在执行过程中发生错误: {e}")
    finally:
        driver.quit()
        logger.info("浏览器已关闭。")

    return found_data

# --- 示例用法 ---
if __name__ == "__main__":
    # 替换为你的实际场景信息
    example_start_url = "https://www.example.com/data_page" # 假设你的页面URL
    example_element_id = "loadDataButton" # 假设点击这个ID的按钮会触发API请求
    example_api_url_part = "/api/v1/getData" # 假设你的API路径包含这个部分

    # 模拟一个简单的 HTML 页面,用于测试
    # 你需要手动创建一个这样的 HTML 文件或者有一个真实的页面
    # 例如,创建一个 `test_page.html` 文件:
    # ```html
    # <!DOCTYPE html>
    # <html>
    # <head>
    #     <title>Test Page</title>
    #     <script>
    #         function loadData() {
    #             fetch('/api/v1/getData', { method: 'GET' })
    #                 .then(response => response.json())
    #                 .then(data => {
    #                     document.getElementById('result').innerText = JSON.stringify(data, null, 2);
    #                     console.log('Data loaded:', data);
    #                 })
    #                 .catch(error => console.error('Error:', error));
    #         }
    #     </script>
    # </head>
    # <body>
    #     <h1>API Data Loader</h1>
    #     <button id="loadDataButton" onclick="loadData()">Load Data</button>
    #     <pre id="result"></pre>
    # </body>
    # </html>
    # ```
    # 然后你可以用一个本地服务器(如 Python 的 http.server)来提供这个文件
    # 并在 `example_start_url` 中使用 `http://localhost:8000/test_page.html`

    # 注意:为了让此示例代码运行,你需要有一个真实的网页和 API 端点。
    # 如果只是为了演示 selenium-wire 的功能,可以访问一个已知会触发 AJAX 请求的公共网站,
    # 并调整 element_id_to_click 和 api_url_part。

    # 假设我们有一个本地测试服务器运行在 8000 端口,提供 test_page.html
    # example_start_url = "http://localhost:8000/test_page.html"
    # example_element_id = "loadDataButton"
    # example_api_url_part = "/api/v1/getData"

    # 运行捕获函数
    api_response_data = capture_api_response(
        start_url=example_start_url,
        element_id_to_click=example_element_id,
        api_url_part=example_api_url_part
    )

    if api_response_data:
        print("\n最终捕获到的 API 响应数据:")
        print(json.dumps(api_response_data, indent=2, ensure_ascii=False))
    else:
        print("\n未能成功捕获到目标 API 响应数据。")
登录后复制

示例代码解析

  1. 导入必要的库: selenium_wire.webdriver 是核心,用于创建支持网络监听的 WebDriver 实例。其他如 By, WebDriverWait, EC 用于 Selenium 常见操作,json 用于处理 JSON 响应,time 用于简单的等待,logging 用于输出信息。
  2. capture_api_response 函数: 封装了整个捕获逻辑,提高了代码的复用性。
  3. WebDriver 初始化: driver = webdriver.Chrome(options=options) 创建了一个 selenium-wire 增强的 Chrome WebDriver 实例。所有通过此 driver 发起的请求都会被 selenium-wire 代理。
  4. 访问页面: driver.get(start_url) 导航到目标网页。
  5. 清除请求记录: driver.delete_all_requests() 是一个非常关键的步骤。在执行可能触发 API 请求的操作之前调用它,可以确保 driver.requests 列表中只包含后续操作产生的网络流量,避免混淆。
  6. 模拟用户操作: 使用 WebDriverWait 和 EC 确保元素加载并可点击,然后通过 element_to_click.click() 模拟用户点击操作,这个操作预期会触发一个 API 请求。
  7. 等待请求完成: time.sleep(5) 提供了一个简单的等待机制,确保浏览器有足够的时间来发送请求并接收响应。在实际应用中,更健壮的方法可能是等待某个特定的 DOM 元素出现或消失,或者使用 WebDriverWait 结合自定义的 expected_conditions 来判断 API 请求是否完成。
  8. 遍历并过滤请求:
    • for request in driver.requests: 遍历 selenium-wire 捕获到的所有请求。
    • if request.response and api_url_part in request.url: 这是一个重要的过滤条件。它首先检查请求是否有对应的响应(即请求已完成),然后检查请求的 URL 是否包含我们关注的 API 端点的一部分。
    • if 'application/json' in content_type: 进一步过滤,确保响应是 JSON 格式。
    • body = request.response.body.decode('utf-8') 获取响应体,它通常是字节流,需要解码成字符串。
    • json_data = json.loads(body) 将字符串解析成 Python 字典。
  9. 错误处理与日志: 使用 try...except 块处理 JSON 解析错误或其他异常,并利用 logging 模块输出详细的运行信息,便于调试和监控。
  10. 关闭浏览器: driver.quit() 在操作完成后关闭浏览器实例,释放资源。

注意事项

  • 异步性: 网络请求是异步的。在点击触发 API 的元素后,需要给予浏览器足够的时间来完成请求和响应。简单的 time.sleep() 并非最佳实践,应考虑更智能的等待策略,例如等待特定数据出现在页面上,或者等待 driver.requests 列表中出现符合条件的请求。
  • 请求过滤: 根据实际需求,可以组合多种过滤条件(如 request.method, request.headers, request.body 等)来精确匹配目标 API 请求。
  • 响应体编码 响应体 request.response.body 是字节数据,需要根据实际编码(通常是 UTF-8)进行解码。
  • 代理配置: selenium-wire 默认会为 WebDriver 配置一个代理。如果你的测试环境有特殊的网络代理需求,可以通过 selenium_wire.webdriver.Chrome(seleniumwire_options={'proxy': ...}) 来进行配置。
  • 资源清理: 每次执行完测试后,务必调用 driver.quit() 关闭浏览器实例,防止资源泄露。
  • 无头模式: 在 CI/CD 环境中,通常会使用无头模式 (options.add_argument('--headless')) 运行浏览器,这不会影响 selenium-wire 的功能。

总结

selenium-wire 极大地扩展了 Selenium 在网络流量监控方面的能力,使其成为一个强大的工具,不仅能模拟用户交互,还能深入洞察前端与后端之间的通信。通过本文介绍的方法,开发者可以轻松地在自动化测试中捕获和分析 API 请求及其响应,这对于验证数据流、检测后端问题或在复杂的单页应用中提取关键数据至关重要。掌握 selenium-wire 将使你的自动化测试和数据抓取能力提升到一个新的水平。

以上就是使用 Selenium-Wire 捕获和分析前端网络请求的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号