滑块验证码(腾讯)——基于selenium,pillow实现
Posted xtjiaoyou
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了滑块验证码(腾讯)——基于selenium,pillow实现相关的知识,希望对你有一定的参考价值。
与网上的其他滑块验证码不同,腾讯的验证码可以直接通过url请求得到,只需要对url进一步分析,提取出验证码原图的地址,并将图片下载即可。
但据我观察,该url似乎是有两种不同类型的地址格式,需要具体分析。这里,选择其中一种进行实验,源码在文章末尾。
def get_img(self): """ 获取验证码阴影图和原图 :return: """ self.driver.switch_to.frame(‘tcaptcha_iframe‘) time.sleep(3) # 获取有阴影的图片 src = self.driver.find_element_by_id(‘slideBg‘).get_attribute(‘src‘) # 分析图片地址,发现原图地址可以通过阴影图地址改动获取 src_bg = re.sub(‘_1_‘, ‘_0_‘, src) urlretrieve(src, ‘img1.png‘) urlretrieve(src_bg, ‘img2.png‘)
有一个需要注意的问题,下载到本地的图片对原图进行了放大,所以要对图片尺寸进行调整还原,保证后面计算出的偏移值的正确性
def resize_img(self, img): """ 下载的图片把网页中的图片进行了放大,所以将图片还原成原尺寸 :param img: 图片 :return: 返回还原后的图片 """ a = 2.428 # 通过本地图片与原网页图片的比较,计算出的缩放比例 (x, y) = img.size x_resize = int(x // a) y_resize = int(y // a) img = img.resize((x_resize, y_resize), Image.ANTIALIAS)
此时,已经拿到原图和阴影图,只需要进行像素比较即可,网上有其他相关教程,可参考
def is_pixel_equal(self, img1, img2, x, y): """ 比较两张图片同一点上的像数值,差距大于设置标准返回False :param img1: 阴影图 :param img2: 原图 :param x: 横坐标 :param y: 纵坐标 :return: 是否相等 """ pixel1, pixel2 = img1.load()[x, y], img2.load()[x, y] sub_index = 100 if abs(pixel1[0] - pixel2[0]) < sub_index and abs(pixel1[1] - pixel2[1]) < sub_index and abs( pixel1[2] - pixel2[2]) < sub_index: return True else: return False def get_gap_offset(self, img1, img2): ‘‘‘ 获取缺口的偏移量 ‘‘‘ offset = None distance = 70 for i in range(distance, img1.size[0]): for j in range(img1.size[1]): # 两张图片对比,(i,j)像素点的RGB差距,过大则该x为偏移值 if not self.is_pixel_equal(img1, img2, i, j): offset = i return offset return offset
最后,通过selenuim的动作链,模拟滑块拖动
def operate_slider(self, track): """ 拖动滑块 :param track: 运动轨迹 :return: """ # 定位到拖动按钮 slider_bt = self.driver.find_element_by_xpath(‘//div[@class="tc-drag-thumb"]‘) # 点击拖动按钮不放 ActionChains(self.driver).click_and_hold(slider_bt).perform() # 按正向轨迹移动 for i in track: ActionChains(self.driver).move_by_offset(xoffset=i, yoffset=0).perform() time.sleep(random.random() / 100) # 每移动一次随机停顿0-1/100秒之间骗过了极验,通过率很高 time.sleep(random.random()) # 按逆向轨迹移动 back_tracks = [-1, -0.5, -1] for i in back_tracks: time.sleep(random.random() / 100) ActionChains(self.driver).move_by_offset(xoffset=i, yoffset=0).perform() # 模拟人手抖动 self.shake_mouse() time.sleep(random.random()) # 松开滑块按钮 ActionChains(self.driver).release().perform()
下面附上完整的代码,仅供参考,不足之处,欢迎大家指正
import time import re import random from selenium import webdriver from urllib.request import urlretrieve from PIL import Image from selenium.webdriver.common.action_chains import ActionChains class Tencent(): def __init__(self): """ 初始化属性,传入url地址,驱动路径,浏览器窗口最大化,伪造ua """ self.url = ‘https://qzone.qq.com/‘ driver_path = r‘C:UsersxiaodengtangAppDataLocalGoogleChromeApplicationchromedriver.exe‘ self.driver = webdriver.Chrome(executable_path=driver_path) self.driver.maximize_window() self.headers = { ‘user-agent‘: ‘Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/79.0.3945.130 Safari/537.36‘} def input_username_password(self, account, password): """ 打开浏览器,传入账号、密码,定位到登录窗口,切换登陆方式 :param account: :param password: :return: """ self.driver.get(self.url) time.sleep(1) self.driver.switch_to.frame(‘login_frame‘) self.driver.find_element_by_id(‘switcher_plogin‘).click() time.sleep(1) self.driver.find_element_by_id(‘u‘).send_keys(account) time.sleep(0.5) self.driver.find_element_by_id(‘p‘).send_keys(password) time.sleep(0.5) self.driver.find_element_by_class_name(‘login_button‘).click() def get_img(self): """ 获取验证码阴影图和原图 :return: """ self.driver.switch_to.frame(‘tcaptcha_iframe‘) time.sleep(3) # 获取有阴影的图片 src = self.driver.find_element_by_id(‘slideBg‘).get_attribute(‘src‘) # 分析图片地址,发现原图地址可以通过阴影图地址改动获取 src_bg = re.sub(‘_1_‘, ‘_0_‘, src) urlretrieve(src, ‘img1.png‘) urlretrieve(src_bg, ‘img2.png‘) captcha1 = Image.open(‘img1.png‘) captcha2 = Image.open(‘img2.png‘) return captcha1, captcha2 def resize_img(self, img): """ 下载的图片把网页中的图片进行了放大,所以将图片还原成原尺寸 :param img: 图片 :return: 返回还原后的图片 """ a = 2.428 # 通过本地图片与原网页图片的比较,计算出的缩放比例 (x, y) = img.size x_resize = int(x // a) y_resize = int(y // a) img = img.resize((x_resize, y_resize), Image.ANTIALIAS) return img def is_pixel_equal(self, img1, img2, x, y): """ 比较两张图片同一点上的像数值,差距大于设置标准返回False :param img1: 阴影图 :param img2: 原图 :param x: 横坐标 :param y: 纵坐标 :return: 是否相等 """ pixel1, pixel2 = img1.load()[x, y], img2.load()[x, y] sub_index = 100 if abs(pixel1[0] - pixel2[0]) < sub_index and abs(pixel1[1] - pixel2[1]) < sub_index and abs( pixel1[2] - pixel2[2]) < sub_index: return True else: return False def get_gap_offset(self, img1, img2): ‘‘‘ 获取缺口的偏移量 ‘‘‘ offset = None distance = 70 for i in range(distance, img1.size[0]): for j in range(img1.size[1]): # 两张图片对比,(i,j)像素点的RGB差距,过大则该x为偏移值 if not self.is_pixel_equal(img1, img2, i, j): offset = i return offset return offset def get_track(self, offset): ‘‘‘ 计算滑块的移动轨迹 ‘‘‘ offset -= 30 # 滑块并不是从0开始移动,有一个初始值 a = offset / 4 track = [a, a, a, a] return track def shake_mouse(self): """ 模拟人手释放鼠标抖动 :return: None """ ActionChains(self.driver).move_by_offset(xoffset=-2, yoffset=0).perform() ActionChains(self.driver).move_by_offset(xoffset=2, yoffset=0).perform() def operate_slider(self, track): """ 拖动滑块 :param track: 运动轨迹 :return: """ # 定位到拖动按钮 slider_bt = self.driver.find_element_by_xpath(‘//div[@class="tc-drag-thumb"]‘) # 点击拖动按钮不放 ActionChains(self.driver).click_and_hold(slider_bt).perform() # 按正向轨迹移动 for i in track: ActionChains(self.driver).move_by_offset(xoffset=i, yoffset=0).perform() time.sleep(random.random() / 100) # 每移动一次随机停顿0-1/100秒之间骗过了极验,通过率很高 time.sleep(random.random()) # 按逆向轨迹移动 back_tracks = [-1, -0.5, -1] for i in back_tracks: time.sleep(random.random() / 100) ActionChains(self.driver).move_by_offset(xoffset=i, yoffset=0).perform() # 模拟人手抖动 self.shake_mouse() time.sleep(random.random()) # 松开滑块按钮 ActionChains(self.driver).release().perform() def login(self, account, password): ‘‘‘ 实现主要的登陆逻辑 ‘‘‘ self.input_username_password(account, password) time.sleep(2) a, b = self.get_img() a = self.resize_img(a) b = self.resize_img(b) distance = self.get_gap_offset(a, b) track = self.get_track(distance) self.operate_slider(track) if __name__ == ‘__main__‘: qq = Tencent() account = ‘123548658‘ password = ‘yanzhengma‘ qq.login(account, password)
以上是关于滑块验证码(腾讯)——基于selenium,pillow实现的主要内容,如果未能解决你的问题,请参考以下文章