笔者是一名初入 Python 爬虫的小白, 通过书籍 (静觅大神出的书籍) 的方式学了下突破验证的方式实现模拟登录有此类型的应用的知识. 该方法不涉及任何商业关系, 如果有违规行为麻烦联系下笔者
实现的步骤分为 3 步:
1. 使应用出现验证的完整图片和带有缺口的图片
2. 识别缺口的位置
3. 模拟拖动滑块至缺口处, 完成验证
引入相关的库
- from selenium import webdriver;
- from selenium.webdriver.support.wait import WebDriverWait;
- from selenium.webdriver.support import expected_conditions as EC;
- from selenium.webdriver.common.by import By;
- from selenium.webdriver import ActionChains;
- import time;
- from PIL import Image;
- from io import BytesIO;
我们定义一个类来实现相关的操作并且定义一些配置
- EMAIL='xxx' #账号
- PASSWORD='xxx' #密码(只是简单的处理)
- BORDER=6; #开始滑动的小块与左边缘的距离
- INIT_LEFT=60; #开始从 X 轴方向即 x=60 开始检测缺口的位置
- class GrackGeetest(object):
- def __init__(self):
- #这边我们开始定义一些相关的参数信息(我们用登录极验官网来做例子, 其它的方式类似)
- self.url='https://auth.geetest.com/login/';
- self.browser=webdriver.Chrome();
- self.wait=WebDriverWait(self.browser,20);
- self.email=EMAIL;
- self.password=PASSWORD;
- # 实现步骤 1 相关方法:
- def getGeetestButton(self):
- #获取点击可以使现验证图出现的按钮节点元素并返回
- button=self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME,'geetest_radar_tip')));
- return button;
- #获取验证图在网页中的位置并以元组的方式返回
- def getImagePosition(self):
- geetestImage=self.wait.until(EC.presence_of_element_located((By.CLASS_NAME,'geetest_canvas_img')));
- time.sleep(2);
- location=geetestImage.location;
- size=geetestImage.size;
- top,bottom,left,right=location['y'],location['y']+size['height'],location['x'],location['x']+size['width'];
- return (top,bottom,left,right);
- #截取当前页面
- def getChromePage(self):
- pageShot=self.browser.get_screenshot_as_png();
- pageShot=Image.open(BytesIO(pageShot));
- return pageShot;
- #从网页中截取该验证图片并返回
- def getGeetestImage(self,name='geetest.png'):
- top,bottom,left,right=self.getImagePosition();
- #截取当前页面的图片
- pageShot=self.getChromePage();
- #截取其中出现的验证图的位置
- captchaImage=pageShot.crop((left,top,right,bottom));
- captchaImage.save(name);# 保存到当前的文件夹中
- return captchaImage;
- # 实现步骤 2 相关方法: 识别缺口位置
- def getSlider(self):
- #获取可拖动的滑块对象
- slider=self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME,'geetest_slider_button')));
- return slider;
- #通过对比 2 张图的像素点的差距得出缺口位置
- def getGap(self,image1,image2):
- left=60;
- #size[0]->width,size[1]->height
- for i in range(left,image1.size[0]):
- for j in range(image1.size[1]):
- if not self.isPixelEqual(image1,image2,i,j):
- #因为小滑块和缺口是同一条水平线上的所以就只取 x 轴方向上的值
- left=i;
- return left;
- return left;
- def isPixelEqual(self,image1,image2,x,y):
- #判断 2 个像素是否相同
- pixel1=image1.load()[x,y]; #pixel1,pixel2 为 rgb 值, 是一个元组
- pixel2=image2.load()[x,y];
- #阀值当超出这个阀值的时候则证明这 2 个像素点不匹配, 为缺口的左上角的像素点
- threshold=60;
- if abs(pixel1[0]-pixel2[0])<threshold and abs(pixel1[1]-pixel2[1])<threshold and abs(pixel1[2]-pixel2[2])<threshold :
- return True;
- else:
- return False;
- #步骤三相关方法: 最关键的一步也是突破极验验证机器学习算法的一步
- #采用物理中物体的分阶段改变加速度的方式, 这里采用先加速后减速的方式
- #公式 x=v0*t+1/2*a*t*t v=v0+a*t
- def getTrack(self,distance):
- #distance 偏移量
- #移动轨迹
- tranck=[];
- #当前位移
- current=0;
- #开始减速的阀值
- mid=distance*4/5;
- #计算间隔
- t=0.2;
- #初速度
- v=0;
- while current<distance:
- if current<mid:
- a=2;
- else:
- #开始减速
- a=-3;
- #初速度
- v0=v;
- #当前速度
- v=v0+a*t;
- #位移
- move=v0*t+1/2*a*t*t;
- #当前位移
- current+=move;
- #加入轨迹
- track.append(round(move));
- return track;
- #按照运动轨迹移动滑块
- def moveToGap(self,slider,tracks):
- #拖动滑块到缺口处
- ActionChains(self.browser).click_and_hold(slider).perform();
- for x in tracks:
- ActionChains(self.browser).move_by_offset(xoffset=x,yoffset=0).perform();
- time.sleep(0.5);
- ActionChains(self.browser).release().perform();
- #最后模拟点击登录应用就行了
- def login(self):
- button=self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'#base> div.content-outter> div> div> div:nth-child(3)> div> form> div:nth-child(5)> div> button')));
- button.click();
- time.sleep(10);
- #接下来直接实现通过一个方法将这整个过程连接起来
- def sendUserAndPassword(self):
- self.browser.get(self.url);
- #通过类选择器, 我是直接在浏览器那边复制过来的, 所以比较长, 可以通过其它方式得到该元素(右键那个网页元素就有一些选择可以看看哈)
- email=self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#base> div.content-outter> div> div> div:nth-child(3)> div> form> div:nth-child(1)> div> div.ivu-input-wrapper.ivu-input-type.ivu-input-group.ivu-input-group-with-prepend> input')));
- password=self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'#base> div.content-outter> div> div> div:nth-child(3)> div> form> div:nth-child(2)> div> div.ivu-input-wrapper.ivu-input-type.ivu-input-group.ivu-input-group-with-prepend> input')));
- email.send_keys(self.email);
- password.send_keys(self.password);
- def doVerifyLogin(self):
- #步骤 1:
- #输入账号密码
- self.sendUserAndPassword();
- #点击验证按钮
- verifyButton=self.getGeetestButton();
- verifyButton.click();
- #开始获取 2 张验证图
- image1=self.getGeetestImage('geetest1.png');
- #点击小滑块得到有缺口的验证图
- slider=self.getSlider();
- slider.click();
- #获取带缺口的验证图
- image2=self.getGeetestImage('geetest2.png');
- #步骤 2:
- #获取缺口位置
- gap=self.getGap(image1,image2);
- #缺口的位置需要减去那个小滑块与左边那一小段距离
- gap-=BORDER;
- #步骤 3:
- #移动轨迹
- track=self.getTrack();
- #拖动滑块
- self.moveToGap(slider,track);
- #最后判断是否成功了, 不成功就重新操作这一过程
- try:
- success = self.wait.until(
- EC.text_to_be_present_in_element((By.CLASS_NAME, 'geetest_success_radar_tip_content'), '验证成功'))
- print(success)
- # 失败后重试
- if not success:
- self.doVerifyLogin()
- else:
- self.login()
- except:
- self.doVerifyLogin();
- if __name__ == '__main__':
- crack = GrackGeetest();
- crack.doVerifyLogin();
以上就是完整的代码了, 同时需要安装 ChromeDriver, 安装的具体过程找下搜索引擎问问
来源: https://juejin.im/post/5b29bb5de51d455e2c32e3de