使用 C# 和 Selenium WebDriver 实现自动化

需求如果我现在需要对某一个网页 进行运维 运维操作为点击页面按钮查看所有功能是否正常,现在想开发自动化运维的方式替代重复性操作,请提供可行方案, 我目前的想法是 python selenium自动化操作 或者 本质是对接口能否正常返回数据的测试,也可以使用C# python,进行接口测试
如果需要 进行页面 点击 设置账目密码登陆 判断是否登陆成功 或者判断点击后页面响应是否正常,推荐用什么方法,继续用C# 还是换python的呢
判断页面是否加载完成 特定元素是否出现或存在

1.NUGET 下载浏览器对应版本

工具 > NuGet 包管理器 > 管理解决方案的 NuGet 包

搜索并安装以下包:

  • Selenium.WebDriver
  • Selenium.WebDriver.ChromeDriver(如果你使用 Chrome
  • Selenium.Support(可选,用于等待、选择等)
Update-Package Selenium.WebDriver
Update-Package Selenium.WebDriver.ChromeDriver
可检查环境变量
确保 Chrome 的安装目录已经添加到了系统的 PATH 环境变量中。这样可以让操作系统知道在哪里查找 Chrome 可执行文件。如果没有添加,可以手动添加: 
找到 Chrome 的安装目录,通常是 C:\Program Files\Google\Chrome\Application\。
将这个路径添加到系统的 PATH 环境变量中。

2.使用对应版本的driver

  • 访问 EdgeDriver https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/下载页面 确认你需要的版本。
  • 更新或降级 Selenium.WebDriver.MSEdgeDriver 到正确的版本以匹配你的 Edge 浏览器版本
  • 准备浏览器驱动(如 ChromeDriver)
    • 下载 ChromeDriver(版本需与你的 Chrome 浏览器一致)
    • 解压后将 chromedriver.exe 放到项目目录中,或者指定完整路径使用
//IWebDriver driver = new ChromeDriver();
// 初始化EdgeDriver(自动下载匹配的驱动)
IWebDriver driver = new EdgeDriver();

// 指定驱动路径
var service = EdgeDriverService.CreateDefaultService();
Console.WriteLine($"驱动路径: {service.DriverServicePath}");
Console.WriteLine($"驱动是否存在: {File.Exists(service.DriverServicePath)}");
// 指定驱动路径
var options = new EdgeOptions();
//var service = EdgeDriverService.CreateDefaultService(@"C:\Users\AJRSOFT\Desktop\wjl\automaintainer\packages\Selenium.WebDriver.MSEdgeDriver.138.0.3351.83\driver\win32\msedgedriver.exe");
driver = new EdgeDriver(service, options); 

public IWebDriver driver ;

关闭driver后才退出   private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            // 关闭浏览器
            driver.Quit();
            Application.Exit();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            // 关闭浏览器
            driver.Quit();
            Application.Exit();

        }

注意事项

<span b-rctmneqryd="" id="ldd" onclick="window.location.href='/Home/Downloaddb'" style="cursor: pointer; color: rgb(231, 239, 247);">Down</span>

//  点击bak下载按钮
IWebElement houtaiButton = driver.FindElement(By.XPath("/html/body/header/div[2]/div/div/a/button"));
houtaiButton.Click();  
IWebElement bakButton = driver.FindElement(By.XPath("/html/body/header/div[2]/div/div/span[2]"));
bakButton.Click();
为啥第二个按钮不行
  1. <span> 不是可点击元素(默认行为)

  2. 使用 JavaScript 点击更可靠

由于 .Click() 有时会失败,特别是对于非标准可点击元素(如 span),建议你改用 JavaScript 来触发点击事件:

在winform中 怎么使用流程化 展示 使用从上到下的树形 中间圆环节点 的结构

在selenium中明明浏览器已经显示元素,却报错不存在

// 获取当前窗口句柄
            string mainWindowHandle = driver.CurrentWindowHandle;
            UpdateLog("进入研究工具");
            IWebElement bakButton = driver.FindElement(By.XPath("/html/body/div[4]/div[1]/div/div[3]/ul/li[1]/i"));  
            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bakButton);
            // 等待新窗口出现并切换
            WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
            wait.Until(drv => drv.WindowHandles.Count > 1); 
            foreach (string handle in driver.WindowHandles)
            {
                if (handle != mainWindowHandle)
                {
                    driver.SwitchTo().Window(handle);
                    break;
                }
            }
            // 等待新窗口内容加载完成
            wait.Until(drv => {
                try
                {
                    return drv.FindElement(By.Id("treeZJ_1_a")).Displayed;
                }
                catch
                {
                    return false;
                }
            });
            UpdateLog("点击数据分析");  //为啥没有找到id'明明已经切换了tab

可能原因及解决方案

  1. 新窗口尚未完全加载

    // 改进后的等待条件(添加页面就绪状态检查)
    wait.Until(drv => {
        try {
            return ((IJavaScriptExecutor)drv).ExecuteScript("return document.readyState").Equals("complete") 
                   && drv.FindElement(By.Id("treeZJ_1_a")).Displayed;
        } catch {
            return false;
        }
    });
    
  2. 元素在 iframe 中

    // 检查并切换到iframe
    wait.Until(drv => {
        try {
         // 先确保主文档加载完成
            if (!((IJavaScriptExecutor)drv).ExecuteScript("return document.readyState").Equals("complete"))
                return false;
            var iframe = drv.FindElement(By.Id("contentFrame")); // 替换为实际iframe的ID或选择器
            drv.SwitchTo().Frame(iframe);
            return drv.FindElement(By.Id("treeZJ_1_a")).Displayed;
        } catch {
        // 如果失败,切换回默认内容(重要!)
            drv.SwitchTo().DefaultContent();
            return false;
        }
    });
    
      // 检查iframe是否存在
        var iframes = driver.FindElements(By.TagName("iframe"));
        if (iframes.Count > 0) {
            UpdateLog("检测到iframe,尝试切换...");
            driver.SwitchTo().Frame(iframes[0]);
        }
    
    
  3. 元素ID动态生成

    // 使用更灵活的选择器(如XPath通过文本定位)
    wait.Until(drv => drv.FindElement(By.XPath("//a[contains(text(),'数据分析')]")).Displayed);
    
  4. 页面存在延迟渲染(如AJAX)

    // 添加额外等待时间
    Thread.Sleep(1000); // 临时方案(不推荐长期使用)
    // 或使用更智能的等待
    wait.Until(drv => (bool)((IJavaScriptExecutor)drv)
        .ExecuteScript("return jQuery.active == 0")); // 等待jQuery请求完成
    
  5. 窗口切换未成功

    // 验证窗口切换是否真正生效
    string newWindowHandle = driver.CurrentWindowHandle;
    if (newWindowHandle == mainWindowHandle) {
        UpdateLog("错误:窗口切换失败!");
        return;
    }
    

查找页面元素 一层CheckElementExists


        public bool CheckElementExists(IWebDriver driver, By elementLocator, int timeoutSeconds = 10)
        {
            var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutSeconds));
            try
            {
                return wait.Until(drv =>
                {
                    try
                    { 
                        return drv.FindElement(elementLocator).Displayed;
                    }
                    catch (NoSuchElementException)
                    {
                        UpdateLog("等待页面元素未出现"); 
                        return false;
                    }
                });
            }
            catch (WebDriverTimeoutException)
            {
                UpdateLog("等待页面加载超时");
                return false;
            }
        }

封装好的 CheckElementInIframeExists 方法,它可以智能地在 iframe 中查找元素

/// <summary>
/// 在iframe中检查元素是否存在并可交互
/// </summary>
/// <param name="driver">WebDriver实例</param>
/// <param name="elementLocator">元素定位器(By对象)</param>
/// <param name="iframeLocator">iframe定位器(By对象)</param>
/// <param name="timeoutSeconds">超时时间(秒)</param>
/// <returns>是否找到元素</returns>
public bool CheckElementInIframeExists(
    IWebDriver driver, 
    By elementLocator, 
    By iframeLocator = null, 
    int timeoutSeconds = 10)
{
    var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutSeconds));
    
    try
    {
        return wait.Until(drv => 
        {
            try
            {
                // 1. 确保主文档就绪
                if (!((IJavaScriptExecutor)drv).ExecuteScript("return document.readyState").Equals("complete"))
                    return false;

                // 2. 切换到iframe(如果指定了iframe定位器)
                if (iframeLocator != null)
                {
                    var iframe = drv.FindElement(iframeLocator);
                    drv.SwitchTo().Frame(iframe);
                }

                // 3. 查找目标元素
                var element = drv.FindElement(elementLocator);
                bool isVisible = element.Displayed && element.Enabled;

                // 4. 无论成功与否都切换回主文档
                drv.SwitchTo().DefaultContent();
                return isVisible;
            }
            catch (NoSuchElementException)
            {
                drv.SwitchTo().DefaultContent();
                UpdateLog($"元素未找到: {elementLocator}");
                return false;
            }
            catch (StaleElementReferenceException)
            {
                drv.SwitchTo().DefaultContent();
                UpdateLog("元素状态已过期,正在重试...");
                return false;
            }
            catch (Exception ex)
            {
                drv.SwitchTo().DefaultContent();
                UpdateLog($"检查元素时出错: {ex.Message}");
                return false;
            }
        });
    }
    catch (WebDriverTimeoutException)
    {
        UpdateLog($"操作超时,未找到元素: {elementLocator}");
        return false;
    }
}

使用示例

// 示例1:在指定iframe中查找元素
bool exists = CheckElementInIframeExists(
    driver, 
    elementLocator: By.Id("treeZJ_1_a"), 
    iframeLocator: By.Name("ifr1"),
    timeoutSeconds: 15);

高级用法(嵌套iframe场景):

// 第一层iframe
if (CheckElementInIframeExists(driver, By.Name("outerFrame")))
{
    // 第二层iframe
    CheckElementInIframeExists(
        driver, 
        By.Id("targetElement"), 
        By.Id("innerFrame"));
}

元素为什么一会找到,一会没找到

   // 获取当前窗口句柄
            string mainWindowHandle = driver.CurrentWindowHandle;
            UpdateLog("进入研究工具");
            IWebElement bakButton = driver.FindElement(By.XPath("/html/body/div[4]/div[1]/div/div[3]/ul/li[1]/i"));  
            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", bakButton);
            // 等待新窗口出现并切换
            WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
            wait.Until(drv => drv.WindowHandles.Count > 1); 
            foreach (string handle in driver.WindowHandles)
            {
                if (handle != mainWindowHandle)
                {
                    driver.SwitchTo().Window(handle);
                    break;
                }
            }
            // 等待新窗口内容加载完成
            wait.Until(drv => {
                try
                {
                    return drv.FindElement(By.Id("treeZJ_1_a")).Displayed;
                }
                catch
                {
                    return false;
                }
            });
            UpdateLog("点击数据分析");  //为啥没有找到 明明已经切换了tab
            IWebElement analyButton = driver.FindElement(By.Id("treeZJ_1_a"));  
            ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", analyButton);

​ bool isliumingSuccess = CheckElementInIframeExists(driver, By.XPath("/html/body/div[5]/div[2]/div[1]/div[1]/fieldset[2]/div/div[3]/a[81]"), By.Name("ifr"));

By.XPath("//fieldset[contains(@class,'opotion')]//a[contains(text(),'碱敏分析')]")

同级 的iframe 必须切换回原文档

时间等待不够吗

​ bool isliumingSuccess = CheckElementInIframeExists(driver, By.XPath("/html/body/div[5]/div[2]/div[1]/div[1]/fieldset[2]/div/div[3]/a[81]"), By.Id("ifr"));

by name 和by id 测试

为什么直接执行找不到元素,慢慢调试就能找到

原因 WebDriver 自动化测试中的经典难题,根本原因在于 运行时与调试时的时序差异绝对 XPath 的脆弱性

修复

第二步:增强 iframe 等待机制

  /// <summary>
        /// 在iframe中检查元素是否存在并可交互
        /// </summary>
        /// <param name="driver">WebDriver实例</param>
        /// <param name="elementLocator">元素定位器(By对象)</param>
        /// <param name="iframeLocator">iframe定位器(By对象)</param>
        /// <param name="timeoutSeconds">超时时间(秒)</param>
        /// <returns>是否找到元素</returns>
        public bool CheckElementInIframeExists(
            IWebDriver driver,
            By elementLocator,
            By iframeLocator = null,
            int timeoutSeconds = 15)
        {
            try { 
                // 1. 主文档就绪 + iframe存在双重检查
                new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutSeconds))
                    .Until(drv =>
                        ((IJavaScriptExecutor)drv).ExecuteScript("return document.readyState").Equals("complete")
                        && drv.FindElements(iframeLocator).Count > 0);
                // 2. 切换到iframe并等待其就绪
                driver.SwitchTo().Frame(driver.FindElement(iframeLocator));
                new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutSeconds))
                    .Until(drv => ((IJavaScriptExecutor)drv)
                        .ExecuteScript("return document.readyState").Equals("complete"));
                // 3. 查找元素(带重试机制)
                for (int i = 0; i < 3; i++)
                {
                    try
                    {
                        var element = driver.FindElement(elementLocator);
                        if (element.Displayed && element.Enabled)
                            return true;
                    }
                    catch
                    {
                        Thread.Sleep(1000); // 每次重试间隔1秒
                    }
                }
                return false;
            }
            finally { }
    }

第三步:添加智能等待策略

// 专用iframe等待方法
public void WaitForIframeContent(IWebDriver driver, By iframeLocator, int timeout = 15)
{
    var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeout));
    
    // 等待iframe存在并可用
    var iframe = wait.Until(drv => 
    {
        var frame = drv.FindElement(iframeLocator);
        return frame.Displayed && frame.Enabled ? frame : null;
    });

    // 切换到iframe并等待内容
    driver.SwitchTo().Frame(iframe);
    try 
    {
        wait.Until(drv => 
            ((IJavaScriptExecutor)drv).ExecuteScript(
                "return document.readyState === 'complete' && " +
                "document.body.children.length > 0"));
    }
    finally 
    {
        driver.SwitchTo().DefaultContent();
    } 
}

CheckElementExists等待

  CheckElementExists

失败时自动截图

catch (Exception ex)
{
    string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
    ((ITakesScreenshot)driver).GetScreenshot()
        .SaveAsFile($"error_{timestamp}.png");
    throw;
}

窗口切换问题

// 获取当前窗口句柄
                        string mainWindowHandle = driver.CurrentWindowHandle;
                        IWebElement cao1Button = driver.FindElement(By.XPath("/html/body/div[1]/div[4]/div/fieldset/div[2]/div/div[7]/div[2]/span[4]"));
                        ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].click();", cao1Button);
                        // 等待新窗口出现并切换
                        WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
                        wait.Until(drv => drv.WindowHandles.Count > 1);
                        foreach (string handle in driver.WindowHandles)
                        {
                            if (handle != mainWindowHandle)
                            {
                                driver.SwitchTo().Window(handle);
                                break;
                            }
                        } 本来是要进入出现的第三个窗口,但是进入了第一个去了

问题分析

  1. 窗口句柄顺序不可靠WindowHandles 集合的顺序可能与窗口打开顺序不一致
  2. 当前逻辑缺陷:代码只是简单地切换到「非当前窗口」的第一个窗口

替代方案(更可靠)

如果窗口有可预测的特征,最好通过标题或URL来识别:

// 方法1:通过标题切换
string targetTitle = "目标窗口标题";
var targetWindow = driver.WindowHandles
    .FirstOrDefault(h => {
        driver.SwitchTo().Window(h);
        return driver.Title.Contains(targetTitle);
    });

// 方法2:通过URL切换
string targetUrl = "expected-part-of-url";
var targetWindow = driver.WindowHandles
    .FirstOrDefault(h => {
        driver.SwitchTo().Window(h);
        return driver.Url.Contains(targetUrl);
    });

if (targetWindow != null)
{
    driver.SwitchTo().Window(targetWindow);
}
else
{
    throw new NoSuchWindowException("未找到目标窗口");
}

定义

导航直接进入链接

driver.Navigate().GoToUrl(url); 这个是在原窗口修改链接直接进入,怎么返回原链接呢

// 1. 先访问 A 页面
driver.Navigate().GoToUrl("https://example.com/pageA"); 
// 2. 再访问 B 页面
driver.Navigate().GoToUrl("https://example.com/pageB"); 
// 3. 返回上一页(即 pageA)
driver.Navigate().Back();
// 再次前往 pageB
driver.Navigate().Forward();
// 返回 A 页面(直接重新访问)
driver.Navigate().GoToUrl(urlA);

dirver每次打开都要出现你的连接不是专用连接,怎么办,

在使用 Selenium 的 EdgeDriver(或其他浏览器驱动)时,如果每次打开网页都遇到 "你的连接不是专用连接"(NET::ERR_CERT_AUTHORITY_INVALID) 的安全警告,通常是因为:

  1. 网站证书无效(自签名证书/过期证书/非受信任 CA 签发)
  2. 浏览器严格的安全策略
  3. WebDriver 未配置忽略证书错误

解决方案(5 种方法)

方法 1:让 WebDriver 忽略证书错误(推荐)

在初始化 EdgeOptions 时,添加 AcceptInsecureCertificates 或绕过证书错误:

var options = new EdgeOptions();
options.AddArgument("--ignore-certificate-errors");  // 忽略证书错误
options.AddArgument("--allow-insecure-localhost");  // 允许不安全本地主机
options.AddArgument("--disable-web-security");      // 禁用同源策略(慎用)

// 如果使用 Selenium 4.6+
options.AcceptInsecureCertificates = true;

driver = new EdgeDriver(options);

方法 2:手动添加证书为信任(适用于自签名证书)

  1. 访问目标网站,手动导出证书(Chrome/Edge → 点击锁图标 → "证书" → "导出")。
  2. 将证书安装到 "受信任的根证书颁发机构"(Windows 证书管理器)。
  3. 重启浏览器。

方法 3:强制使用 HTTPS(适用于开发环境)

如果是本地测试(如 https://localhost),可改用 http:// 或配置有效的本地证书。


方法 4:禁用浏览器安全策略(不推荐生产环境)

var options = new EdgeOptions();
options.AddArgument("--disable-extensions");
options.AddArgument("--disable-notifications");
options.AddArgument("--disable-popup-blocking");
options.AddArgument("--disable-infobars");
options.AddArgument("--test-type");  // 绕过某些安全限制

driver = new EdgeDriver(options);

方法 5:使用 Firefox 或 Chrome 的特定配置

如果问题仅在 Edge 出现,可尝试切换浏览器:

// Chrome 示例
var chromeOptions = new ChromeOptions();
chromeOptions.AcceptInsecureCertificates = true;
driver = new ChromeDriver(chromeOptions);

临时绕过警告(非自动化方式)

如果只是想手动继续访问,可通过代码自动点击 "高级" → "继续前往"(不推荐,因页面结构可能变):

try {
    driver.FindElement(By.Id("details-button")).Click();  // 点击 "高级"
    driver.FindElement(By.Id("proceed-link")).Click();    // 点击 "继续前往"
} catch { /* 忽略错误 */ }

根本解决方案

  1. 修复服务器证书(最佳实践)。
  2. 如果是内部系统,让 IT 部门部署有效证书(如 Let's Encrypt 或企业 CA 签发)。

选择最适合你场景的方法即可!