c# Selenium 如何模拟滑动geetest 验证码
?參考資料https://www.cnblogs.com/hujunmin/p/11506958.html,原博主貼出來的關鍵代碼,但是并不完整。我將補全部分代碼,能正常運行。
? ? ? Nuget:?Selenium.WebDriver,Selenium.WebDriver.ChromeDriver
思路:
一:獲取原始圖片,如下圖:(圖1)
二:獲取原始圖加缺口圖疊加后的圖片
隨意拖動一次后,得到下圖(圖2):
通過JS控制CSS隱藏上圖中紅色塊后,得到原始圖加缺口圖組合后的圖,如下圖:(圖3)
三:對比前2步驟的圖片,獲取缺口位置
對比 圖1 圖3,獲得缺口在圖片的X坐標
四:減去左邊偏移量,獲得移動距離
減去 圖2 中缺口圖起點X坐標(4px)??
五:根據移動距離,計算移動軌跡
極驗驗證碼后臺對滑動軌跡有驗證。若是通過代碼直接勻速直接移動到指定位置,會提示:“圖片被怪物吃掉了”。所以要程序模擬認為滑動操作:
離缺口位置遠,移動速度快。
離缺口位置近,移動速度慢。
需要模擬不小心超過指定位置,然后再慢慢回頭對缺口操作。
六:根據移動軌跡拖動滑塊
調用?Actions 的?MoveByOffset,按照移動軌跡一步步移動。注意:每次移動后?Actions? 要重新 new ,否則會對不上缺口。具體原因自己去找資料(重復 MoveByOffset ,每次移動是之前的累計值)
七:判斷拖動滑塊后是否驗證通過,若不通過,重試
?拖動后,判讀這個按鈕是否還存在
至此,思路完畢。
注意:
原始圖
https://static.geetest.com/pictures/gt/969ffa43c/969ffa43c.webp
原始圖加缺口圖組合后的組合圖
https://static.geetest.com/pictures/gt/969ffa43c/bg/a0a1cdb4c.webp
兩個圖片是無序的,和我在瀏覽器上看到的不一致。
所以對比圖片的時候,需要將無序圖片轉成正常的圖片。
轉換思路一:
經分析極驗驗證碼是把圖片分成52塊小圖片,按照指定順序打亂后,通過css再重新排序顯示的。
知道圖片規則,我們就按照這個規則,把圖片切成52個小圖,然后排序再組合成一張有序的原始圖。
轉換思路二:
直接去瀏覽器上通過顯示隱藏不同的圖片,然后截圖對比(目前我代碼這個思路處理的)。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 | using?System; using?System.Collections.Generic; using?System.Drawing; using?System.Threading; using?OpenQA.Selenium; using?OpenQA.Selenium.Interactions; using?OpenQA.Selenium.Remote; namespace?Sniffer.VerificationCode.VerificationCodes { ????public?class?GeetestSlideVerificationCode : ISlideVerificationCode ????{ ????????#region 屬性 ????????/// <summary> ????????/// 拖動按鈕 ????????/// </summary> ????????private?string?_slidButton =?"gt_slider_knob"; ????????/// <summary> ????????/// 原始圖層 ????????/// </summary> ????????private?string?_originalMap =?"gt_fullbg"; ????????/// <summary> ????????/// 原始圖加缺口背景圖 ????????/// </summary> ????????private?string?_newMap =?"gt_bg"; ????????/// <summary> ????????/// 缺口圖層 ????????/// </summary> ????????private?string?_sliceMap =?"gt_slice"; ????????/// <summary> ????????/// 重試次數 ????????/// </summary> ????????private?int?_tryTimes = 6; ????????/// <summary> ????????/// 缺口圖默認偏移像素 ????????/// </summary> ????????private?int?_leftOffset = 4; ????????private?string?_fullScreenPath = AppDomain.CurrentDomain.BaseDirectory +?"全屏.png"; ????????private?string?_originalMapPath = AppDomain.CurrentDomain.BaseDirectory +?"原圖.png"; ????????private?string?_newMapPath = AppDomain.CurrentDomain.BaseDirectory +?"新圖.png"; ????????#endregion ????????public?bool?Pass(RemoteWebDriver remoteWebDriver) ????????{ ????????????int?failTimes = 0; ????????????bool?flag =?false; ????????????do ????????????{ ????????????????//#TODO 檢查圖層是否正常彈出 ????????????????//截圖 ????????????????Console.WriteLine("開始截圖..."); ????????????????ScreenMap(remoteWebDriver); ????????????????Console.WriteLine("開始計算距離..."); ????????????????//獲取缺口圖層位移距離 ????????????????var?distance = GetDistance(); ????????????????//獲取移動軌跡 ????????????????Console.WriteLine("開始獲取移動軌跡..."); ????????????????var?moveEntitys = GetMoveEntities(distance); ????????????????//移動 ????????????????Console.WriteLine("開始移動..."); ????????????????Move(remoteWebDriver, moveEntitys); ????????????????Console.WriteLine("休眠3秒,顯示等待提交驗證碼..."); ????????????????Thread.Sleep(3000); ????????????????Console.WriteLine("開始檢查認證是否通過..."); ????????????????//檢查移動是否成功 ????????????????flag = CheckSuccess(remoteWebDriver); ????????????????if?(flag) ????????????????????break; ????????????}?while?(++failTimes < _tryTimes); ????????????return?flag; ????????} ????????#region 內部方法 ????????protected??virtual?bool?CheckSuccess(RemoteWebDriver remoteWebDriver) ????????{ ????????????//WebDriverWait wait = new WebDriverWait(remoteWebDriver, TimeSpan.FromSeconds(5)); ????????????//IWebElement gt_ajax_tip = null; ????????????//gt_ajax_tip = wait.Until<IWebElement>((d) => ????????????//{ ????????????//??? try ????????????//??? { ????????????//??????? return d.FindElement(By.CssSelector(".gt_holder .gt_ajax_tip.gt_success")); ????????????//??? } ????????????//??? catch (Exception ex) ????????????//??? { ????????????//??????? return null; ????????????//??? } ????????????//}); ????????????//if (gt_ajax_tip == null) ????????????//{ ????????????//??? Console.WriteLine("驗證失敗,顯示等待6秒刷新驗證碼..."); ????????????//??? Thread.Sleep(6000); ????????????//??? return false; ????????????//} ????????????//else ????????????//{ ????????????//??? return true; ????????????//} ????????????var?gt_slider_knob = remoteWebDriver.FindElementExt(By.ClassName(_slidButton), 10); ????????????if?(gt_slider_knob ==?null) ????????????{ ????????????????return?true; ????????????} ????????????else ????????????{ ????????????????Console.WriteLine("驗證失敗,顯示等待6秒刷新驗證碼..."); ????????????????Thread.Sleep(6000); ????????????????return?false; ????????????} ????????} ????????private?void?Move(RemoteWebDriver remoteWebDriver,List<MoveEntity> moveEntities) ????????{ ????????????var?slidButton = GetSlidButtonElement(remoteWebDriver); ????????????Actions builder =?new?Actions(remoteWebDriver); ????????????builder.ClickAndHold(slidButton).Perform(); ????????????int?offset = 0; ????????????int?index = 0; ????????????foreach?(var?item?in?moveEntities) ????????????{ ????????????????index++; ????????????????builder =?new?Actions(remoteWebDriver); ????????????????builder.MoveByOffset(item.X, item.Y).Perform(); ????????????????//Console.WriteLine("向右總共移動了:" + (offset = offset + item.X)); ????????????????//if (offset != 0 && index != moveEntities.Count) ????????????????//??? Thread.Sleep(item.MillisecondsTimeout / offset); ????????????} ????????????builder.Release().Perform(); ????????} ????????private?List<MoveEntity> GetMoveEntities(int?distance) ????????{ ????????????List<MoveEntity> moveEntities =?new?List<MoveEntity>(); ????????????int?allOffset = 0; ????????????do ????????????{ ????????????????int?offset = 0; ????????????????double?offsetPercentage = allOffset / (double)distance; ????????????????if?(offsetPercentage > 0.5) ????????????????{ ????????????????????if?(offsetPercentage < 0.85) ????????????????????{ ????????????????????????offset =?new?Random().Next(10, 20); ????????????????????} ????????????????????else ????????????????????{ ????????????????????????offset =?new?Random().Next(2, 5); ????????????????????} ????????????????} ????????????????else ????????????????{ ????????????????????offset =?new?Random().Next(20, 30); ????????????????} ????????????????allOffset += offset; ????????????????int?y = (new?Random().Next(0, 1) == 1 ??new?Random().Next(0, 2) : 0 -?new?Random().Next(0, 2)); ????????????????moveEntities.Add(new?MoveEntity(offset,y , offset)); ????????????}?while?(allOffset <= distance + 5); ????????????//最后一部分移動 ????????????var?moveOver = allOffset > distance; ????????????for?(int?j = 0; j < Math.Abs(distance - allOffset);) ????????????{ ????????????????int?step = 3; ????????????????int?offset = moveOver ? -step : step; ????????????????int?sleep =?new?Random().Next(100, 200); ????????????????moveEntities.Add(new?MoveEntity(offset,0, sleep)); ; ????????????????j = j + step; ????????????} ????????????return?moveEntities; ????????} ????????/// <summary> ????????/// 比較兩張圖片的像素,確定陰影圖片位置 ????????/// </summary> ????????/// <param name="oldBmp"></param> ????????/// <param name="newBmp"></param> ????????/// <returns></returns> ????????private?int?GetArgb(Bitmap oldBmp, Bitmap newBmp) ????????{ ????????????//由于陰影圖片四個角存在黑點(矩形1*1) ????????????for?(int?i = 0; i < newBmp.Width; i++) ????????????{ ????????????????for?(int?j = 0; j < newBmp.Height; j++) ????????????????{ ????????????????????if?((i >= 0 && i <= 1) && ((j >= 0 && j <= 1) || (j >= (newBmp.Height - 2) && j <= (newBmp.Height - 1)))) ????????????????????{ ????????????????????????continue; ????????????????????} ????????????????????if?((i >= (newBmp.Width - 2) && i <= (newBmp.Width - 1)) && ((j >= 0 && j <= 1) || (j >= (newBmp.Height - 2) && j <= (newBmp.Height - 1)))) ????????????????????{ ????????????????????????continue; ????????????????????} ????????????????????//獲取該點的像素的RGB的顏色 ????????????????????Color oldColor = oldBmp.GetPixel(i, j); ????????????????????Color newColor = newBmp.GetPixel(i, j); ????????????????????if?(Math.Abs(oldColor.R - newColor.R) > 60 || Math.Abs(oldColor.G - newColor.G) > 60 || Math.Abs(oldColor.B - newColor.B) > 60) ????????????????????{ ????????????????????????return?i; ????????????????????} ????????????????} ????????????} ????????????return?0; ????????} ????????/// <summary> ????????/// 獲取實際圖層缺口實際距離 ????????/// </summary> ????????/// <returns></returns> ????????private?int?GetDistance() ????????{ ????????????using?(Bitmap oldBitmap = (Bitmap)Image.FromFile(_originalMapPath)) ????????????{ ????????????????using?(Bitmap newBitmap = (Bitmap)Image.FromFile(_newMapPath)) ????????????????{ ????????????????????var?distance = GetArgb(oldBitmap, newBitmap); ????????????????????distance = distance - _leftOffset; ????????????????????return?distance; ????????????????} ????????????} ????????} ????????/// <summary> ????????/// 截圖 ????????/// </summary> ????????/// <param name="remoteWebDriver"></param> ????????private?void?ScreenMap(RemoteWebDriver remoteWebDriver) ????????{ ????????????//顯示原始圖 ????????????ShowOriginalMap(remoteWebDriver); ????????????//全屏截圖 ????????????FullScreen(remoteWebDriver); ????????????//獲取原始圖層 ????????????var?originalElement = GetOriginalElement(remoteWebDriver); ????????????//保存原始圖 ????????????CutBitmap(_fullScreenPath, _originalMapPath, originalElement); ????????????//顯示新圖層 ????????????ShowNewMap(remoteWebDriver); ????????????//全屏截圖 ????????????FullScreen(remoteWebDriver); ????????????//獲取新圖層 ????????????var?newElement = GetNewMapElement(remoteWebDriver); ????????????//保存新圖 ????????????CutBitmap(_fullScreenPath, _newMapPath, newElement); ????????????//顯示缺口圖 ????????????ShowSliceMap(remoteWebDriver); ????????} ????????/// <summary> ????????/// 截圖 ????????/// </summary> ????????/// <param name="sourcePath"></param> ????????/// <param name="targetPath"></param> ????????/// <param name="webElement"></param> ????????private?void?CutBitmap(string?sourcePath,?string?targetPath, IWebElement webElement) ????????{ ????????????//獲取原始圖 ????????????using?(var?bitmap = (Bitmap)Image.FromFile(sourcePath)) ????????????{ ????????????????var?newBitmap = bitmap.Clone(new?Rectangle(webElement.Location, webElement.Size), System.Drawing.Imaging.PixelFormat.DontCare); ????????????????newBitmap.Save(targetPath); ????????????????newBitmap.Dispose(); ????????????????bitmap.Dispose(); ????????????} ????????} ????????/// <summary> ????????/// 全屏截圖 ????????/// </summary> ????????/// <param name="remoteWebDriver"></param> ????????private?void?FullScreen(RemoteWebDriver remoteWebDriver) ????????{ ????????????remoteWebDriver.GetScreenshot().SaveAsFile(_fullScreenPath); ????????} ????????/// <summary> ????????/// 獲取原始圖層元素 ????????/// </summary> ????????/// <param name="remoteWebDriver"></param> ????????/// <returns></returns> ????????protected?virtual?IWebElement GetOriginalElement(RemoteWebDriver remoteWebDriver) ????????{ ????????????return?remoteWebDriver.FindElementExt(By.ClassName(_originalMap), 10); ????????} ????????/// <summary> ????????/// 獲取原始圖加缺口背景圖元素 ????????/// </summary> ????????/// <param name="remoteWebDriver"></param> ????????/// <returns></returns> ????????protected?virtual?IWebElement GetNewMapElement(RemoteWebDriver remoteWebDriver) ????????{ ????????????return?remoteWebDriver.FindElementExt(By.ClassName(_newMap), 10); ????????} ????????/// <summary> ????????/// 獲取缺口圖層元素 ????????/// </summary> ????????/// <param name="remoteWebDriver"></param> ????????/// <returns></returns> ????????protected?virtual?IWebElement GetSliceMapElement(RemoteWebDriver remoteWebDriver) ????????{ ????????????return?remoteWebDriver.FindElementExt(By.ClassName(_sliceMap), 10); ????????} ????????/// <summary> ????????/// 獲取拖動按鈕元素 ????????/// </summary> ????????/// <param name="remoteWebDriver"></param> ????????/// <returns></returns> ????????protected?virtual?IWebElement GetSlidButtonElement(RemoteWebDriver remoteWebDriver) ????????{ ????????????return?remoteWebDriver.FindElementExt(By.ClassName(_slidButton), 10); ????????} ????????/// <summary> ????????/// 顯示原始圖層 ????????/// </summary> ????????/// <param name="remoteWebDriver"></param> ????????protected?virtual?bool?ShowOriginalMap(RemoteWebDriver remoteWebDriver) ????????{ ????????????remoteWebDriver.ExecuteScript("$('."?+ _newMap +?"').hide();$('."?+ _originalMap +?"').show();$('."?+ _sliceMap +?"').hide();"); ????????????Console.WriteLine("顯示原始圖"); ????????????Thread.Sleep(100); ????????????//#TODO 判斷JS執行后是否正確 ????????????return?true; ????????} ????????/// <summary> ????????/// 顯示原始圖加缺口背景之后的圖層 ????????/// </summary> ????????/// <param name="remoteWebDriver"></param> ????????/// <returns></returns> ????????protected?virtual?bool?ShowNewMap(RemoteWebDriver remoteWebDriver) ????????{ ????????????remoteWebDriver.ExecuteScript("$('."?+ _newMap +?"').show();$('."?+ _originalMap +?"').hide();$('."?+ _sliceMap +?"').hide();"); ????????????Console.WriteLine("顯示原始圖加缺口背景之后的圖層"); ????????????Thread.Sleep(100); ????????????//#TODO 判斷JS執行后是否正確 ????????????return?true; ????????} ????????/// <summary> ????????/// 顯示缺口圖 ????????/// </summary> ????????/// <param name="remoteWebDriver"></param> ????????/// <returns></returns> ????????protected?virtual?bool?ShowSliceMap(RemoteWebDriver remoteWebDriver) ????????{ ????????????remoteWebDriver.ExecuteScript("$('."?+ _sliceMap +?"').show();"); ????????????Console.WriteLine("顯示原始圖加缺口背景之后的圖層"); ????????????Thread.Sleep(100); ????????????//#TODO 判斷JS執行后是否正確 ????????????return?true; ????????} ????????#endregion ????} } public?interface?ISlideVerificationCode ???{ ???????bool?Pass(RemoteWebDriver remoteWebDriver); ???} using?OpenQA.Selenium.Chrome; using?Sniffer.VerificationCode.VerificationCodes; using?System; using?System.Collections.Generic; using?System.Drawing; using?System.Linq; using?System.Text; using?System.Threading; using?System.Threading.Tasks; namespace?Sniffer.VerificationCode.Tests { ????class?Program ????{ ????????static?void?Main(string[] args) ????????{ ????????????ChromeDriver driver =?new?ChromeDriver(); ????????????driver.Navigate().GoToUrl("https://www.tianyancha.com/"); ????????????//driver.Manage().Window.Maximize();//窗口最大化,便于腳本執行 ????????????driver.Manage().Window.Size =?new?Size(800, 800); ????????????//Console.WriteLine("ChromeDriver 設置超時等待(隱式等待)時間設置10秒"); ????????????//設置超時等待(隱式等待)時間設置10秒 ????????????//driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10); ????????????//點擊《登錄/注冊》按鈕 ????????????driver.ExecuteScript("header.loginLink(event)"); ????????????Console.WriteLine("點擊《登錄/注冊》按鈕"); ????????????Thread.Sleep(500); ????????????//點擊 《密碼登錄》 ????????????driver.ExecuteScript("loginObj.changeCurrent(1);"); ????????????Console.WriteLine("點擊 《密碼登錄》按鈕"); ????????????Thread.Sleep(500); ????????????//輸入賬號密碼 ????????????driver.ExecuteScript("$('.contactphone').val('18620800677')"); ????????????driver.ExecuteScript("$('.contactword').val('******')"); ????????????Console.WriteLine("輸入賬號密碼"); ????????????Thread.Sleep(500); ????????????//點擊登錄按鈕 ????????????driver.ExecuteScript("loginObj.loginByPhone(event);"); ????????????Console.WriteLine("點擊《登錄》按鈕"); ????????????Thread.Sleep(1000); ????????????GeetestSlideVerificationCode slideVerificationCode =?new?GeetestSlideVerificationCode(); ????????????var?flag = slideVerificationCode.Pass(driver); ????????????Console.WriteLine("過驗證 "?+ flag); ????????} ????} } |
?以上內容完全照搬原博主
?下面是缺失的部分
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | internal?class?MoveEntity { ????public?int?X; ????public?int?Y; ????public?int?sleep; ????public?MoveEntity(int?offset,?int?v,?int?sleep) ????{ ????????this.X = offset; ????????this.Y = v; ????????this.sleep = sleep; ????} } |
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | public?static?class?WebElementExtensions ???{ ???????public?static?IWebElement FindElementExt(this?IWebDriver driver, By?by,?int?timeoutInSeconds) ???????{ ???????????????var?wait =?new?DefaultWait<IWebDriver>(driver); ???????????????wait.IgnoreExceptionTypes(typeof(StaleElementReferenceException),?typeof(NoSuchElementException)); ???????????????wait.Timeout = TimeSpan.FromSeconds(timeoutInSeconds); ???????????????return?wait.Until(d => driver.FindElement(by)); ???????} ???} |
到此程序可以完美運行了。。
總結
以上是生活随笔為你收集整理的c# Selenium 如何模拟滑动geetest 验证码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 国家电网网络运营属于什么职业类型?
- 下一篇: pest分析对福特汽车公司战略发展有何影