• 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 byint 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));
                
     
           }
       }

      

    到此程序可以完美运行了。。

  • 相关阅读:
    Mysql初识
    滚动监听实例
    利用div显示隐藏实现的分页效果
    animation动画汇总(一阶段项目)
    css3动画效果
    animate动画基础
    切换式轮播方式
    Ajax基础
    Jquery日历编写小练习
    [转]好文章:Android的AlertDialog详解
  • 原文地址:https://www.cnblogs.com/soundcode/p/15882706.html
Copyright © 2020-2023  润新知