Selenium是一款免費的分佈式的自動化測試工具,支持多種開發語言,不管是C、 java、ruby、python、或是C# ,你均可以經過selenium完成自動化測試。本文以一個簡單的小例子,簡述C# 利用Selenium進行瀏覽器的模擬操做,僅供學習分享使用,若有不足之處,還請指正。css
要實現本例的功能,除了要掌握Html ,JavaScript,CSS等基礎知識,還涉及如下知識點:java
本例開發工具爲VS2019,經過NuGet進行須要的軟件包的安裝與管理,以下所示:python
本例採用Chrome瀏覽器,用於監控某一個網站並獲取相應內容,以下所示:web
定義一個webDriver,以下所示:瀏覽器
1 //谷歌瀏覽器 2 ChromeOptions options = new ChromeOptions(); 3 this.driver = new ChromeDriver(options);
經過ID獲取元素並填充內容和觸發事件,以下所示:ruby
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;
主要的核心代碼,就是瀏覽器的元素定位查找和事件觸發,以下所示:ide
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 }
關於日誌幫助類,代碼以下:函數
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 }
行路難·其一工具
金樽清酒鬥十千,玉盤珍羞直萬錢。
停杯投箸不能食,拔劍四顧心茫然。
欲渡黃河冰塞川,將登太行雪滿山。
閒來垂釣碧溪上,忽復乘舟夢日邊。
行路難,行路難,多歧路,今安在?
長風破浪會有時,直掛雲帆濟滄海。