爬虫, 滑块验证码

简单说明一下:

测试网址: https://accounts.douban.com/passport/login (豆瓣登陆页)
类型: 滑块验证码
基本思路: 下载图片到本地 => 使用CV获取边缘值并计算距离 => 模拟用户拖动

一、模拟点击

1.点击至验证页

首先测试环境:
import requests
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from get_pic_distance import get_distance
import time

path = r"D:\python310\chromedriver.exe"
browser = webdriver.Chrome(path)
browser.get('https://accounts.douban.com/passport/login')

首先分析首页:
在这里插入图片描述
可得步骤:
①选择密码登陆 => ②输入账号 => ③输入密码 => ④ 点击登陆
分别找到对应控件的id(或name或xpath)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
即:

browser.find_element(By.CLASS_NAME, 'account-tab-account').click()
 # 登陆用户名和密码
browser.find_element(By.ID, 'username').send_keys('1111111111')
browser.find_element(By.ID, 'password').send_keys('1111111111')
# 点击登陆
browser.find_element(By.CLASS_NAME, 'btn.btn-account.btn-active').click()

2.保存图片

图片这里直接找url是找不到的,因为有一个iframe,需要先进到iframe中,再查找图片的url.
在这里插入图片描述
在这里插入图片描述

# 找到内嵌的iframe 并进入
iframe = browser.find_element(By.ID, 'tcaptcha_iframe_dy')
browser.switch_to.frame(iframe)
url = browser.find_element(By.ID, 'slideBg').get_attribute("style")
# 正则提取链接
url = 'https://t.captcha.qq.com'+re.findall('url\("(.*?)"\);', url, re.S)[0]
# 下载图片到本地
with open('verify.jpg', 'wb') as f:
    f.write(requests.get(url).content)

这样就获取到了图片
在这里插入图片描述

二、CV识别缺口

1. 边缘识别

提取边缘值:① 高斯滤波降噪 ② 图像二值化 ③ 提取边缘值

# 高斯滤波
blurred = cv.GaussianBlur(image, (5, 5), 0)
# 图像二值化
canny = cv.Canny(blurred, 200, 400)
# 提取边缘轮廓  参数说明 分别为: 二值图像, 只检测最外围轮廓, 仅保存轮廓的拐点信息
contours, hierarchy = cv.findContours(canny, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)

二值化效果图:
在这里插入图片描述

2.计算距离

这个要复杂些,主要是判断边缘值的形状和方向(moments)
然后根据这些信息对边缘图像进行筛选(一般是根据图像的周长和面积来筛选)(可能有其他边缘值干扰)
最后选择图像的最小外接矩形的最小X值来作为距离
即:

    for i, contour in enumerate(contours):
        M = cv.moments(contour)  # 计算边缘值的形状和方向  主要是判断方向
        print(M)
        if M['m00'] == 0:
            cx = 0
        else:
            cx, cy = M['m10'] / M['m00'], M['m01'] / M['m00']
        print()
        # 面积在5000-70000之间 且 周长在 300-390  依次来筛选在指定范围大小的矩形
        if 5000 < cv.contourArea(contour) < 7000 and 300 < cv.arcLength(contour, True) < 390:  # 计算轮廓的面积
            if cx < 240:
                continue
            x, y, w, h = cv.boundingRect(contour)  # 最小外接矩形
            # cv.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2)  显示外接矩形
            return x
    return 0

矩形效果图:
在这里插入图片描述
返回结果:250

三、模拟用户拖动

1. 距离分散

对于稍微严格点的网页验证,会识别用户滑动的轨迹,这时就需要模拟用户的滑动轨迹(代码从网上参考,来源忘了,这里直接贴代码了)

def get_track(distance):
    track = []
    current = 0
    mid = distance * 3 / 4
    t = 0.2
    v = 0
    while current < distance:
        if current < mid:
            a = 2
        else:
            a = -3
        v0 = v
        v = v0 + a * t
        move = v0 * t + 1 / 2 * a * t * t
        current += move
        track.append(round(move))
    return track

运算结果:[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4]

2. 拖动

最后就是滑动滑块了,先找到滑块的位置:
先获取滑块属性,再定义滑块

btn = browser.find_element(By.CLASS_NAME, 'tc-fg-item.tc-slider-normal')  # 获取滑块
ActionChains(browser).click_and_hold(btn).perform()  # 定义滑动  调用函数缓慢滑动滑块

按照生成的距离列表,滑动滑块:

for x in get_track(dis):
    ActionChains(browser).move_by_offset(xoffset=x, yoffset=0).perform()
# 释放滑块
ActionChains(browser).release().perform()

最后补充一点:
这个方式不能应对所有的滑块情况,所以如果识别距离为0,这个时候要刷新图片,方法如法炮制,点击刷新即可。

最终代码:

import re
import requests
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from get_pic_distance import get_distance
import time

path = r"D:\python310\chromedriver.exe"
browser = webdriver.Chrome(path)
browser.get('https://accounts.douban.com/passport/login')


def get_track(distance):
    track = []
    current = 0
    mid = distance * 3 / 4
    t = 0.2
    v = 0
    while current < distance:
        if current < mid:
            a = 2
        else:
            a = -3
        v0 = v
        v = v0 + a * t
        move = v0 * t + 1 / 2 * a * t * t
        current += move
        track.append(round(move))
    return track


# 滑块验证
def move_verify():
    error_num = 0
    dis = 0
    while error_num <= 3:  # 有一种形状的拼图暂获取不到 如果遇到则刷新
        print('下载图片...')
        # browser.switch_to.default_content()
        iframe = browser.find_element(By.ID, 'tcaptcha_iframe_dy')  # 找到“嵌套”的iframe
        browser.switch_to.frame(iframe)
        url = browser.find_element(By.ID, 'slideBg').get_attribute("style")
        # 正则提取链接
        url = 'https://t.captcha.qq.com'+re.findall('url\("(.*?)"\);', url, re.S)[0]
        # 下载图片到本地
        with open('verify.jpg', 'wb') as f:
            f.write(requests.get(url).content)
        print('开始计算距离...')
        dis = get_distance('verify.jpg')  # 如果返回为空  则需要重新获取图片
        if dis != 0:
            print(dis)
            dis = round(dis * 341 / 680, 2) - 27.5 - 12  # -12微调
            print(f'实际距离:{dis}')
            break
        else:
            print('返回了0')
            browser.find_element(By.ID, 'reload').click()  # 未识别到距离  则重新获取验证
            error_num += 1
    btn = browser.find_element(By.CLASS_NAME, 'tc-fg-item.tc-slider-normal')  # 获取滑块
    ActionChains(browser).click_and_hold(btn).perform()  # 定义滑动  调用函数缓慢滑动滑块
    for x in get_track(dis):
        ActionChains(browser).move_by_offset(xoffset=x, yoffset=0).perform()

    ActionChains(browser).release().perform()
    print('结束')
    time.sleep(600)


if __name__ == '__main__':
    # 切换为密码登陆
    # account-tab-account
    browser.find_element(By.CLASS_NAME, 'account-tab-account').click()
    # 登陆用户名和密码
    browser.find_element(By.ID, 'username').send_keys('1111111111')
    browser.find_element(By.ID, 'password').send_keys('1111111111')
    time.sleep(3)
    # 点击登陆
    browser.find_element(By.CLASS_NAME, 'btn.btn-account.btn-active').click()
    time.sleep(2)
    # time.sleep(2)
    move_verify()

最终效果展示:
请添加图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值