岁月蹉跎,转眼又浪费了一个月,准备重新的生活起来,知识点总结起来,之前的算法暂时停歇到年底,因为目前爬虫还没完全参透,接下来会在爬虫、spss、机器学习、建模、深度学习等方面进行系统全新的学习,总结的代码和一些感悟会写进简书里,大佬们多多指教。
今天学习了爬虫中验证码的识别--滑动验证码该类验证码的关键就在于原图(没有缺口的图)与有缺口的图的像素比较/从而得到缺口的位置/然后拖动滑块进行识别
知识点则主要涉及selenium 的用法
代码注释内容是对崔庆才--催大佬的书籍中源码的自我消化
滑动验证码的类型如下

下面为代码和注释部分
EMAIL = 'xxxxxxxx'
PASSWORD = 'xxxxxxx'
INIT_LEFT = 60
BORDER=6
class crackgeetest(object):
def __init__(self):
self.url='https://account.geetest.com/login'
self.browser=webdriver.Chrome()
self.wait=WebDriverWait(self.browser,20)
self.email=EMAIL
self.password=PASSWORD
def __del__(self):
self.browser.close()#关掉浏览器
def get_slider(self):
"""
获取滑动按钮
"""
slider=self.wait.until(By.CLASS_NAME,'geetest_radar_tip')#根据class 名称来定位
return slider
def get_geetest_button(self):
"""
获取初始验证按钮(这样才会显示出验证图片)
:return:
"""
button = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_radar_tip')))
return button
def get_position(self):
"""
获取验证码的位置,并且返回验证码位置的元组
"""
img=self.wait.until(EC.presence_of_element_located((By.CLASS_NAME,'geetest_canvas_img')))
time.sleep(2)#等待两秒
location=img.location
size=img.size
top,bottom,left,right=location['y'],location['y']+size['height'],location['x'],location['x']+size['width']#获取完整图片的位置
return (top,bottom,left,right)
def get_screenshot(self):#目的是获取完整图片的截图
"""
获取验证码的截图,并且返回截图
"""
screenshot=self.browser.get_screenshot_as_png()
screenshot=Image.open(BytesIO(screenshot))
return screenshot
def get_slider(self):
"""
获取带缺口的图片,要使得该图片出现
需要点击下方的滑块即可,动作一旦触发,图片中的缺口就会出现
"""
slider = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_slider_button')))
return slider
def get_geetest_image(self,name='captcha.png'):
"""
获取带有缺口的验证码的图片截图
"""
top,bottom,left,right=self.get_position()
print ('验证码位置',top,bottom,left,right)
screenshot=self.get_screenshot()#调用获取截图
captcha=screenshot.crop((left,top,right,bottom))#注意为左上角和右下角
captcha.save(name)
return captcha
def open(self):
"""
打开网页并且输入用户名和密码
"""
self.browser.get(self.url)
email=self.wait.until(EC.presence_of_element_located((By.ID, 'email')))#获取键入框
password=self.wait.until(EC.presence_of_element_located((By.ID, 'password')))#获取键入框
email.send_keys(self.email)
password.send_keys(self.password)
"""
上边已经获取了两张图片的截图,这里来遍历每一个坐标点的像素RGB数据,如果两者的像素差在
一定的范围内则说明像素相同,反之不同
"""
def is_pixel_equal(self,image1,image2,x,y):#比较像素
"""
image1:不带缺口的图片
image2:带有缺口的图片
"""
pixel1=image1.load()[x,y]#本质上得到RGB数据(3个数)组成的数组
pixel2=image2.load()[x,y]
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
def get_gap(self,image1,image2):#获取缺口位置的方法
left=60#缺口一般在滑块的右侧,如果要寻找缺口直接从滑块的右侧寻找即可,故遍历的起始坐标为60
for i in range(left,image1.size[0]):
for j in range(image1.size[1]):
if not self.is_pixel_equal(image1,image2,i,j):#说明像素不同
left=i
return left
return left
def get_track(self,distance):#有了以上准备下面就开始拖动滑块
"""
如果为匀速拖动,那么由于极验运用的是机器模型,必然会识别出是程序所为,验证不会通过
所以崔大佬书中讲要先匀加速后匀减速
x=v0*t+1/2*a*t*t
v=v0+a*t
"""
track=[]#运动轨迹
current=0#当前位移
mid=distance*4/5#减速阈值
t=0.2#时间间隔
v=0#初速度
while current<distance:
if current<mid:
a=2#加速度为正
else:
a=-3
vo=v
v=vo+a*t
move=vo*t+1/2*a*t*t
current+=move
track.append(round(move))#round 函数返回四舍五入
return track #存入的为每个时间间隔内移动的距离
def move_to_gap(self,slider,tracks):
"""
第一个参数为get_slider()函数定位的滑块
第二个参数为上面的函数得到的单位时间间隔滑块所在位置的集合
"""
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):#当上面模拟滑动验证成功时候,需要点击登陆按钮
submit = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'login-btn')))
submit.click()
time.sleep(10)
print('登录成功')
def crack(self):
self.open()#首先登陆到该极验界面
button=self.get_geetest_button()
button.click()#这样就会首先显示出完整的图片
image1=self.get_geetest_image('captcha1.png')
slider=self.get_slider()
slider.click()#点按就会得到带有缺口的图片
image2=self.get_geetest_image('captcha2.png')#得到带有缺口的图片位置
gap=self.get_gap(image1,image2)
print ('缺口位置',gap)
gap-=BORDER#减去缺口位移
track=self.get_track(gap)
print ('滑动轨迹',track)
self.move_to_gap(slider,track)
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.crack()
else:
self.login()
if __name__=='__main__':
crack=crackgeetest()
crack.crack()
后续还会自己总结书中代码进行学习
网友评论