参考资料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));
       }
   }

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

c# Selenium 如何模拟滑动geetest 验证码相关推荐

  1. selenium篇之滑动验证码

    一.介绍 现在出现了一种通过用户鼠标移动滑块来填补有缺口图片的验证码,我们叫做滑动验证码.它的原理很简单,首先生成一张图片,然后随机挖去一块,在页面展示被挖去部分的图片,再通过js获取用户滑动距离,以 ...

  2. Python使用selenium过天眼查滑块验证码反爬实现模拟登录

    Python使用selenium过天眼查滑块验证码反爬实现模拟登录 天眼查的滑块验证码样式 一顿操作到滑块验证码阶段 破解滑块验证码 结束 天眼查的滑块验证码样式 在输入账号和密码后会发现这个令人讨厌 ...

  3. selenium爬虫|破解滑动验证码以极验为例

    爬虫访问一些网站遇到滑动验证码解决方案 这里是用selenium做模拟,如果是requests可以封装这个登录方法来获取登录后的cookies也是可以用的. 1 思路 先讲思路,分析流程 我们输入账号 ...

  4. 基于Java+selenium+opencv模拟网页滑动验证

    目前很多网页都有滑动验证,目的就是防止不良爬虫扒他们网站的数据,我这次本着学习的目的使用Java和selenium学习解决滑动验证的问题,前前后后花了一周时间(抄代码),终于成功了某音的滑动验证! 效 ...

  5. python+selenium+chrome 做滑动验证码 会被浏览器检测到使用的自动软件导致滑动验证失败

    python+selenium+chrome 做滑动验证码 会被浏览器检测到使用的自动软件导致滑动验证失败 解决方法:代码中添加: import time from selenium import w ...

  6. 爬虫进阶教程:极验(GEETEST)验证码破解教程

    原文链接及原作者:爬虫进阶教程:极验(GEETEST)验证码破解教程 | Jack Cui 一.前言 爬虫最大的敌人之一是什么?没错,验证码![Geetest]作为提供验证码服务的行家,市场占有率还是 ...

  7. 破解极验(geetest)验证码

    最近在搞爬虫的时候在好几个网站都碰到了一种叫做geetest的滑动条验证码,一直没有太好的办法只能在触发这个验证码后发个报警去手动处理一下.http://www.geetest.com/exp_emb ...

  8. 爬虫模拟登陆手机验证码_网络爬虫干货总结,这次比较全面!

    我从五个方面介绍了利用 Python 进行网络爬虫开发的相关知识点和技巧: 抓取 解析 存储 反爬 加速 目录 一.爬取 爬取的目标可以大致分为两类:网页.APP 对于网页,可以分为两种类别,即 服务 ...

  9. 爬虫之极验验证码破解-滑动拼图验证码破解

    滑动拼图验证码破解 前言 步骤分析 第一步,获取原图 第二步 拼接图片 第三步 计算豁口所在位置 第四步 计算拖动距离 模拟拖动 其他 前言 滑动验证码已经流行很多年了,我们在这里尝试一下如何实现滑动 ...

最新文章

  1. Node.js v7 Beta版引入citgm
  2. 双目立体匹配——归一化互相关(NCC)
  3. jQuery clearQueue
  4. Linux 系统应用编程——网络编程(TCP/IP 数据包格式解析)
  5. 【华为云技术分享】数据湖数据库,别再傻傻分不清了
  6. 内核中的UDP socket流程(1)
  7. Windows系统 notepad命令详解,Windows系统打开记事本
  8. 直方图均衡化作用及实现
  9. 自定义ImageView加载圆形图片
  10. python itchat实现微信自动回复
  11. 微电子电路——期中总结
  12. 洗地机性价比高的是哪款?性价比高适合入手的洗地机介绍
  13. 让企业订单交期满足率提升3.5倍,新一代APS(高级生产计划与排程系统)是什么样的?
  14. 程序员养花几个实用小技巧
  15. 微信小程序项目-电子木鱼
  16. 因签署Android独家协议排除竞争对手 谷歌在印度被罚款1.62亿美元
  17. Python数据分析||基于逻辑回归的糖尿病视网膜病变的影响因素分析
  18. VS2019运行OpenGL时出现的常见错误及解决办法
  19. 钉钉正式接入阿里“通义千问”大模型;金山办公发布“WPS AI”;北大团队推出ChatExcel丨每日大事件...
  20. (转)任务管理与职责管理

热门文章

  1. OpenGL indexedCube索引多维数据集的实例
  2. C语言实现djikstra算法(附完整源码)
  3. QT的QLibrary类的使用
  4. C++继承中的普通函数,纯虚函数、虚函数
  5. C++11右值引用和移动构造函数
  6. C语言获取mysql表数据结构_【数据结构】实现顺序表(c语言)
  7. 13.3.虚拟化工具--jinfo详解
  8. java并发包消息队列
  9. 关于Eclipse创建Android项目时,会多出一个appcompat_v7的问题
  10. 一步步创建Qt Widget项目+TextFinder案例(摘自笔者2015年将出的《QT5权威指南》,本文为试读篇)