一、准备工作

  本次使用Selenium,浏览器为Chrome,并配置好ChromDriver

二、分析

  1.模拟点击验证按钮:可以直接使用Selenium完成。

    2.识别滑块的缺口位置:先观察图片中缺口的位置以及周围边缘,利用原图与其对比检测来识别缺口位置。

    同时获取原图与缺口图片,设定一个对比阀值,然后对两张图片进行遍历,找出相同位置像素RGB差距超过此阀值的像素点。即缺口的位置

  3.模拟拖动滑块:极验增加了机械轨迹识别与速度检测,只有完全模拟人的操作才能通过验证。运动轨迹一般先加速,然后在减速。

三,代码实现

  1.初始化

    使用魅族登陆注册页面进行测试,https://i.flyme.cn/register?。在这里先初始化一些配置

             

  

import time
from io import BytesIO
from PIL import Image
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC phone = '' class GeetestSpider(): def __init__(self):
self.url = "https://i.flyme.cn/register?"
self.browser =webdriver.Chrome(executable_path=r'D:\Google\Chrome\Application\chromedriver')
self.wait = WebDriverWait(self.browser, 20)
self.phone = phone

  2.模拟点击  

    def get_button(self):
"""
获取初始验证按钮,模拟点击
:return:按钮对象
"""
button = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_radar_tip')))
return button

  3.识别缺口

  获取前后两张比对图片,不一致的地方即为缺口位置。利用Selenium获取图片元素,得到位置和宽高,然后获取整个网页的截图,再将图片剪裁出来即可。  

    def get_screen_image(self):
"""
获取网页截图
:return: 截图对象
"""
screen_img = self.browser.get_screenshot_as_png()
screen_img = Image.open(BytesIO(screen_img))
return screen_img def get_position(self):
"""
获取验证码的位置
:return: 验证码位置元祖
"""
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_geetest_img(self, name='captcha.png'):
"""
获取验证码图片
:return: 图片对象,Image对象
"""
top, bottom, left, right = self.get_position()
print('验证码位置:', top, bottom, left, right)
screen_img = self.get_screen_image()
# 剪裁图片
captcha = screen_img.crop((left, top, right, bottom))
     captcha.save(name)
return captcha def get_slider(self):
"""
获取滑块
:return:滑块对象
"""
slider = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_slider_button')))
return slider

  获取滑块slider之后,调用click()方法即可触发点击,缺口图片就好呈现出来。再调用get_geetest_img()方法将获取第二章图片。

  将获取到的两张图片分别赋值给img1,img2。进行图片对比,遍历图片的每个坐标点,获取两张图片对应像素点的RGB数据。如果二者的RGB数据差在一定的范围内,则代表两个像素相同,继续进行下一个点的对比。如果差距超过一定范围,则代表像素点不相同,即缺口的位置。

    def equal_rgb(self, img1, img2, x, y):
"""
判断两个像素点是否相同
:param img1: 图片1
:param img2: 图片2
:param x: 位置x
:param y: 位置y
:return: 是否相同
"""
# 获取两张图片的像素点
pixel1 = img1.load()[x, y]
pixel2 = img2.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, img1, img2):
"""
获取偏移量
:param img1:不带缺口的图片
:param img2: 带缺口的图片
:return: 偏移量
"""
left = 60
for i in range(left, img1.size[0]):
for j in range(img1.size[1]):
if not self.equal_rgb(img1,img2,i,j):
left = i
return left
return left

  4.模拟拖动滑块

  为了模拟人的操作,滑块的拖动速度不应该是匀速,也不应该是保持在某一速度上下抖动。滑块的速度变化应该是一开始在加速,接近缺口就会减速,想象一下我们手动拖动滑块的情况。

  滑块的加速度用a表示,当前速度用v表示,初始速度用v0表示,移动距离用x表示,运动时间t表示,满足以下关系:

  x = v0 * t + a * t * t / 2

  v = v0 + a * t  

    def get_track(self, d):
"""
根据偏移量获取运动轨迹
:param d: 偏移量
:return: 运动轨迹,每次移动距离
"""
# 运动轨迹
track = []
# 当前位移
current = 0
# 开始减速的偏移量,设位移达到偏移量的2/3时开始减速
deviation = d * 2 / 3
#  间隔时间
t = 0.2
# 初始速度
v = 0 while current < d:
if current < deviation:
# 加速阶段
a = 2
else:
# 减速阶段
a = -2 # 初始速度
v0 = v
# 当前速度
v = v0 + a * t
# 位移
move = v0 * t + a * t * t / 2
# 当前位移
current += move
# 添加到轨迹,保留整数
track.append(round(move))
return track def move_slider(self, slider, track):
"""
按照轨迹移动滑块至缺口
:param slider:滑块
:param track: 轨迹
:return:
"""
# 鼠标按住滑块
ActionChains(self.browser).click_and_hold(slider).perform()
for i in track:
# 遍历轨迹元素,每次移动对应位移
ActionChains(self.browser).move_by_offset(xoffset=i, yoffset=0).perform()
time.sleep(0.5)
# 移动完成后,松开鼠标
ActionChains(self.browser).release().perform()

  验证成功:

    

 

5.完整代码

# _*_ coding=utf-8 _*_

import time
from io import BytesIO
from PIL import Image
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC # 初始化
class GeetestSpider():
def __init__(self):
self.url = "https://i.flyme.cn/register"
self.browser = webdriver.Chrome(executable_path=r'D:\Google\Chrome\Application\chromedriver')
self.wait = WebDriverWait(self.browser, 20) def get_button(self):
"""
获取初始验证按钮,模拟点击
:return:按钮对象
"""
button = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_radar_tip_content')))
return button def get_screen_image(self):
"""
获取网页截图
:return: 截图对象
"""
screen_img = self.browser.get_screenshot_as_png()
screen_img = Image.open(BytesIO(screen_img))
return screen_img def get_position(self):
"""
获取验证码的位置
:return: 验证码位置元祖
"""
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_geetest_img(self, name='captcha.png'):
"""
获取验证码图片
:return: 图片对象,Image对象
"""
top, bottom, left, right = self.get_position()
print('验证码位置:', top, bottom, left, right)
screen_img = self.get_screen_image()
# 剪裁图片
captcha = screen_img.crop((left, top, right, bottom))
captcha.save(name)
return captcha def get_slider(self):
"""
获取滑块
:return:滑块对象
"""
slider = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_slider_button')))
return slider def equal_rgb(self, img1, img2, x, y):
"""
判断两个像素点是否相同
:param img1: 图片1
:param img2: 图片2
:param x: 位置x
:param y: 位置y
:return: 是否相同
"""
# 获取两张图片的像素点
pixel1 = img1.load()[x, y]
pixel2 = img2.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, img1, img2):
"""
获取偏移量
:param img1:不带缺口的图片
:param img2: 带缺口的图片
:return: 偏移量
"""
# 直接从滑块的右侧开始遍历
left = 60
for i in range(left, img1.size[0]):
for j in range(img1.size[1]):
if not self.equal_rgb(img1, img2, i, j):
left = i
return left
return left def get_track(self, d):
"""
根据偏移量获取运动轨迹
:param d: 偏移量
:return: 运动轨迹,每次移动距离
"""
# 运动轨迹
track = []
# 当前位移
current = 0
# 开始减速的偏移量,设位移达到偏移量的2/3时开始减速
deviation = d * 2 / 3
#  间隔时间
t = 0.2
# 初始速度
v = 0 while current < d:
if current < deviation:
# 加速阶段
a = 2
else:
# 减速阶段
a = -2 # 初始速度
v0 = v
# 当前速度
v = v0 + a * t
# 位移
move = v0 * t + a * t * t / 2
# 当前位移
current += move
# 添加到轨迹,保留整数
track.append(round(move))
return track def move_slider(self, slider, track):
"""
按照轨迹移动滑块至缺口
:param slider:滑块
:param track: 轨迹
:return:
"""
# 鼠标按住滑块
ActionChains(self.browser).click_and_hold(slider).perform()
for i in track:
# 遍历轨迹元素,每次移动对应位移
ActionChains(self.browser).move_by_offset(xoffset=i, yoffset=0).perform()
time.sleep(0.5)
# 移动完成后,松开鼠标
ActionChains(self.browser).release().perform() def crack(self):
"""
模拟验证的各种操作
:return:None
"""
# 打开网站,输入注册手机号
self.browser.get(self.url)
self.wait.until(EC.presence_of_element_located((By.ID, 'phone')))
# 点击验证
button = self.get_button()
button.click()
# 获取验证码图片
img1 = self.get_geetest_img('captcha1.png')
# 获取滑块
slider = self.get_slider()
slider.click()
# 获取带缺口的图片
img2 = self.get_geetest_img('captcha2.png')
# 获取缺口位置
gap = self.get_gap(img1, img2)
print("缺口位置:", gap)
# 减去缺口位移
gap -= 6
# 获取轨迹
track = self.get_track(gap)
print("轨迹:", track)
# 拖动滑块
self.move_slider(slider, track) success = self.wait.until(
EC.text_to_be_present_in_element((By.ID, 'geetest_success_radar_tip_content'), '验证成功')) # 失败重试
if not success:
self.crack() if __name__ == '__main__':
crack = GeetestSpider()
crack.crack()

  

最新文章

  1. spider RPC插件化体系
  2. Oracle Sales Cloud:报告和分析(BIEE)小细节2——利用变量和过滤器传参(例如,根据提示展示不同部门的数据)
  3. python基础05 if选择
  4. ora-01033和ora-12560错误的解决方案
  5. php设计模式 数据对象映射模式
  6. 并行程序设计模式--Master-Worker模式
  7. UVa 10791 Minimum Sum LCM【唯一分解定理】
  8. Backbone.js学习之Router
  9. sunlime text 3 快捷键总结
  10. WebForm中TreeView的使用
  11. 用高德地图API 通过详细地址获得经纬度
  12. 在WPF中自定义你的绘制(二)
  13. Linux怎样访问Windows共享文件和文件夹
  14. centeOS6.5 RPM方式安装MySQL5.6
  15. html-webpack-plugin 遇到 throw new Error(&#39;Cyclic dependency&#39; + nodeRep)
  16. Unable to launch the IIS Express Web server
  17. 关于MySQL锁的详解
  18. Windows下的包管理工具-Scoop
  19. 6 个开源的家庭自己主动化工具 | Linux 中国
  20. JS 详解对象的继承

热门文章

  1. 代做Assignment时排比结构的使用解析
  2. html 基础 (9.19 第八天)
  3. (转)null和NULL和nullptr和””区别
  4. C++编程学习(四)声明/枚举
  5. Canvas基本定义
  6. C语言:大数求和
  7. 【分类问题中模型的性能度量(二)】超强整理,超详细解析,一文彻底搞懂ROC、AUC
  8. jar包-循环遍历-开机启动服务-微服务-多项目拷贝-pid杀死进程-mysql备份脚本-防火墙检测脚本
  9. 爱奇艺用券付费VIP电影+python爬虫程序+可视化界面+下载本地
  10. 关于torch.flatten的笔记