破解极验滑动验证码

一 介绍

    一些网站会在正常的账号密码认证之外加一些验证码,以此来明确地区分人/机行为,从一定程度上达到反爬的效果,对于简单的校验码Tesserocr就可以搞定,如下

    但一些网站加入了滑动验证码,最典型的要属于极验滑动认证了,极验官网:http://www.geetest.com/,下图是极验的登录界面

破解极验滑动验证码_第1张图片

    现在极验验证码已经更新到了 3.0 版本,截至 2017 年 7 月全球已有十六万家企业正在使用极验,每天服务响应超过四亿次,广泛应用于直播视频、金融服务、电子商务、游戏娱乐、政府企业等各大类型网站

对于这类验证,如果我们直接模拟表单请求,繁琐的认证参数与认证流程会让你蛋碎一地,我们可以用selenium驱动浏览器来解决这个问题,大致分为以下几个步骤

[python]  view plain  copy
  1. #步骤一:点击按钮,弹出没有缺口的图片  
  2.   
  3. #步骤二:获取步骤一的图片  
  4.   
  5. #步骤三:点击滑动按钮,弹出带缺口的图片  
  6.   
  7. #步骤四:获取带缺口的图片  
  8.   
  9. #步骤五:对比两张图片的所有RBG像素点,得到不一样像素点的x值,即要移动的距离  
  10.   
  11. #步骤六:模拟人的行为习惯(先匀加速拖动后匀减速拖动),把需要拖动的总距离分成一段一段小的轨迹  
  12.   
  13. #步骤七:按照轨迹拖动,完全验证  
  14.   
  15. #步骤八:完成登录  

二 实现

[python]  view plain  copy
  1. #安装:selenium+chrome/phantomjs  
  2.   
  3. #安装:Pillow  
  4. Pillow:基于PIL,处理python 3.x的图形图像库.因为PIL只能处理到python 2.x,而这个模块能处理Python3.x,目前用它做图形的很多.  
  5. http://www.cnblogs.com/apexchu/p/4231041.html  
  6.   
  7. C:\Users\Administrator>pip3 install pillow  
  8. C:\Users\Administrator>python3  
  9. Python 3.6.1 (v3.6.1:69c0db5, Mar 21 201718:41:36) [MSC v.1900 64 bit (AMD64)] on win32  
  10. Type "help""copyright""credits" or "license" for more information.  
  11. >>> from PIL import Image  
  12. >>>  

[python]  view plain  copy
  1. from selenium import webdriver  
  2. from selenium.webdriver import ActionChains  
  3. from selenium.webdriver.common.by import By  
  4. from selenium.webdriver.common.keys import Keys  
  5. from selenium.webdriver.support import expected_conditions as EC  
  6. from selenium.webdriver.support.wait import WebDriverWait  
  7. from PIL import Image  
  8. import time  
  9.   
  10. def get_snap():  
  11.     ''''' 
  12.     对整个网页截图,保存成图片,然后用PIL.Image拿到图片对象 
  13.     :return: 图片对象 
  14.     '''  
  15.     driver.save_screenshot('snap.png')  
  16.     page_snap_obj=Image.open('snap.png')  
  17.     return page_snap_obj  
  18.   
  19. def get_image():  
  20.     ''''' 
  21.     从网页的网站截图中,截取验证码图片 
  22.     :return: 验证码图片 
  23.     '''  
  24.     img=wait.until(EC.presence_of_element_located((By.CLASS_NAME,'geetest_canvas_img')))  
  25.     time.sleep(2#保证图片刷新出来  
  26.     localtion=img.location  
  27.     size=img.size  
  28.   
  29.     top=localtion['y']  
  30.     bottom=localtion['y']+size['height']  
  31.     left=localtion['x']  
  32.     right=localtion['x']+size['width']  
  33.   
  34.     page_snap_obj=get_snap()  
  35.     crop_imag_obj=page_snap_obj.crop((left,top,right,bottom))  
  36.     return crop_imag_obj  
  37.   
  38.   
  39. def get_distance(image1,image2):  
  40.     ''''' 
  41.     拿到滑动验证码需要移动的距离 
  42.     :param image1:没有缺口的图片对象 
  43.     :param image2:带缺口的图片对象 
  44.     :return:需要移动的距离 
  45.     '''  
  46.     threshold=60  
  47.     left=57  
  48.     for i in range(left,image1.size[0]):  
  49.         for j in range(image1.size[1]):  
  50.             rgb1=image1.load()[i,j]  
  51.             rgb2=image2.load()[i,j]  
  52.             res1=abs(rgb1[0]-rgb2[0])  
  53.             res2=abs(rgb1[1]-rgb2[1])  
  54.             res3=abs(rgb1[2]-rgb2[2])  
  55.             if not (res1 < threshold and res2 < threshold and res3 < threshold):  
  56.                 return i-7 #经过测试,误差为大概为7  
  57.     return i-7 #经过测试,误差为大概为7  
  58.   
  59.   
  60. def get_tracks(distance):  
  61.     ''''' 
  62.     拿到移动轨迹,模仿人的滑动行为,先匀加速后匀减速 
  63.     匀变速运动基本公式: 
  64.     ①v=v0+at 
  65.     ②s=v0t+½at² 
  66.     ③v²-v0²=2as 
  67.  
  68.     :param distance: 需要移动的距离 
  69.     :return: 存放每0.3秒移动的距离 
  70.     '''  
  71.     #初速度  
  72.     v=0  
  73.     #单位时间为0.2s来统计轨迹,轨迹即0.2内的位移  
  74.     t=0.3  
  75.     #位移/轨迹列表,列表内的一个元素代表0.2s的位移  
  76.     tracks=[]  
  77.     #当前的位移  
  78.     current=0  
  79.     #到达mid值开始减速  
  80.     mid=distance*4/5  
  81.   
  82.     while current < distance:  
  83.         if current < mid:  
  84.             # 加速度越小,单位时间的位移越小,模拟的轨迹就越多越详细  
  85.             a= 2  
  86.         else:  
  87.             a=-3  
  88.   
  89.         #初速度  
  90.         v0=v  
  91.         #0.2秒时间内的位移  
  92.         s=v0*t+0.5*a*(t**2)  
  93.         #当前的位置  
  94.         current+=s  
  95.         #添加到轨迹列表  
  96.         tracks.append(round(s))  
  97.   
  98.         #速度已经达到v,该速度作为下次的初速度  
  99.         v=v0+a*t  
  100.     return tracks  
  101.   
  102.   
  103. try:  
  104.     driver=webdriver.Chrome()  
  105.     driver.get('https://account.geetest.com/login')  
  106.     wait=WebDriverWait(driver,10)  
  107.   
  108.     #步骤一:先点击按钮,弹出没有缺口的图片  
  109.     button=wait.until(EC.presence_of_element_located((By.CLASS_NAME,'geetest_radar_tip')))  
  110.     button.click()  
  111.   
  112.     #步骤二:拿到没有缺口的图片  
  113.     image1=get_image()  
  114.   
  115.     #步骤三:点击拖动按钮,弹出有缺口的图片  
  116.     button=wait.until(EC.presence_of_element_located((By.CLASS_NAME,'geetest_slider_button')))  
  117.     button.click()  
  118.   
  119.     #步骤四:拿到有缺口的图片  
  120.     image2=get_image()  
  121.   
  122.     # print(image1,image1.size)  
  123.     # print(image2,image2.size)  
  124.   
  125.     #步骤五:对比两张图片的所有RBG像素点,得到不一样像素点的x值,即要移动的距离  
  126.     distance=get_distance(image1,image2)  
  127.   
  128.     #步骤六:模拟人的行为习惯(先匀加速拖动后匀减速拖动),把需要拖动的总距离分成一段一段小的轨迹  
  129.     tracks=get_tracks(distance)  
  130.     print(tracks)  
  131.     print(image1.size)  
  132.     print(distance,sum(tracks))  
  133.   
  134.   
  135.     #步骤七:按照轨迹拖动,完全验证  
  136.     button=wait.until(EC.presence_of_element_located((By.CLASS_NAME,'geetest_slider_button')))  
  137.     ActionChains(driver).click_and_hold(button).perform()  
  138.     for track in tracks:  
  139.         ActionChains(driver).move_by_offset(xoffset=track,yoffset=0).perform()  
  140.     else:  
  141.         ActionChains(driver).move_by_offset(xoffset=3,yoffset=0).perform() #先移过一点  
  142.         ActionChains(driver).move_by_offset(xoffset=-3,yoffset=0).perform() #再退回来,是不是更像人了  
  143.   
  144.     time.sleep(0.5#0.5秒后释放鼠标  
  145.     ActionChains(driver).release().perform()  
  146.   
  147.   
  148.     #步骤八:完成登录  
  149.     input_email=driver.find_element_by_id('email')  
  150.     input_password=driver.find_element_by_id('password')  
  151.     button=wait.until(EC.element_to_be_clickable((By.CLASS_NAME,'login-btn')))  
  152.   
  153.     input_email.send_keys('[email protected]')  
  154.     input_password.send_keys('linhaifeng123')  
  155.     # button.send_keys(Keys.ENTER)  
  156.     button.click()  
  157.   
  158.     import time  
  159.     time.sleep(200)  
  160. finally:  
  161.     driver.close()  

案例:

[html]  view plain  copy
  1. 破解博客园后台登录  
  2.   
  3. from selenium import webdriver  
  4. from selenium.webdriver import ActionChains  
  5. from selenium.webdriver.common.by import By  
  6. from selenium.webdriver.common.keys import Keys  
  7. from selenium.webdriver.support import expected_conditions as EC  
  8. from selenium.webdriver.support.wait import WebDriverWait  
  9. from PIL import Image  
  10. import time  
  11.   
  12. def get_snap():  
  13.     driver.save_screenshot('full_snap.png')  
  14.     page_snap_obj=Image.open('full_snap.png')  
  15.     return page_snap_obj  
  16.   
  17. def get_image():  
  18.     img=driver.find_element_by_class_name('geetest_canvas_img')  
  19.     time.sleep(2)  
  20.     location=img.location  
  21.     size=img.size  
  22.   
  23.     left=location['x']  
  24.     top=location['y']  
  25.     right=left+size['width']  
  26.     bottom=top+size['height']  
  27.   
  28.     page_snap_obj=get_snap()  
  29.     image_obj=page_snap_obj.crop((left,top,right,bottom))  
  30.     # image_obj.show()  
  31.     return image_obj  
  32.   
  33. def get_distance(image1,image2):  
  34.     start=57  
  35.     threhold=60  
  36.   
  37.     for i in range(start,image1.size[0]):  
  38.         for j in range(image1.size[1]):  
  39.             rgb1=image1.load()[i,j]  
  40.             rgb2=image2.load()[i,j]  
  41.             res1=abs(rgb1[0]-rgb2[0])  
  42.             res2=abs(rgb1[1]-rgb2[1])  
  43.             res3=abs(rgb1[2]-rgb2[2])  
  44.             # print(res1,res2,res3)  
  45.             if not (res1 < threhold and res2 < threhold and res3 < threhold):  
  46.                 return i-7  
  47.     return i-7  
  48.   
  49. def get_tracks(distance):  
  50.     distance+=20 #先滑过一点,最后再反着滑动回来  
  51.     v=0  
  52.     t=0.2  
  53.     forward_tracks=[]  
  54.   
  55.     current=0  
  56.     mid=distance*3/5  
  57.     while current < distance:  
  58.         if current < mid:  
  59.             a=2  
  60.         else:  
  61.             a=-3  
  62.   
  63.         s=v*t+0.5*a*(t**2)  
  64.         v=v+a*t  
  65.         current+=s  
  66.         forward_tracks.append(round(s))  
  67.   
  68.     #反着滑动到准确位置  
  69.     back_tracks=[-3,-3,-2,-2,-2,-2,-2,-1,-1,-1] #总共等于-20  
  70.   
  71.     return {'forward_tracks':forward_tracks,'back_tracks':back_tracks}  
  72.   
  73. try:  
  74.     # 1、输入账号密码回车  
  75.     driver = webdriver.Chrome()  
  76.     driver.implicitly_wait(3)  
  77.     driver.get('https://passport.cnblogs.com/user/signin')  
  78.   
  79.     username = driver.find_element_by_id('input1')  
  80.     pwd = driver.find_element_by_id('input2')  
  81.     signin = driver.find_element_by_id('signin')  
  82.   
  83.     username.send_keys('linhaifeng')  
  84.     pwd.send_keys('xxxxx')  
  85.     signin.click()  
  86.   
  87.     # 2、点击按钮,得到没有缺口的图片  
  88.     button = driver.find_element_by_class_name('geetest_radar_tip')  
  89.     button.click()  
  90.   
  91.     # 3、获取没有缺口的图片  
  92.     image1 = get_image()  
  93.   
  94.     # 4、点击滑动按钮,得到有缺口的图片  
  95.     button = driver.find_element_by_class_name('geetest_slider_button')  
  96.     button.click()  
  97.   
  98.     # 5、获取有缺口的图片  
  99.     image2 = get_image()  
  100.   
  101.     # 6、对比两种图片的像素点,找出位移  
  102.     distance = get_distance(image1, image2)  
  103.   
  104.     # 7、模拟人的行为习惯,根据总位移得到行为轨迹  
  105.     tracks = get_tracks(distance)  
  106.     print(tracks)  
  107.   
  108.     # 8、按照行动轨迹先正向滑动,后反滑动  
  109.     button = driver.find_element_by_class_name('geetest_slider_button')  
  110.     ActionChains(driver).click_and_hold(button).perform()  
  111.   
  112.     # 正常人类总是自信满满地开始正向滑动,自信地表现是疯狂加速  
  113.     for track in tracks['forward_tracks']:  
  114.         ActionChains(driver).move_by_offset(xoffset=trackyoffset=0).perform()  
  115.   
  116.     # 结果傻逼了,正常的人类停顿了一下,回过神来发现,卧槽,滑过了,然后开始反向滑动  
  117.     time.sleep(0.5)  
  118.     for back_track in tracks['back_tracks']:  
  119.         ActionChains(driver).move_by_offset(xoffset=back_trackyoffset=0).perform()  
  120.   
  121.     # 小范围震荡一下,进一步迷惑极验后台,这一步可以极大地提高成功率  
  122.     ActionChains(driver).move_by_offset(xoffset=-3, yoffset=0).perform()  
  123.     ActionChains(driver).move_by_offset(xoffset=3yoffset=0).perform()  
  124.   
  125.     # 成功后,骚包人类总喜欢默默地欣赏一下自己拼图的成果,然后恋恋不舍地松开那只脏手  
  126.     time.sleep(0.5)  
  127.     ActionChains(driver).release().perform()  
  128.   
  129.     time.sleep(10)  # 睡时间长一点,确定登录成功  
  130. finally:  
  131.     driver.close()  

[python]  view plain  copy
  1. 修订版  
  2.   
  3. from selenium import webdriver  
  4. from selenium.webdriver import ActionChains  
  5. from selenium.webdriver.common.by import By  
  6. from selenium.webdriver.common.keys import Keys  
  7. from selenium.webdriver.support import expected_conditions as EC  
  8. from selenium.webdriver.support.wait import WebDriverWait  
  9. from PIL import Image  
  10. import time  
  11.   
  12. def get_snap(driver):  
  13.     driver.save_screenshot('full_snap.png')  
  14.     page_snap_obj=Image.open('full_snap.png')  
  15.     return page_snap_obj  
  16.   
  17. def get_image(driver):  
  18.     img=driver.find_element_by_class_name('geetest_canvas_img')  
  19.     time.sleep(2)  
  20.     location=img.location  
  21.     size=img.size  
  22.   
  23.     left=location['x']  
  24.     top=location['y']  
  25.     right=left+size['width']  
  26.     bottom=top+size['height']  
  27.   
  28.     page_snap_obj=get_snap(driver)  
  29.     image_obj=page_snap_obj.crop((left,top,right,bottom))  
  30.     # image_obj.show()  
  31.     return image_obj  
  32.   
  33. def get_distance(image1,image2):  
  34.     start=57  
  35.     threhold=60  
  36.   
  37.     for i in range(start,image1.size[0]):  
  38.         for j in range(image1.size[1]):  
  39.             rgb1=image1.load()[i,j]  
  40.             rgb2=image2.load()[i,j]  
  41.             res1=abs(rgb1[0]-rgb2[0])  
  42.             res2=abs(rgb1[1]-rgb2[1])  
  43.             res3=abs(rgb1[2]-rgb2[2])  
  44.             # print(res1,res2,res3)  
  45.             if not (res1 < threhold and res2 < threhold and res3 < threhold):  
  46.                 return i-7  
  47.     return i-7  
  48.   
  49. def get_tracks(distance):  
  50.     distance+=20 #先滑过一点,最后再反着滑动回来  
  51.     v=0  
  52.     t=0.2  
  53.     forward_tracks=[]  
  54.   
  55.     current=0  
  56.     mid=distance*3/5  
  57.     while current < distance:  
  58.         if current < mid:  
  59.             a=2  
  60.         else:  
  61.             a=-3  
  62.   
  63.         s=v*t+0.5*a*(t**2)  
  64.         v=v+a*t  
  65.         current+=s  
  66.         forward_tracks.append(round(s))  
  67.   
  68.     #反着滑动到准确位置  
  69.     back_tracks=[-3,-3,-2,-2,-2,-2,-2,-1,-1,-1#总共等于-20  
  70.   
  71.     return {'forward_tracks':forward_tracks,'back_tracks':back_tracks}  
  72.   
  73. def crack(driver): #破解滑动认证  
  74.     # 1、点击按钮,得到没有缺口的图片  
  75.     button = driver.find_element_by_class_name('geetest_radar_tip')  
  76.     button.click()  
  77.   
  78.     # 2、获取没有缺口的图片  
  79.     image1 = get_image(driver)  
  80.   
  81.     # 3、点击滑动按钮,得到有缺口的图片  
  82.     button = driver.find_element_by_class_name('geetest_slider_button')  
  83.     button.click()  
  84.   
  85.     # 4、获取有缺口的图片  
  86.     image2 = get_image(driver)  
  87.   
  88.     # 5、对比两种图片的像素点,找出位移  
  89.     distance = get_distance(image1, image2)  
  90.   
  91.     # 6、模拟人的行为习惯,根据总位移得到行为轨迹  
  92.     tracks = get_tracks(distance)  
  93.     print(tracks)  
  94.   
  95.     # 7、按照行动轨迹先正向滑动,后反滑动  
  96.     button = driver.find_element_by_class_name('geetest_slider_button')  
  97.     ActionChains(driver).click_and_hold(button).perform()  
  98.   
  99.     # 正常人类总是自信满满地开始正向滑动,自信地表现是疯狂加速  
  100.     for track in tracks['forward_tracks']:  
  101.         ActionChains(driver).move_by_offset(xoffset=track, yoffset=0).perform()  
  102.   
  103.     # 结果傻逼了,正常的人类停顿了一下,回过神来发现,卧槽,滑过了,然后开始反向滑动  
  104.     time.sleep(0.5)  
  105.     for back_track in tracks['back_tracks']:  
  106.         ActionChains(driver).move_by_offset(xoffset=back_track, yoffset=0).perform()  
  107.   
  108.     # 小范围震荡一下,进一步迷惑极验后台,这一步可以极大地提高成功率  
  109.     ActionChains(driver).move_by_offset(xoffset=-3, yoffset=0).perform()  
  110.     ActionChains(driver).move_by_offset(xoffset=3, yoffset=0).perform()  
  111.   
  112.     # 成功后,骚包人类总喜欢默默地欣赏一下自己拼图的成果,然后恋恋不舍地松开那只脏手  
  113.     time.sleep(0.5)  
  114.     ActionChains(driver).release().perform()  
  115.   
  116. def login_cnblogs(username,password):  
  117.     driver = webdriver.Chrome()  
  118.     try:  
  119.         # 1、输入账号密码回车  
  120.         driver.implicitly_wait(3)  
  121.         driver.get('https://passport.cnblogs.com/user/signin')  
  122.   
  123.         input_username = driver.find_element_by_id('input1')  
  124.         input_pwd = driver.find_element_by_id('input2')  
  125.         signin = driver.find_element_by_id('signin')  
  126.   
  127.         input_username.send_keys(username)  
  128.         input_pwd.send_keys(password)  
  129.         signin.click()  
  130.   
  131.         # 2、破解滑动认证  
  132.         crack(driver)  
  133.   
  134.         time.sleep(10)  # 睡时间长一点,确定登录成功  
  135.     finally:  
  136.         driver.close()  
  137.   
  138. if __name__ == '__main__':  
  139.     login_cnblogs(username='linhaifeng',password='xxxx')  

三 说明

  面对简单的滑动验证码,极验其实是有更复杂版本的,如下所示

破解极验滑动验证码_第2张图片破解极验滑动验证码_第3张图片

机器识别难度高了,大部分屌丝码农搞不定了。然而人类也蒙蔽了,易用性降到极低。

使用了上述验证的网站常常会在用户一片怨声载道中,又将其恢复成易于破解的滑动验证。

验证过程,是个破解难度、用户体验之间的一个平衡点。体验越好的,破解也越容易。 
嘲讽验证码无效,破解简单,是很 LOW 的行为。

网站方、验证码平台方,知道你能破解,你牛 B。。。更难的验证码他们也有,只是这会严重降低体验,他们不用而已。

你可能感兴趣的:(爬虫,验证码)