本文将展示如何使用Lua和Selenium实现极验滑动验证码的自动识别。从模拟点击到识别滑动缺口、计算位移并模拟拖动滑块。如果认证失败,则重复调用直到成功。
识别思路
模拟点击切换为滑动验证,并显示验证界面。
识别滑动缺口的位置,计算位移。
模拟拖动滑块。
若认证失败,重复调用。
详细过程及代码
初始化
首先,初始化Selenium WebDriver对象并配置参数。极验验证码测试页面的网址如下:
lua
local selenium = require("selenium")
local lfs = require("lfs")
local image = require("image")
local time = require("socket").gettime
local BORDER = 6
local CrackGeetest = {}
CrackGeetest.__index = CrackGeetest
function CrackGeetest:new()
local self = setmetatable({}, CrackGeetest)
self.url = "https://www.geetest.com/type/"
self.browser = selenium.ChromeDriver()
self.wait = selenium.WebDriverWait(self.browser, 10)
return self
end
function CrackGeetest:open()
self.browser:get(self.url)
end
function CrackGeetest:close()
self.browser:close()
self.browser:quit()
end
function CrackGeetest:change_to_slide()
local huadong = self.wait:until(selenium.conditions.elementToBeClickable("css selector", ".products-content ul > li:nth-child(2)"))
return huadong
end
function CrackGeetest:get_geetest_button()
local button = self.wait:until(selenium.conditions.elementToBeClickable("css selector", ".geetest_radar_tip"))
return button
end
function CrackGeetest:wait_pic()
self.wait:until(selenium.conditions.presenceOfElementLocated("css selector", ".geetest_popup_wrap"))
end
function CrackGeetest:get_screenshot()
local screenshot = self.browser:getScreenshotAs("png")
local screenshot_image = image.load(screenshot)
return screenshot_image
end
function CrackGeetest:get_position()
local img = self.wait:until(selenium.conditions.presenceOfElementLocated("class name", "geetest_canvas_img"))
time.sleep(2)
local location = img:location()
local size = img:size()
return {top = location.y, bottom = location.y + size.height, left = location.x, right = location.x + size.width}
end
function CrackGeetest:get_slider()
local slider = self.wait:until(selenium.conditions.elementToBeClickable("class name", "geetest_slider_button"))
return slider
end
function CrackGeetest:get_geetest_image(name)
local position = self:get_position()
print(string.format("验证码位置: top=%d, bottom=%d, left=%d, right=%d", position.top, position.bottom, position.left, position.right))
local screenshot = self:get_screenshot()
local captcha = screenshot:crop(position.left, position.top, position.right - position.left, position.bottom - position.top)
captcha:save(name)
return captcha
end
function CrackGeetest:delete_style()
self.browser:executeScript('document.querySelectorAll("canvas")[2].style=""')
end
function CrackGeetest:is_pixel_equal(img1, img2, x, y)
local pix1 = img1:get_pixel(x, y)
local pix2 = img2:get_pixel(x, y)
local threshold = 60
return math.abs(pix1[1] - pix2[1]) < threshold and math.abs(pix1[2] - pix2[2]) < threshold and math.abs(pix1[3] - pix2[3]) < threshold
end
function CrackGeetest:get_gap(img1, img2)
local left = 60
for i = left, img1:width() - 1 do
for j = 0, img1:height() - 1 do
if not self:is_pixel_equal(img1, img2, i, j) then
return i
end
end
end
return left
end
function CrackGeetest:get_track(distance)
local track = {}
local current = 0
local mid = distance * 3 / 5
local t = 0.2
local v = 0
distance = distance + 14
while current < distance do
local a = (current < mid) and 2 or -1.5
local v0 = v
v = v0 + a * t
local move = v0 * t + 0.5 * a * t * t
current = current + move
table.insert(track, move)
end
return track
end
function CrackGeetest:shake_mouse()
local actions = selenium.ActionChains(self.browser)
actions:moveByOffset(-3, 0):perform()
actions:moveByOffset(2, 0):perform()
end
function CrackGeetest:move_to_gap(slider, tracks)
local actions = selenium.ActionChains(self.browser)
actions:clickAndHold(slider):perform()
for _, x in ipairs(tracks) do
actions:moveByOffset(x, 0):perform()
end
actions:release():perform()
self:shake_mouse()
time.sleep(0.5)
end
function CrackGeetest:crack()
while true do
self:open()
self:change_to_slide():click()
self:get_geetest_button():click()
self:wait_pic()
local slider = self:get_slider()
local image1 = self:get_geetest_image("captcha1.png")
self:delete_style()
local image2 = self:get_geetest_image("captcha2.png")
local gap = self:get_gap(image1, image2) - BORDER
local track = self:get_track(gap)
self:move_to_gap(slider, track)
local success = self.wait:until(selenium.conditions.textToBePresentInElement("class name", "geetest_success_radar_tip_content", "验证成功"))
if success then
print("验证成功")
time.sleep(5)
self:close()
break
else
print("验证失败,重试中...")
end
end
local crack = CrackGeetest:new()
crack:crack()