最早在爬虫中遇到滑动验证码是在国家企业信用信息公示系统中,当时也是运用selenium来完成。现在该网站已经改为点触验证码了,故现在用bilibili登录页面作为一个示例。
识别滑动验证码需要完成如下几步:
1)模拟点击验证按钮
2)得到完整的验证码图片
3)得到带缺口的验证码图片
4)比较图片的像素差异,识别缺口
5)模拟滑动动作
b站的登录地址为https://passport.bilibili.com/login,需要预先注册好的账号
class BilibiliLogin():
def __init__(self):
self.url = 'https://passport.bilibili.com/login'
self.browser = webdriver.Chrome()
self.wait = WebDriverWait(self.browser, 10)
self.username = username
self.password = password
实现思路中的第一步,点击按钮显示完整的滑动验证码图片,然后实现第二步得到完整图片
点击圈中按钮,会显示完整的图片
def get_button(self):
"""
获取初始验证按钮
:return:
"""
button = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'gt_ajax_tip')))
return button
通过分析节点信息,得到图片位置并保存图片
def get_position(self):
"""
获取验证码位置
:return: 验证码位置元组
"""
img = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'gt_cut_fullbg')))
time.sleep(2.5)
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_image(self, image):
"""
获取验证码图片
:return: 图片对象
"""
top, bottom, left, right = self.get_position()
screenshot = self.get_screenshot()
captcha = screenshot.crop((left, top, right, bottom))
captcha.save(image)
return captcha
单击一下滑动按钮,会出现缺口,再调用get_image()方法,得到带有缺口的图片。
def get_gap(self, image1, image2):
"""
获取缺口偏移量
:param image1: 不带缺口图片
:param image2: 带缺口图片
:return:
"""
left = 60
for i in range(left, image1.size[0]):
for j in range(image1.size[1]):
print(self.is_pixel_equal(image1, image2, i, j))
if not self.is_pixel_equal(image1, image2, i, j):
left = i
return left
return left
def is_pixel_equal(self, image1, image2, x, y):
"""
判断两个像素是否相同
:param image1: 图片1
:param image2: 图片2
:param x: 位置x
:param y: 位置y
:return: 像素是否相同
"""
# 取两个图片的像素点
pixel1 = image1.load()[x, y]
pixel2 = image2.load()[x, y]
threshold = 20
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
在拖动滑块的时候,我借鉴了网上的一些经验,为模拟人的动作,拖动滑块应该是先加速后减速,所以构造了一个track函数,整个操作本身直接用selenium的动作链完成即可。
def move_to_gap(self, slider, track):
"""
拖动滑块到缺口处
:param slider: 滑块
:param track: 轨迹
:return:
"""
ActionChains(self.browser).click_and_hold(slider).perform()
for x in track:
ActionChains(self.browser).move_by_offset(xoffset=x, yoffset=0).perform()
time.sleep(0.5)
ActionChains(self.browser).release().perform()
效果如下gif动图,完整代码以后上传到github。