c# Selenium 破解極驗驗證碼


背景:由於爬 https://www.tianyancha.com/ ,需要登錄登錄認證,所以來做破解 極驗驗證

參考資料:https://www.cnblogs.com/ZQWelcomeIndex/p/8367202.html 破解騰訊空間滑塊 (注:目前圖片地址有變化,該地址代碼下載不能直接使用。可以參考 https://www.cnblogs.com/hujunmin/p/11486124.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個小圖,然后排序再組合成一張有序的原始圖。

轉換思路二:

直接去瀏覽器上通過顯示隱藏不同的圖片,然后截圖對比(目前我代碼這個思路處理的)。

 

上代碼:

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

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM