使用Crystal语言实现极验滑动验证码识别


我们将展示如何使用Crystal语言实现自动识别极验滑动验证码的全过程,从模拟点击到识别滑动缺口、计算位移并模拟拖动滑块。如果认证失败,则重复调用直到成功。

识别思路
模拟点击切换为滑动验证,并显示验证界面。
识别滑动缺口的位置,计算位移。更多内容联系1436423940
模拟拖动滑块。
若认证失败,重复调用。
详细过程及代码
初始化
首先,初始化selenium对象和一些参数配置,极验验证码测试页面的网址如下:

crystal

require "selenium-webdriver"
require "file_utils"
require "image_magick"

class CrackGeetest
  BORDER = 6

  def initialize
    @url = "https://www.geetest.com/type/"
    @browser = Selenium::WebDriver.for :chrome
    @wait = Selenium::WebDriver::Wait.new(timeout: 10)
  end

  def open
    @browser.get(@url)
  end

  def close
    @browser.quit
  end
end
定义了一个 CrackGeetest 类,初始化selenium对象和一些参数配置,网址是极验的验证码测试页面。

模拟点击
首先模拟点击切换为滑动验证,然后模拟点击弹出验证图片。

crystal

def change_to_slide
  huadong = @wait.until { @browser.find_element(css: ".products-content ul > li:nth-child(2)") }
  huadong
end

def get_geetest_button
  button = @wait.until { @browser.find_element(css: ".geetest_radar_tip") }
  button
end
该步骤定义了两个方法,均利用显示等待的方法实现。并返回按钮对象,后用click方法模拟点击。

获取背景图
首先等待验证码加载完成(wait_pic),获取网页截图(get_screenshot),然后获取验证背景图所在的位置及大小参数(get_position)和滑块对象(get_slider)。

crystal

def wait_pic
  @wait.until { @browser.find_element(css: ".geetest_popup_wrap") }
end

def get_screenshot
  screenshot = @browser.screenshot_as(:png)
  File.open("screenshot.png", "wb") { |f| f.write(screenshot) }
  screenshot
end

def get_position
  img = @wait.until { @browser.find_element(class: "geetest_canvas_img") }
  sleep 2
  location = img.location
  size = img.size
  { top: location.y, bottom: location.y + size.height, left: location.x, right: location.x + size.width }
end

def get_slider
  slider = @wait.until { @browser.find_element(class: "geetest_slider_button") }
  slider
end
再通过上述返回的背景图位置和大小参数,对网页截图进行切片(get_geetest_image),最后获取背景图。

crystal

def get_geetest_image(name = "captcha.png")
  position = get_position
  puts "验证码位置: #{position[:top]}, #{position[:bottom]}, #{position[:left]}, #{position[:right]}"
  get_screenshot
  image = Magick::Image.read("screenshot.png").first.crop(position[:left], position[:top], position[:right] - position[:left], position[:bottom] - position[:top])
  image.write(name)
  image
end
到这里,已经获取了带缺口的背景图。我们需要获取不带缺口滑块的原图。这里通过改变CSS样式获得原图。

crystal

def delete_style
  js = 'document.querySelectorAll("canvas")[2].style=""'
  @browser.execute_script(js)
end
执行js脚本之后(delete_style)获得了无缺口的原图,再调用之前的截图方法,就可以获取同大小的背景图。

识别缺口
我们得到了两张图,接下来对比它们来获取缺口位置。

crystal

def is_pixel_equal(img1, img2, x, y)
  pix1 = img1.pixel_color(x, y)
  pix2 = img2.pixel_color(x, y)
  threshold = 60
  if (pix1.red - pix2.red).abs < threshold &&
     (pix1.green - pix2.green).abs < threshold &&
     (pix1.blue - pix2.blue).abs < threshold
    true
  else
    false
  end
end
get_gap()方法遍历两张图片的每个像素,再利用is_pixel_equal()方法判断两张图片同一位置的像素。

crystal

def get_gap(img1, img2)
  left = 60
  (left...img1.columns).each do |i|
    (0...img1.rows).each do |j|
      unless is_pixel_equal(img1, img2, i, j)
        return i
      end
    end
  end
  left
end
模拟拖动
我们获得了滑块的位置,现在只需计算距离并模拟拖动即可。

crystal

def get_track(distance)
  track = [] of Int32
  current = 0
  mid = distance * 3 / 5
  t = 0.2
  v = 0
  distance += 14
  while current < distance
    a = current < mid ? 2 : -1.5
    v0 = v
    v = v0 + a * t
    move = v0 * t + 0.5 * a * t * t
    current += move
    track << move.round
  end
  track
end
前3/5路程加速,后面减速,track返回的是一个列表,其中每个元素代表的是每次移动的距离。然后模拟释放鼠标时的人手抖动(shake_mouse)。

crystal

def shake_mouse
  action = Selenium::WebDriver::ActionBuilder.new(@browser)
  action.move_by(x: -3, y: 0).perform
  action.move_by(x: 2, y: 0).perform
end

def move_to_gap(slider, tracks)
  action = Selenium::WebDriver::ActionBuilder.new(@browser)
  action.click_and_hold(slider).perform
  tracks.each do |x|
    action.move_by(x: x, y: 0).perform
  end
  [-1, -1, -2, -2, -3, -2, -2, -1, -1].each do |x|
    action.move_by(x: x, y: 0).perform
  end
  shake_mouse
  sleep 0.5
  action.release.perform
end
最后根据之前所得到的运动轨迹拖动滑块(move_to_gap)即可。

整个控制流程
执行主体流程,若验证失败,则再次调用crack()进行识别,直至成功。

crystal

def crack
  begin
    open
    change_to_slide.click
    get_geetest_button.click
    wait_pic
    slider = get_slider
    image1 = get_geetest_image("captcha1.png")
    delete_style
    image2 = get_geetest_image("captcha2.png")
    gap = get_gap(image1, image2)
    puts "缺口位置: #{gap}"
    gap -= BORDER
    track = get_track(gap)
    move_to_gap(slider, track)
    success = @wait.until { @browser.find_element(class: "geetest_success_radar_tip_content").text == "验证成功" }
    puts success
    sleep 5
    close
  rescue
    puts "Failed-Retry"
    crack
  end
end

if __FILE__ == $0
  crack_geetest = CrackGeetest.new
  crack_geetest.crack
end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值