You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何记录Selenium/WebDriver启动浏览器中的手动用户操作?

问题描述

我希望记录由Selenium/WebDriver启动的浏览器中的手动用户操作。目前我的脚本可以正常记录自动化操作,但无法记录/打印我在浏览器中执行的手动操作,请问我哪里出错了?

我的脚本

from selenium import webdriver
import logging
from selenium.webdriver.common.by import By
from selenium.webdriver.support.events import EventFiringWebDriver
from selenium.webdriver.support.events import AbstractEventListener

logging.basicConfig(filename='app.log', level=logging.INFO, format='%(asctime)s:%(levelname)s:%(message)s')

class MyListener(AbstractEventListener):
    def __init__(self, logger):
        super().__init__()
        self.logger = logger

    def before_navigate_to(self, url, driver):
        self.logger.info(f"Laden der Seite: {url}")

    def before_find(self, by, value, driver):
        self.logger.info(f"Suche nach {by} mit dem Wert {value}.")

    def after_find(self, by, value, driver):
        self.logger.info(f"Element gefunden: {by} mit dem Wert {value}.")

    def before_click(self, element, driver):
        print(f"Klick auf {element}.")
        self.logger.info(f"Klick auf {element}.")

    def after_click(self, element, driver):
        print(f"Klick auf {element} beendet.")
        self.logger.info(f"Klick auf {element} beendet.")

    def before_change_value_of(self, element, driver):
        print(f"Eingabe in {element}.")
        self.logger.info(f"Eingabe in {element}.")

    def after_change_value_of(self, element, driver):
        print(f"Eingabe in {element} beendet.")
        self.logger.info(f"Eingabe in {element} beendet.")

chrome_options = webdriver.ChromeOptions()
chrome_options.add_experimental_option('excludeSwitches', ['enable-logging'])
chrome_options.add_experimental_option("detach", True)

driver = webdriver.Chrome(options=chrome_options)   
driver = EventFiringWebDriver(driver, MyListener(logging))

driver.get("https://www.google.com/")

nutzungsb = driver.find_element(By.ID,"W0wltc")
nutzungsb.click()

search_box = driver.find_element(By.NAME,"q")
search_box.send_keys("user_input")
search_box.submit()

日志输出(app.log)

2023-03-10 10:39:32,961:INFO:Laden der Seite: https://www.google.com/
2023-03-10 10:39:34,720:INFO:Suche nach id mit dem Wert W0wltc.
2023-03-10 10:39:34,753:INFO:Element gefunden: id mit dem Wert W0wltc.
2023-03-10 10:39:34,753:INFO:Klick auf <selenium.webdriver.remote.webelement.WebElement (session="ba492d8265b7d98601d6c58daafd1d81", element="4e348086-9653-461c-bf8e-6dfc8129c608")>.
2023-03-10 10:39:34,843:INFO:Klick auf <selenium.webdriver.remote.webelement.WebElement (session="ba492d8265b7d98601d6c58daafd1d81", element="4e348086-9653-461c-bf8e-6dfc8129c608")> beendet.
2023-03-10 10:39:34,843:INFO:Suche nach name mit dem Wert q.
2023-03-10 10:39:34,862:INFO:Element gefunden: name mit dem Wert q.
2023-03-10 10:39:34,862:INFO:Eingabe in <selenium.webdriver.remote.webelement.WebElement (session="ba492d8265b7d98601d6c58daafd1d81", element="9174f201-0095-4c9b-9344-ee2fa54bb56b")>.
2023-03-10 10:39:34,937:INFO:Eingabe in <selenium.webdriver.remote.webelement.WebElement (session="ba492d8265b7d98601d6c58daafd1d81", element="9174f201-0095-4c9b-9344-ee2fa54bb56b")> beendet.

问题原因与解决方案

原因

你使用的AbstractEventListener只负责监听WebDriver自身发起的命令,只有当通过Selenium API调用点击、输入等操作时,这些事件才会被触发。而手动操作是直接在浏览器中发起的,WebDriver默认不会捕获这类用户交互事件,所以你的监听器无法记录手动操作。

解决方案

要记录手动操作,需要通过页面注入JavaScript监听事件,或者利用浏览器DevTools协议捕获用户交互。

方案1:注入JavaScript监听页面事件

通过Selenium向页面注入JS代码,监听click、input等用户事件,将事件信息存储在页面全局变量中,再定期通过WebDriver读取这些信息并记录。

修改后的示例脚本:

from selenium import webdriver
import logging
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.events import EventFiringWebDriver
from selenium.webdriver.support.events import AbstractEventListener

logging.basicConfig(filename='app.log', level=logging.INFO, format='%(asctime)s:%(levelname)s:%(message)s')

class MyListener(AbstractEventListener):
    def __init__(self, logger):
        super().__init__()
        self.logger = logger

    def before_navigate_to(self, url, driver):
        self.logger.info(f"Laden der Seite: {url}")

    def before_find(self, by, value, driver):
        self.logger.info(f"Suche nach {by} mit dem Wert {value}.")

    def after_find(self, by, value, driver):
        self.logger.info(f"Element gefunden: {by} mit dem Wert {value}.")

    def before_click(self, element, driver):
        print(f"Klick auf {element}.")
        self.logger.info(f"Klick auf {element}.")

    def after_click(self, element, driver):
        print(f"Klick auf {element} beendet.")
        self.logger.info(f"Klick auf {element} beendet.")

    def before_change_value_of(self, element, driver):
        print(f"Eingabe in {element}.")
        self.logger.info(f"Eingabe in {element}.")

    def after_change_value_of(self, element, driver):
        print(f"Eingabe in {element} beendet.")
        self.logger.info(f"Eingabe in {element} beendet.")

def inject_manual_event_listener(driver):
    # 注入JS,监听点击和输入事件,存储到window.manualEvents数组
    js_script = """
    window.manualEvents = [];
    document.addEventListener('click', function(e) {
        let elemInfo = {
            type: 'click',
            tagName: e.target.tagName,
            id: e.target.id || '无ID',
            className: e.target.className || '无类名',
            timestamp: new Date().toISOString()
        };
        window.manualEvents.push(elemInfo);
    });
    document.addEventListener('input', function(e) {
        let elemInfo = {
            type: 'input',
            tagName: e.target.tagName,
            id: e.target.id || '无ID',
            className: e.target.className || '无类名',
            value: e.target.value,
            timestamp: new Date().toISOString()
        };
        window.manualEvents.push(elemInfo);
    });
    """
    driver.execute_script(js_script)

def log_manual_events(driver, logger):
    # 读取页面中存储的手动事件并记录
    events = driver.execute_script("return window.manualEvents || [];")
    for event in events:
        if event['type'] == 'click':
            msg = f"手动点击元素: 标签={event['tagName']}, ID={event['id']}, 类名={event['className']}, 时间={event['timestamp']}"
        elif event['type'] == 'input':
            msg = f"手动输入内容: 标签={event['tagName']}, ID={event['id']}, 类名={event['className']}, 内容={event['value']}, 时间={event['timestamp']}"
        logger.info(msg)
        print(msg)
    # 清空已记录的事件,避免重复记录
    driver.execute_script("window.manualEvents = [];")

chrome_options = webdriver.ChromeOptions()
chrome_options.add_experimental_option('excludeSwitches', ['enable-logging'])
chrome_options.add_experimental_option("detach", True)

driver = webdriver.Chrome(options=chrome_options)   
driver = EventFiringWebDriver(driver, MyListener(logging))

driver.get("https://www.google.com/")
# 注入手动事件监听器
inject_manual_event_listener(driver)

nutzungsb = driver.find_element(By.ID,"W0wltc")
nutzungsb.click()

search_box = driver.find_element(By.NAME,"q")
search_box.send_keys("user_input")
search_box.submit()

# 等待用户执行手动操作,实际场景可放在循环中定期检查
print("等待用户执行手动操作...")
time.sleep(10)
log_manual_events(driver, logging)

方案2:使用Chrome DevTools协议监听事件

如果需要更全面的交互记录,可以利用Selenium的DevTools接口,直接通过浏览器协议捕获鼠标、键盘等操作,无需注入JS。

示例代码片段:

# 启用DevTools的Input模块
driver.execute_cdp_cmd('Input.enable', {})

# 监听鼠标点击事件
def on_mouse_event(event):
    if event['params']['type'] == 'mousePressed':
        logging.info(f"手动鼠标点击: x={event['params']['x']}, y={event['params']['y']}, 按钮={event['params']['button']}")

driver.add_cdp_listener('Input.dispatchMouseEvent', on_mouse_event)

# 监听键盘输入事件
def on_key_event(event):
    if event['params']['type'] == 'keyDown' and 'text' in event['params']:
        logging.info(f"手动键盘输入: 字符={event['params']['text']}")

driver.add_cdp_listener('Input.dispatchKeyEvent', on_key_event)

注意事项

  • 注入JS的方式受页面内容安全策略(CSP)限制,若页面有严格的CSP配置,脚本可能无法执行。
  • DevTools协议仅支持Chromium内核浏览器(Chrome、Edge等),不同浏览器的事件名称可能略有差异。

内容的提问来源于stack exchange,提问作者Malta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 19:44:57