如何让Selenium ChromeDriver在元素变化时执行回调(C#)
监听网页Div内容变化的C#实现方案
要替代轮询实现精准响应Div内容变化,核心是利用浏览器原生的MutationObserver API监听DOM变更,再通过C#与网页的通信机制把变更事件同步到C#代码中。以下分两种常见场景给出实现方案:
场景一:使用Selenium自动化浏览器
适合爬虫或自动化测试场景,通过Selenium的DevTools协议接收网页发送的变更通知:
实现步骤
- 初始化Selenium驱动并加载目标网页
- 注入
MutationObserver脚本监听目标Div的内容变化 - 通过DevTools协议监听网页的控制台输出接收变更数据
using OpenQA.Selenium; using OpenQA.Selenium.Chrome; using OpenQA.Selenium.DevTools; using OpenQA.Selenium.DevTools.V119.Runtime; using System.Text.Json; // 初始化Chrome驱动 IWebDriver driver = new ChromeDriver(); driver.Navigate().GoToUrl("你的目标网页URL"); // 注入MutationObserver脚本,监听mydiv的内容变更 var observerScript = @" const target = document.getElementById('mydiv'); const observer = new MutationObserver(mutations => { mutations.forEach(() => { // 将变更内容序列化为JSON并输出到控制台 console.log(JSON.stringify({ type: 'divUpdate', content: target.textContent.trim() })); }); }); // 配置监听规则:监听子节点变化(内容变更属于子节点更新) observer.observe(target, { childList: true, subtree: true }); "; ((IJavaScriptExecutor)driver).ExecuteScript(observerScript); // 启用DevTools会话监听控制台输出 var devTools = driver as IDevTools; var session = devTools.GetDevToolsSession(); await session.Runtime.Enable(new EnableCommandSettings()); // 注册控制台消息回调 session.Runtime.ConsoleAPICalled += (sender, e) => { if (e.Type != ConsoleAPICalledEventType.Log) return; foreach (var arg in e.Args) { var rawData = arg.Value.ToString(); if (!rawData.Contains("divUpdate")) continue; var updateData = JsonSerializer.Deserialize<DivUpdateData>(rawData); // 这里处理内容变更逻辑 Console.WriteLine($"Div内容已更新:{updateData.Content}"); } }; // 用于接收变更数据的模型类 public class DivUpdateData { public string Type { get; set; } public string Content { get; set; } }
场景二:使用WebView2嵌入网页(WinForms/WPF)
适合桌面应用嵌入网页的场景,WebView2支持JS与C#的双向直接通信,实现更简洁:
实现步骤
- 初始化WebView2控件并加载网页
- 将C#回调对象注册到JS全局环境
- 注入
MutationObserver脚本,触发时直接调用C#的回调方法
using Microsoft.Web.WebView2.Core; using System.Runtime.InteropServices; using System.Windows.Forms; public partial class MainForm : Form { public MainForm() { InitializeComponent(); Load += MainForm_Load; } private async void MainForm_Load(object sender, EventArgs e) { // 初始化WebView2 await webView21.EnsureCoreWebView2Async(); webView21.CoreWebView2.Navigate("你的目标网页URL"); // 将C#回调对象注册到JS,命名为DivChangeHandler webView21.CoreWebView2.AddHostObjectToScript("DivChangeHandler", new DivChangeCallback()); // 注入MutationObserver脚本 var observerScript = @" const target = document.getElementById('mydiv'); const observer = new MutationObserver(mutations => { mutations.forEach(() => { // 直接调用C#的回调方法 window.chrome.webview.hostObjects.sync.DivChangeHandler.OnContentChanged(target.textContent.trim()); }); }); observer.observe(target, { childList: true, subtree: true }); "; await webView21.CoreWebView2.ExecuteScriptAsync(observerScript); } } // 回调类需标记为ComVisible才能被JS访问 [ComVisible(true)] public class DivChangeCallback { public void OnContentChanged(string newContent) { // 处理内容变更,注意WinForms/WPF需切换回UI线程操作控件 Console.WriteLine($"Div内容更新为:{newContent}"); // 示例:更新WinForms控件(需Invoke避免跨线程异常) // Application.OpenForms[0].Invoke(new Action(() => { // label1.Text = newContent; // })); } }
关键注意事项
- MutationObserver配置:必须设置
childList: true,因为Div内容变更本质是文本子节点的替换;如果内容包含嵌套元素,需额外开启subtree: true监听所有子节点变更。 - 资源清理:不再需要监听时,需在JS中调用
observer.disconnect()释放资源,避免内存泄漏。 - 跨线程处理:在UI框架中(WinForms/WPF),回调方法运行在非UI线程,需用
Invoke/Dispatcher.Invoke切换回UI线程操作控件。
内容的提问来源于stack exchange,提问作者dxvbhst t
相关产品推荐
相关产品推荐

