兄弟们肯定在搞爬虫时经常遇到滑块验证码的反爬措施,这苦恼了我几天,好在有dddd和selenium模块助我前行
第一个函数是登录函数,主要就是输入用户名和密码后自动弹出滑块验证码
主要用到selenium模块
导入模块:
from selenium import webdriver
from selenium.webdriver.common.by import By
知识点:
drive = webdriver.Firefox()
#创建实例,浏览器为火狐
element = driver.find_element(By.XPATH,"这里锁定元素输入xpath路径就行")
#利用By.XPATH锁定元素当然还有By.CLASSNAME、By.ID等等,提供不同元素名锁定元素
element.send_keys("111")
#向锁定的element元素输入111值
element.click()
#点击该元素
如果遇到同一CLASSBNAME有两个元素
可以使用
element = driver.find_elements(By.CLASSNAME)[1]
这样的方式锁定第二个元素位置 注意:element后面有个s
def login(driver,uname,pwd):
"""
某系统登录框输入手机号和密码,并点击成功
"""
driver.get("https://user.xxxx.com/user/ut=1&style=1#/pages/white-hat/login/login")
# 等待页面加载完成,设置超时停止加载
try:
driver.set_page_load_timeout(10)
except:
pass
# 点击同意按钮
ty = driver.find_element(By.XPATH,"/html/body/div/div/div[2]/div[2]/div/div[1]/div[2]/div/div[1]/div/div[2]/div[1]/div[1]/form/div[3]/label/span[1]/span")
ty.click()
#输入手机号
phone = driver.find_element(By.XPATH,"/html/body/div/div/div[2]/div[2]/div/div[1]/div[2]/div/div[1]/div/div[2]/div[1]/div[1]/form/div[1]/div/div[2]/input")
phone.send_keys(uname)
#输入密码
passwd = driver.find_element(By.XPATH,"/html/body/div/div/div[2]/div[2]/div/div[1]/div[2]/div/div[1]/div/div[2]/div[1]/div[1]/form/div[2]/div/div[2]/input")
passwd.send_keys(pwd)
#点击登录
dl = driver.find_element(By.XPATH,"/html/body/div/div/div[2]/div[2]/div/div[1]/div[2]/div/div[1]/div/div[2]/div[1]/div[1]/form/div[4]/div/button")
dl.click()
接下来silder_imgs函数主要是提取出滑动模块的三个图片,主要是为了计算移动距离而准备
def slider_imgs(driver):
"""
保存每次滑动验证码的三个图片
"""
#定位背景图元素的位置
active = True
while active:
try:
time.sleep(1)
img_element = driver.find_element(By.XPATH,"/html/body/div[2]/div[2]/div[6]/div/div[1]/div[1]/div/a/div[1]/div/canvas[1]")
hk_element = driver.find_element(By.XPATH,"/html/body/div[2]/div[2]/div[6]/div/div[1]/div[1]/div/a/div[1]/div/canvas[2]")
yt_element = driver.find_element(By.XPATH,"/html/body/div[2]/div[2]/div[6]/div/div[1]/div[1]/div/a/div[1]/canvas")
active = False
except:
continue
# 执行js 获取改元素的data:url
img_data_b64 = driver.execute_script("return arguments[0].toDataURL('image/png').substring(22);",img_element)
hk_data_b64 = driver.execute_script("return arguments[0].toDataURL('image/png').substring(22);",hk_element)
yt_data_b64 = driver.execute_script("return arguments[0].toDataURL('image/png').substring(22);",yt_element)
#解码
img_data = base64.b64decode(img_data_b64)
hk_data = base64.b64decode(hk_data_b64)
yt_data = base64.b64decode(yt_data_b64)
#以二进制保存
with open("./imgs/bg.png","wb") as f:
f.write(img_data)
#print("[*]滑块验证码背景图已保存")
with open("./imgs/hk.png","wb") as f:
f.write(hk_data)
#print("[*]滑块验证码滑块图片已保存")
with open("./imgs/yt.png","wb") as f:
f.write(yt_data)
#print("[*]滑块验证码原图已保存")
print("[*]保存滑动验证码成功")
提取出来如下图所示,(hk_jg.png是下一个模块处理后的结果,不用管)
计算距离的主要需要图片就是通过hk.png处理后的hk_jg.png和bg.png两个图片
接下来处理hk.png为hk_jg.png
直接传入hk.png这种图片的路径,可以实现将带有白框的图片截取成只有图片的最小方框,例如:
这么多白框的图片变成下图这种
代码实现如下所示
def hk_chuli(hk_img):
"""
处理滑块图片
:return:
"""
image = Image.open(hk_img) # 打开tiff图像
ImageArray = np.array(image)
row = ImageArray.shape[0]
col = ImageArray.shape[1]
# 先计算所有图片的裁剪范围,然后再统一裁剪并输出图片
x_left = row
x_top = col
x_right = 0
x_bottom = 0
for r in range(row):
for c in range(col):
# if ImageArray[row][col][0] < 255 or ImageArray[row][col][0] ==0:
if ImageArray[r][c][0] < 255 and ImageArray[r][c][0] != 0: # 外框有个黑色边框,增加条件判断
if x_top > r:
x_top = r # 获取最小x_top
if x_bottom < r:
x_bottom = r # 获取最大x_bottom
if x_left > c:
x_left = c # 获取最小x_left
if x_right < c:
x_right = c # 获取最大x_right
#print(x_left, x_top, x_right, x_bottom)
cropped = image.crop((x_left - 5, x_top - 5, x_right + 5, x_bottom + 5)) # (left, upper, right, lower)
cropped.save(r".\imgs\{}.png".format("hk_jg"))
print("[*]处理滑块图片成功")
现在开始引入带带弟弟(ddddocr)模块计算滑动距离了
模块引用:
import ddddocr知识点:
det = ddddocr.Dddd0cr(det=False,show_ad=False)
#创建实例
det.slide_match(hk_jg,bgsimple_target=True)
#传入处理好的滑块图片和有缺口的背景图片,实现计算距离,准确度很高,但是可能固定查5左右,要自己调试
def get_len(hk,bg):
"""
调用ddddocr获取滑动距离
"""
det = ddddocr.DdddOcr(det=False,ocr=False,show_ad=False)
with open(hk,"rb") as f:
hk = f.read()
with open(bg,"rb") as f:
bg = f.read()
res = det.slide_match(hk,bg,simple_target=True)
return res["target"][0]
下一步就是利用selenium模块拖动滑块元素移动了
具体解释都在代码块里,这里再提一点,由于极验的滑块验证码会检验鼠标轨迹,所以必须模拟人手的鼠标滑动轨迹,所以我搞了一个加速的函数
get_tracks就是加速函数,这个函数各位直接拿去用就行,不用理解,在自动滑动的时候引用就行
def get_tracks(distance):
"""
拿到移动轨迹,模拟人的滑动行为,先匀加速后匀减速
匀变速运动基本公式:
1.v=v₀+at
2.s=v₀t+1/2*at²
:param distance:滑块一次性快速移动到某一位置后剩下的距离,进行五等分
:return:位置/轨迹列表,列表内的一个元素代表0.3s的位移
"""
v = 0 # 初速度
t = 0.3 # 单位时间为0.3s来统计轨迹,轨迹即0.3内的位移
tracks = [] # 位置/轨迹列表,列表内的一个元素代表0.3s的位移
current = 0 # 当前的位移
mid = distance * 4 / 5 # 到达mid值开始减速,前4/5匀加速,后1/5匀减速
while current < distance:
if current < mid: # 加速度越小,单位时间内的位移越小,模拟的轨迹就越多越详细
a = 2
else:
a = -3
v0 = v # 初速度
s = v0 * t + 0.5 * a * (t ** 2) # 0.3s内的位移
current += s # 当前的位置
tracks.append(round(s)) # 添加到轨迹列表
v = v0 + a * t # 速度已经达到v,该速度作为下次的初速度
return tracks # tracks:[第1个0.3s的移动距离,第2个0.3s的移动距离,......]
滑动函数如下所示
def slider_verification_code(driver,length):
"""
滑动滑块验证码
"""
#按住滑块按钮
silder = driver.find_element(By.XPATH,"/html/body/div[2]/div[2]/div[6]/div/div[1]/div[2]/div[2]")
# 创建鼠标动态链,
subiao = ActionChains(driver)
# 点击指定元素并保持
subiao.click_and_hold(on_element=silder).perform()
# 滑动指定单位 xoffset代表水平方向的移动 正数为向右
subiao.move_to_element_with_offset(to_element=silder,xoffset=15,yoffset=15).perform()
time.sleep(0.5)
#使用加速度
tracks = get_tracks(length-15)
for track in tracks:
subiao.move_by_offset(xoffset=track,yoffset=0).perform()
# 放开鼠标
time.sleep(1)
subiao.release().perform()
#网络不给力情况
try:
continue_element = driver.find_element(By.XPATH, "/html/body/div[2]/div[2]/div[4]/div[3]")
if continue_element:
print("[*]出现网络不给力情况,立马开始重试")
continue_element.click()
main()
except:
pass
# 没绕过情况
try:
#检测滑动验证码是否还存在
huadon = driver.find_element(By.CLASS_NAME,"geetest_slider_button")
if huadon:
# 刷新
sx = driver.find_element(By.XPATH, "/html/body/div[2]/div[2]/div[6]/div/div[2]/div/a[2]")
sx.click()
print("[*]绕过滑动验证码失败,开始下一次尝试\n")
main()
try:
#检测是否出现多次尝试失败情况
hk_anniu_element = driver.find_element(By.CLASS_NAME,"geetest_slider_button")
except:
print("[*]ERROR:多次尝试绕过验证码失败,立马开始重试\n")
conshi_element = driver.find_element(By.XPATH,"/html/body/div[2]/div[2]/div[4]/div[3]")
conshi_element.click()
time.sleep(2)
main()
except:
print("[*]滑动验证码bypass成功\n")
pass
破解效果视频如下所示
滑动验证码绕过效果视频
版权归原作者 网安_秋刀鱼 所有, 如有侵权,请联系我们删除。