2023年11月25日发(作者:)

C#利⽤Selenium实现浏览器⾃动化操作

概述

Selenium是⼀款免费的分布式的⾃动化测试⼯具,⽀持多种开发语⾔,⽆论是C javarubypython、或是C# ,你都可以通过selenium

成⾃动化测试。本⽂以⼀个简单的⼩例⼦,简述C# 利⽤Selenium进⾏浏览器的模拟操作,仅供学习分享使⽤,如有不⾜之处,还请指正。

涉及知识点

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

log4net:主要⽤于⽇志的记录和存储,本例采⽤log4net进⾏⽇志记录,便于过程跟踪和问题排查,关于log4net的配置和介绍,之前已有说

明,本⽂不做赘述。

Queue:队列,先进先出模式,本⽂主要⽤于将⽇志信息保存于队列中,然后再显⽰到页⾯上,其中Enqueue⽤于添加内容到结尾

处,Dequeue⽤于返回并移除⼀个位置的对象。

IWebDriver:浏览器驱动接⼝,所有的关于浏览器的操作都可以通过此接⼝进⾏,不同浏览器有不同的实现类,如:IE浏览器

InternetExplorerDriverChrome浏览器(ChromeDriver)等。

BackgroundWorker:后台⼯作线程,区别于主线程,通过事件触发不同的状态。

Selenium安装

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

⽰例效果图

本例采⽤Chrome浏览器,⽤于监控某⼀个⽹站并获取相应内容,如下所⽰:

Selenium⽰例介绍

定义⼀个webDriver,如下所⽰:

1 //⾕歌浏览器

2 ChromeOptions options = new ChromeOptions();

3 this.driver = new ChromeDriver(options);

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

1 this.ement(("email")).SendKeys(username);

2 this.ement(("password")).SendKeys(password);

3 //# 7. 点击登录按钮

4 this.ement(("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.ement((xpath1)).Text;

核⼼代码

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

1 using um;

2 using ;

3 using ;

4 using System;

5 using c;

6 using ;

7 using ;

8 using ing;

9 using ;

10

11 namespace

12 {

13 public class Smoking

14 {

15 ///

16 /// 是否正在运⾏

17 ///

18 private bool running = false;

19

20 ///

21 /// 驱动

22 ///

23 private IWebDriver driver = null;

24

25

26 ///

27 /// # ⽆货

28 ///

29 private string no_stock = "Currently Out of Stock";

30

31

32 ///

33 /// # 线程等待秒数

34 ///

35 private int wait_sec = 2;

36

37 private Dictionary<string, string> cfg_info;

38

39 private string work_path = string.Empty;

39 private string work_path = string.Empty;

40

41 ///

42 /// 构造函数

43 ///

44 public Smoking()

45 {

46

47 }

48

49 ///

50 /// 带参构造函数

51 ///

52 ///

53 ///

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 ///

65 /// 开始跑

66 ///

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 ("配置信息不全,请检查⽂件是否为空,然后再重启");

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 //itionalCapability.('encoding=UTF-8')

96 //_argument('Accept= text / css, * / *')

97 //_argument('Accept - Language= zh - Hans - CN, zh - Hans;q = 0.5')

98 //_argument('Accept - Encoding= gzip, deflate')

99 //_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 ("运⾏过程中出错,请重新打开再试"+race);

109 }

110 }

111

112

113 ///

114 /// 运⾏

115 ///

116 ///

117 ///

118 ///

119 ///

120 private void run(string url, string username, string password, string item_id)

121 {

122 //"""运⾏起来"""

123 //# 3. 访问⽹站

123 //# 3. 访问⽹站

124 this.te().GoToUrl(url);

125 //# 4. 最⼤化窗⼝

126 this.().ze();

127 if (this.checkIsExists(xt("账户登录")))

128 {

129 //# 判断是否登录:未登录

130 this.login(username, password);

131 }

132 if (this.checkIsExists(lLinkText("欢迎回来")))

133 {

134 //# 判断是否登录:已登录

135 ("登录成功,下⼀步开始⼯作了");

136 this.working(item_id);

137 }

138 else

139 {

140 ("登录失败,请设置账号密码");

141 }

142 }

143

144 ///

145 /// 停⽌运⾏

146 ///

147 public void stopRun()

148 {

149 //"""停⽌"""

150 try

151 {

152 this.running = false;

153 //# 如果驱动不为空,则关闭

154 //_browser_nicely(self.__driver)

155 if (this.driver != null)

156 {

157 this.();

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.ement(xt("账户登录")).Click();

177 //# 6. 输⼊账号密码

178 //# 判断是否加载完成

179 if (this.checkIsExists(("email")))

180 {

181 this.ement(("email")).SendKeys(username);

182 this.ement(("password")).SendKeys(password);

183 //# 7. 点击登录按钮

184 this.ement(("sign-in")).Click();

185 }

186 }

187

188 ///

189 /// ⼯作状态

190 ///

191 ///

192 private void working(string item_id)

193 {

194 while (this.running)

195 {

196 try

197 {

198 //# 正常获取信息

199 if (this.checkIsExists(("string")))

208 {

209 int count = int.Parse(this.ement((xpath)).Text);

210 if (count < 1)

211 {

212 (this.wait_sec);

213 ("没有查询到item id =" + item_id + "对应的信息");

214 continue;

215 }

216 }

217 else

218 {

219 (this.wait_sec);

220 ("没有查询到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((xpath1)))

227 {

228 string txt = this.ement((xpath1)).Text;

229 if (txt == this.no_stock)

230 {

231 //# 当前⽆货

232 (this.wait_sec);

233 ("查询⼀次" + item_id + ",⽆货");

234 continue;

235 }

236 }

237 //# 链接path1

238 string xpath2 = "//div[@class="product-list"]/div[@class="product"]/div[@class="imgDiv"]/a";

239 //# 判断是否加载完毕

240 //# g((_NAME, "imgDiv"))

241 if (this.checkIsExists((xpath2)))

242 {

243 this.ement((xpath2)).Click();

244 (this.wait_sec);

245 //# 加⼊购物车

246 if (this.checkIsExists(ame("add-to-cart")))

247 {

248 this.ement(ame("add-to-cart")).Click();

249 ("加⼊购物车成功,商品item-id:" + item_id);

250 break;

251 }

252 else

253 {

254 ("未找到加⼊购物车按钮");

255 }

256 }

257 else

258 {

259 ("没有查询到,可能是商品编码不对,或者已下架");

260 }

261 (this.wait_sec);

262 }

263 catch (Exception e)

264 {

265 (this.wait_sec);

266 (e);

267 }

268 }

269 }

270

271 ///

272 /// 判断是否存在

273 ///

274 ///

275 ///

276 private bool checkIsExists(By by)

277 {

278 try

279 {

280 int i = 0;

281 while (this.running && i < 3)

282 {

283 if (this.ements(by).Count > 0)

284 {

285 break;

286 }

287 else

288 {

289 (this.wait_sec);

290 i = i + 1;

291 }

292 }

293 return this.ements(by).Count > 0;

293 return this.ements(by).Count > 0;

294 }

295 catch (Exception e)

296 {

297 (e);

298 return false;

299 }

300 }

301

302 }

303 }

View Code

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

1 using System;

2 using c;

3 using ;

4 using ;

5 using ;

6 using log4net;

7

8 [assembly: figurator(Watch = true)]

9 namespace

10 {

11 ///

12 /// ⽇志帮助类

13 ///

14 public static class LogHelper

15 {

16 ///

17 /// ⽇志实例

18 ///

19 private static ILog logInstance = ger("smoking");

20

21 private static Queue<string> queue = new Queue<string>(2000);

22

23 public static void put(string msg)

24 {

25 e(msg);

26 WriteLog(msg, );

27 }

28

29 public static void put(Exception ex)

30 {

31 WriteLog(race, );

32 }

33

34 public static string get()

35 {

36 if ( > 0)

37 {

38 return e();

39 }

40 else

41 {

42 return string.Empty;

43 }

44 }

45

46 public static void WriteLog(string message, LogLevel level)

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

关于log4net的实例定义,需要由配置⽂件【】⽀撑,如下所⽰:

1 xml version="1.0" encoding="utf-8" ?>

2 <log4net>

3 <root>

4 <level value="DEBUG" />

5 <appender-ref ref="LogFileAppender" />

6 <appender-ref ref="ConsoleAppender" />

7 root>

8 <logger name="smoking">

9 <level value="ALL" />

10 logger>

11 <appender name="LogFileAppender" type="pender" >

12 <param name="File" value="logs/${TMO}" />

13 <StaticLogFileName value="false"/>

14 <param name="AppendToFile" value="true" />

15 <layout type="nLayout">

16 <param name="Header" value="[Header]"/>

17 <param name="Footer" value="[Footer]"/>

18 <param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n"/>

19 layout>

20 <filter type="angeFilter">

21 <param name="LevelMin" value="DEBUG" />

22 <param name="LevelMax" value="ERROR" />

23 filter>

24 appender>

25 <appender name="ConsoleAppender" type="eAppender" >

26 <layout type="nLayout">

27 <param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />

28 layout>

29 appender>

30 log4net>

View Code

还需要在中添加声明,如下所⽰:

1 [assembly: figurator(ConfigFile = "", ConfigFileExtension = "xml", Watch = true)]

备注

⾏路难·其⼀

【作者】李⽩ 【朝代】唐

⾦樽清酒⽃⼗千,⽟盘珍羞直万钱。

停杯投箸不能⾷,拔剑四顾⼼茫然。

欲渡黄河冰塞川,将登太⾏雪满⼭。

闲来垂钓碧溪上,忽复乘⾈梦⽇边。

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

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