C# 利用Selenium实现浏览器自动化操作

  • A+
所属分类:.NET技术
摘要

Selenium是一款免费的分布式的自动化测试工具,支持多种开发语言,无论是C、 java、ruby、python、或是C# ,你都可以通过selenium完成自动化测试。本文以一个简单的小例子,简述C# 利用Selenium进行浏览器的模拟操作,仅供学习分享使用,如有不足之处,还请指正。


概述

Selenium是一款免费的分布式的自动化测试工具,支持多种开发语言,无论是C、 java、ruby、python、或是C# ,你都可以通过selenium完成自动化测试。本文以一个简单的小例子,简述C# 利用Selenium进行浏览器的模拟操作,仅供学习分享使用,如有不足之处,还请指正。

涉及知识点

要实现本例的功能,除了要掌握Html ,JavaScript,CSS等基础知识,还涉及以下知识点:

  • log4net:主要用于日志的记录和存储,本例采用log4net进行日志记录,便于过程跟踪和问题排查,关于log4net的配置和介绍,之前已有说明,本文不做赘述。
  • Queue:队列,先进先出模式,本文主要用于将日志信息保存于队列中,然后再显示到页面上,其中Enqueue用于添加内容到结尾处,Dequeue用于返回并移除一个位置的对象。
  • IWebDriver:浏览器驱动接口,所有的关于浏览器的操作都可以通过此接口进行,不同浏览器有不同的实现类,如:IE浏览器(InternetExplorerDriver)Chrome浏览器(ChromeDriver)等。
  • BackgroundWorker:后台工作线程,区别于主线程,通过事件触发不同的状态。

Selenium安装

本例开发工具为VS2019,通过NuGet进行需要的软件包的安装与管理,如下所示:

C# 利用Selenium实现浏览器自动化操作

示例效果图

本例采用Chrome浏览器,用于监控某一个网站并获取相应内容,如下所示:

C# 利用Selenium实现浏览器自动化操作

Selenium示例介绍

定义一个webDriver,如下所示:

1 //谷歌浏览器 2 ChromeOptions options = new ChromeOptions(); 3 this.driver = new ChromeDriver(options);

通过ID获取元素并填充内容和触发事件,如下所示:

1 this.driver.FindElement(By.Id("email")).SendKeys(username); 2 this.driver.FindElement(By.Id("password")).SendKeys(password); 3 //# 7. 点击登录按钮 4 this.driver.FindElement(By.Id("sign-in")).Click();

通过XPath获取元素,如下所示:

1 string xpath1 = "//div[@class="product-list"]/div[@class="product"]/div[@class="price-and-detail"]/div[@class="price"]/span[@class="noStock"]"; 2 string txt = this.driver.FindElement(By.XPath(xpath1)).Text;

核心代码

主要的核心代码,就是浏览器的元素定位查找和事件触发,如下所示:

C# 利用Selenium实现浏览器自动化操作C# 利用Selenium实现浏览器自动化操作

  1 using OpenQA.Selenium;   2 using OpenQA.Selenium.IE;   3 using OpenQA.Selenium.Chrome;   4 using System;   5 using System.Collections.Generic;   6 using System.Linq;   7 using System.Text;   8 using System.Threading;   9 using System.Threading.Tasks;  10   11 namespace AiSmoking.Core  12 {  13     public class Smoking  14     {  15         /// <summary>  16         /// 是否正在运行  17         /// </summary>  18         private bool running = false;  19   20         /// <summary>  21         /// 驱动  22         /// </summary>  23         private IWebDriver driver = null;  24   25   26         /// <summary>  27         /// # 无货  28         /// </summary>  29         private string no_stock = "Currently Out of Stock";  30   31   32         /// <summary>  33         ///   # 线程等待秒数  34         /// </summary>  35         private int wait_sec = 2;  36   37         private Dictionary<string, string> cfg_info;  38   39         private string work_path = string.Empty;  40   41         /// <summary>  42         /// 构造函数  43         /// </summary>  44         public Smoking()  45         {  46   47         }  48   49         /// <summary>  50         /// 带参构造函数  51         /// </summary>  52         /// <param name="cfg_info"></param>  53         /// <param name="work_path"></param>  54         public Smoking(Dictionary<string, string> cfg_info,string work_path)  55         {  56             this.cfg_info = cfg_info;  57             this.work_path = work_path;  58             this.wait_sec = int.Parse(cfg_info["wait_sec"]);  59             //# 如果小于2,则等于2  60             this.wait_sec = (this.wait_sec < 2 ? 2 : this.wait_sec);  61             this.wait_sec = this.wait_sec * 1000;  62         }  63   64         /// <summary>  65         /// 开始跑  66         /// </summary>  67         public void startRun()  68         {  69             //"""运行起来"""  70             try  71             {  72                 this.running = true;  73                 string url = this.cfg_info["url"];  74                 string username = this.cfg_info["username"];  75                 string password = this.cfg_info["password"];  76                 string item_id = this.cfg_info["item_id"];  77                 if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password) || string.IsNullOrEmpty(item_id))  78                 {  79                     LogHelper.put("配置信息不全,请检查config.cfg文件是否为空,然后再重启");  80                     return;  81                 }  82                 if (this.driver == null)  83                 {  84                     string explorer = this.cfg_info["explorer"];  85                     if (explorer == "Chrome")  86                     {  87                         //谷歌浏览器  88                         ChromeOptions options = new ChromeOptions();  89                         this.driver = new ChromeDriver(options);  90                     }  91                     else  92                     {  93                         //默认IE  94                         var options = new InternetExplorerOptions();  95                         //options.AddAdditionalCapability.('encoding=UTF-8')  96                         //options.add_argument('Accept= text / css, * / *')  97                         //options.add_argument('Accept - Language= zh - Hans - CN, zh - Hans;q = 0.5')  98                         //options.add_argument('Accept - Encoding= gzip, deflate')  99                         //options.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko') 100                         //# 2. 定义浏览器驱动对象 101                         this.driver = new InternetExplorerDriver(options); 102                     } 103                 } 104                 this.run(url, username, password, item_id); 105             } 106             catch (Exception e) 107             { 108                 LogHelper.put("运行过程中出错,请重新打开再试"+e.StackTrace); 109             } 110         } 111  112  113         /// <summary> 114         /// 运行 115         /// </summary> 116         /// <param name="url"></param> 117         /// <param name="username"></param> 118         /// <param name="password"></param> 119         /// <param name="item_id"></param> 120         private void run(string url, string username, string password, string item_id) 121         { 122             //"""运行起来""" 123             //# 3. 访问网站 124             this.driver.Navigate().GoToUrl(url); 125             //# 4. 最大化窗口 126             this.driver.Manage().Window.Maximize(); 127             if (this.checkIsExists(By.LinkText("账户登录"))) 128             { 129                 //# 判断是否登录:未登录 130                 this.login(username, password); 131             } 132             if (this.checkIsExists(By.PartialLinkText("欢迎回来"))) 133             { 134                 //# 判断是否登录:已登录 135                 LogHelper.put("登录成功,下一步开始工作了"); 136                 this.working(item_id); 137             } 138             else 139             { 140                 LogHelper.put("登录失败,请设置账号密码"); 141             } 142         } 143  144         /// <summary> 145         /// 停止运行 146         /// </summary> 147         public void stopRun() 148         { 149             //"""停止""" 150             try 151             { 152                 this.running = false; 153                 //# 如果驱动不为空,则关闭 154                 //self.close_browser_nicely(self.__driver) 155                 if (this.driver != null) 156                 { 157                     this.driver.Quit(); 158                     //# 关闭后切要为None,否则启动报错 159                     this.driver = null; 160                 } 161             } 162             catch (Exception e) 163             { 164                 //print('Stop Failure') 165             } 166             finally 167             { 168                 this.driver = null; 169             } 170         } 171  172  173         private void login(string username, string password) 174         { 175             //# 5. 点击链接跳转到登录页面 176             this.driver.FindElement(By.LinkText("账户登录")).Click(); 177             //# 6. 输入账号密码 178             //# 判断是否加载完成 179             if (this.checkIsExists(By.Id("email"))) 180             { 181                 this.driver.FindElement(By.Id("email")).SendKeys(username); 182                 this.driver.FindElement(By.Id("password")).SendKeys(password); 183                 //# 7. 点击登录按钮 184                 this.driver.FindElement(By.Id("sign-in")).Click(); 185             } 186         } 187  188         /// <summary> 189         /// 工作状态 190         /// </summary> 191         /// <param name="item_id"></param> 192         private void working(string item_id) 193         { 194             while (this.running) 195             { 196                 try 197                 { 198                     //# 正常获取信息 199                     if (this.checkIsExists(By.Id("string"))) 200                     { 201                         this.driver.FindElement(By.Id("string")).Clear(); 202                         this.driver.FindElement(By.Id("string")).SendKeys(item_id); 203                         this.driver.FindElement(By.Id("string")).SendKeys(Keys.Enter); 204                     } 205                     //# 判断是否查询到商品 206                     string xpath = "//div[@class="specialty-header search"]/div[@class="specialty-description"]/div[@class="gt-450"]/span[2] "; 207                     if (this.checkIsExists(By.XPath(xpath))) 208                     { 209                         int count = int.Parse(this.driver.FindElement(By.XPath(xpath)).Text); 210                         if (count < 1) 211                         { 212                             Thread.Sleep(this.wait_sec); 213                             LogHelper.put("没有查询到item id =" + item_id + "对应的信息"); 214                             continue; 215                         } 216                     } 217                     else 218                     { 219                         Thread.Sleep(this.wait_sec); 220                         LogHelper.put("没有查询到item id2 =" + item_id + "对应的信息"); 221                         continue; 222                     } 223                     //# 判断当前库存是否有货 224  225                     string xpath1 = "//div[@class="product-list"]/div[@class="product"]/div[@class="price-and-detail"]/div[@class="price"]/span[@class="noStock"]"; 226                     if (this.checkIsExists(By.XPath(xpath1))) 227                     { 228                         string txt = this.driver.FindElement(By.XPath(xpath1)).Text; 229                         if (txt == this.no_stock) 230                         { 231                             //# 当前无货 232                             Thread.Sleep(this.wait_sec); 233                             LogHelper.put("查询一次" + item_id + ",无货"); 234                             continue; 235                         } 236                     } 237                     //# 链接path1 238                     string xpath2 = "//div[@class="product-list"]/div[@class="product"]/div[@class="imgDiv"]/a"; 239                     //# 判断是否加载完毕 240                     //# this.waiting((By.CLASS_NAME, "imgDiv")) 241                     if (this.checkIsExists(By.XPath(xpath2))) 242                     { 243                         this.driver.FindElement(By.XPath(xpath2)).Click(); 244                         Thread.Sleep(this.wait_sec); 245                         //# 加入购物车 246                         if (this.checkIsExists(By.ClassName("add-to-cart"))) 247                         { 248                             this.driver.FindElement(By.ClassName("add-to-cart")).Click(); 249                             LogHelper.put("加入购物车成功,商品item-id:" + item_id); 250                             break; 251                         } 252                         else 253                         { 254                             LogHelper.put("未找到加入购物车按钮"); 255                         } 256                     } 257                     else 258                     { 259                         LogHelper.put("没有查询到,可能是商品编码不对,或者已下架"); 260                     } 261                     Thread.Sleep(this.wait_sec); 262                 } 263                 catch (Exception e) 264                 { 265                     Thread.Sleep(this.wait_sec); 266                     LogHelper.put(e); 267                 } 268             } 269         } 270  271         /// <summary> 272         /// 判断是否存在 273         /// </summary> 274         /// <param name="by"></param> 275         /// <returns></returns> 276         private bool checkIsExists(By by) 277         { 278             try 279             { 280                 int i = 0; 281                 while (this.running && i < 3) 282                 { 283                     if (this.driver.FindElements(by).Count > 0) 284                     { 285                         break; 286                     } 287                     else 288                     { 289                         Thread.Sleep(this.wait_sec); 290                         i = i + 1; 291                     } 292                 } 293                 return this.driver.FindElements(by).Count > 0; 294             } 295             catch (Exception e) 296             { 297                 LogHelper.put(e); 298                 return false; 299             } 300         } 301  302     } 303 }

View Code

关于日志帮助类,代码如下:

C# 利用Selenium实现浏览器自动化操作C# 利用Selenium实现浏览器自动化操作

 1 using System;  2 using System.Collections.Generic;  3 using System.Linq;  4 using System.Text;  5 using System.Threading.Tasks;  6 using log4net;  7   8 [assembly: log4net.Config.XmlConfigurator(Watch = true)]  9 namespace AiSmoking.Core 10 { 11     /// <summary> 12     /// 日志帮助类 13     /// </summary> 14     public static class LogHelper 15     { 16         /// <summary> 17         /// 日志实例 18         /// </summary> 19         private static ILog logInstance = LogManager.GetLogger("smoking"); 20  21         private static Queue<string> queue = new Queue<string>(2000); 22  23         public static void put(string msg) 24         { 25             queue.Enqueue(msg); 26             WriteLog(msg, LogLevel.Info); 27         } 28  29         public static void put(Exception ex) 30         { 31             WriteLog(ex.StackTrace, LogLevel.Error); 32         } 33  34         public static string get() 35         { 36             if (queue.Count > 0) 37             { 38                 return queue.Dequeue(); 39             } 40             else 41             { 42                 return string.Empty; 43             } 44         } 45  46         public static void WriteLog(string message, LogLevel level) 47         { 48             switch (level) 49             { 50                 case LogLevel.Debug: 51                     logInstance.Debug(message); 52                     break; 53                 case LogLevel.Error: 54                     logInstance.Error(message); 55                     break; 56                 case LogLevel.Fatal: 57                     logInstance.Fatal(message); 58                     break; 59                 case LogLevel.Info: 60                     logInstance.Info(message); 61                     break; 62                 case LogLevel.Warn: 63                     logInstance.Warn(message); 64                     break; 65                 default: 66                     logInstance.Info(message); 67                     break; 68             } 69         } 70  71  72     } 73  74  75     public enum LogLevel 76     { 77         Debug = 0, 78         Error = 1, 79         Fatal = 2, 80         Info = 3, 81         Warn = 4 82     } 83 }

View Code

备注

行路难·其一

【作者】李白 【朝代】唐

金樽清酒斗十千,玉盘珍羞直万钱。

停杯投箸不能食,拔剑四顾心茫然。

欲渡黄河冰塞川,将登太行雪满山。

闲来垂钓碧溪上,忽复乘舟梦日边。

行路难,行路难,多歧路,今安在?

长风破浪会有时,直挂云帆济沧海。