用opencv+playwright过滑动验证码

这篇具有很好参考价值的文章主要介绍了用opencv+playwright过滑动验证码。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

梳理思路

编写代码

总结与提高


在本节,我们将使用opencv和playwright这两个库通过QQ空间的滑动验证码。 

opencv滑动验证码,《用机器/深度学习过爬虫验证码》,opencv,playwright,python,滑动验证码,爬虫

梳理思路

1. 使用playwright打开浏览器,访问qq空间登录页面。

2. 点击密码登录。

opencv滑动验证码,《用机器/深度学习过爬虫验证码》,opencv,playwright,python,滑动验证码,爬虫

 

3. 输入账号密码并点击登录。

opencv滑动验证码,《用机器/深度学习过爬虫验证码》,opencv,playwright,python,滑动验证码,爬虫

4. 出现滑动验证码图片后,我们就可以获取到验证码背景图以及滑块图片。验证码背景图片通过元素style中的url链接就可以获取到,由于下载保存的是原图,所以我们要将宽度调整为280px,280这个值同样也可以在style中看到。

注:从style中也可以看到height值为200px,但其实这个包含了下方滑轨的高度,因此图片的真实高度要小于200px。所以我们在调整原图大小时,高度不要设为200px,而是通过以下公式进行等比缩放。

调整后的高度 = 原图高/(原图宽/280)

opencv滑动验证码,《用机器/深度学习过爬虫验证码》,opencv,playwright,python,滑动验证码,爬虫

5. 我们同样可以找到滑块图片的链接,但打开后却是这样的。

opencv滑动验证码,《用机器/深度学习过爬虫验证码》,opencv,playwright,python,滑动验证码,爬虫

opencv滑动验证码,《用机器/深度学习过爬虫验证码》,opencv,playwright,python,滑动验证码,爬虫

由于不知道滑块在这张大图上的位置,所以无法有效截取。另一种方案是直接通过屏幕截取获得滑块图片。首先,对全屏幕进行截图,然后用playwright获取到滑块元素,获取到该元素的位置和大小后,就可以截取了。滑块的起始位置就是style中的left值。

opencv滑动验证码,《用机器/深度学习过爬虫验证码》,opencv,playwright,python,滑动验证码,爬虫

6. 验证码背景图有了,滑块有了,滑块初始位置也有了,接下来就是判断背景图的缺口位置,再求出滑动距离。我们可以使用opencv-python的matchTemplate()和minMaxLoc()方法获取缺口的x坐标。拿到缺口x坐标后减去滑块的x坐标就可以求出滑动距离了。

为了让matchTemplate()的结果更加准确,我们可以对滑块图片做下处理,调整下对比度,让它暗一些,跟缺口差不多。

注:minMaxLoc()这个函数返回一个最大值和最小值,因为无法知道哪一个是正确的,所以我们两个值都应该拿来验证。也就是说,我们会拿到两个距离值,并且可能要滑动两次。当然,这个其实没啥关系,因为滑动验证码验证失败了的话,是可以再次滑动的。

7. 位置拿到之后,就是用鼠标控制滑轨上的按钮并进行滑动操作。滑动操作要真实,不能匀速,滑动时鼠标肯定也会有上下抖动,总之要尽量模拟人的滑动操作。

8. 滑动成功的话下方的滑轨元素就会消失,我们可以通过这点来判断是否通过了滑动验证码。如果滑了两次都没有通过(小概率),则刷新验证码并再次执行步骤4,5,6,7,8。

编写代码

根据以上思路,我们可以编写出如下代码。

from playwright.sync_api import sync_playwright
from PIL import Image
import numpy as np
import cv2 as cv
import requests
import random
import re


class QQZonSlide:
    def __init__(self):
        self.login_url = "https://i.qq.com/"
        self.username = "你的账号"
        self.password = "你的密码"
        self.page = None

    def start(self):
        with sync_playwright() as p:
            self.init_page(p)
            self.login()

            while True:
                self.get_slide_bg_img()
                start_x = self.get_slide_block_img_and_start_x()
                distance1, distance2 = self.get_slide_distance(start_x)
                slide_result = self.move_to_notch(distance1, distance2)
                if not slide_result:
                    self.refresh_captcha()
                else:
                    break

    def init_page(self, p):
        """初始化浏览器,获取page对象"""
        browser = p.chromium.launch(headless=False)
        self.page = browser.new_page()

    def login(self):
        """通过账号密码登录"""
        print("开始登录")

        # 访问页面
        self.page.goto(self.login_url)

        # 定位到登录框元素并点击密码登录
        login_frame = self.page.frame_locator("#login_frame")
        login_frame.get_by_role("link", name="密码登录").click()

        # 清空账号框然后输入账号
        login_frame.locator("#u").clear()
        login_frame.locator("#u").fill(self.username)

        # 清空密码框然后输入密码
        login_frame.locator("#p").clear()
        login_frame.locator("#p").fill(self.password)

        # 点击登录按钮
        self.page.wait_for_timeout(1000)
        login_frame.locator("#login_button").click()

    def get_slide_bg_img(self):
        """截取滑动验证码背景图片"""
        self.page.wait_for_timeout(2000)
        print("正在获取滑动验证码背景图片")

        # 获取滑动验证码所在的iframe
        captcha_iframe = self.page.frame_locator("#login_frame").frame_locator("#tcaptcha_iframe_dy")

        # 获取滑动验证码的背景图
        slide_bg_style = captcha_iframe.locator("#slideBg").get_attribute("style")
        slide_bg_url = re.search(r'url\("(.+)"\)', slide_bg_style).groups()[0]
        r = requests.get(slide_bg_url)
        with open("./slide_bg.png", "wb") as f:
            f.write(r.content)

        # 调整图片大小,根据style内容将宽度调整为280,高度等比例调整
        img = Image.open("./slide_bg.png")
        ratio = img.width / 280
        img = img.resize(size=(280, int(img.height/ratio)))
        img.save("./slide_bg.png")

    def get_slide_block_img_and_start_x(self):
        """获取滑块图片以及初始x坐标"""
        print("正在获取滑块图片")

        # 首先保存整个登录背景截图
        self.page.screenshot(path="bg.png")

        # 获取滑动验证码所在的iframe
        captcha_iframe = self.page.frame_locator("#login_frame").frame_locator("#tcaptcha_iframe_dy")

        # 获取滑块图片
        # .tc-fg-item对应的有三个元素,一个是目标滑块,一个是滑轨,还有一个是滑轨上的按钮
        for i in range(3):
            slide_block_ele = captcha_iframe.locator(".tc-fg-item").nth(i)
            slide_block_style = slide_block_ele.get_attribute("style")

            # 滑轨按钮元素的style值中不包含url字符串
            if "url" not in slide_block_style:
                continue

            # 从元素的style值中分析得出只有目标滑块的top值小于150
            top_value = re.search(r'top: (.+)px;', slide_block_style).groups()[0]
            if float(top_value) > 150:
                continue

            # 获取x坐标
            slide_block_x = float(re.search(r'left: (.+)px; top: ', slide_block_style).groups()[0])

            # 通过滑块位置,从背景图中截取滑块图片
            slide_block_rect = slide_block_ele.bounding_box()
            bg = Image.open("./bg.png")
            offset = slide_block_rect["width"] // 4  # 从背景图上截取会混入滑块周围的一些像素点,所以加一个偏移值,截取到滑块内部的图片。
            slide_block_img = bg.crop((slide_block_rect["x"] + offset, slide_block_rect["y"] + offset,
                                       slide_block_rect["x"] + slide_block_rect["width"] - offset,
                                       slide_block_rect["y"] + slide_block_rect["height"] - offset))
            slide_block_img.save("slide_block.png")

            return slide_block_x + slide_block_rect["width"] // 4

    @staticmethod
    def set_contrast_brightness(frame, contrast_value, brightness_value):
        if not contrast_value:
            contrast_value = 0.0

        if not brightness_value:
            brightness_value = 0

        blank = np.zeros(frame.shape, frame.dtype)
        frame = cv.addWeighted(frame, contrast_value, blank, 1 - contrast_value, brightness_value)

        return frame

    def get_slide_distance(self, start_x):
        """获取滑动距离"""
        print("正在获取滑动距离")
        # 通过opencv比较图片,获取缺口位置
        slide_bg_img = cv.imread("./slide_bg.png")
        slide_block_img = cv.imread("./slide_block.png")
        slide_block_img = self.set_contrast_brightness(slide_block_img, 0.4, 0)
        result = cv.matchTemplate(slide_block_img, slide_bg_img, cv.TM_CCOEFF_NORMED)
        minVal, maxVal, minLoc, maxLoc = cv.minMaxLoc(result)

        # 缺口的x坐标
        notch_x1 = minLoc[0]
        notch_x2 = maxLoc[0]

        # 距离
        distance1 = notch_x1 - start_x
        distance2 = notch_x2 - start_x
        return distance1, distance2

    @staticmethod
    def get_tracks(distance):
        """获取移动轨迹"""
        tracks = []                             # 移动轨迹
        current = 0                             # 当前位移
        mid = distance * 4 / 5                  # 减速阈值
        t = 0.2                                 # 计算间隔
        v = 0                                   # 初始速度

        while current < distance:
            if current < mid:
                a = random.randint(3, 5)        # 加速度为正5
            else:
                a = random.randint(-5, -3)      # 加速度为负3

            v0 = v                              # 初速度 v0
            v = v0 + a * t                      # 当前速度
            move = v0 * t + 1 / 2 * a * t * t   # 移动距离
            current += move
            tracks.append(round(current))

        return tracks

    def move_to_notch(self, distance1, distance2):
        """移动滑轨按钮到缺口处"""
        # 获取滑动验证码所在的iframe
        captcha_iframe = self.page.frame_locator("#login_frame").frame_locator("#tcaptcha_iframe_dy")

        for i in range(2):
            # 获取按钮位置,将鼠标移到上方并按下
            slider_btn_rect = captcha_iframe.get_by_alt_text("slider").bounding_box()
            self.page.mouse.move(slider_btn_rect['x'], slider_btn_rect['y'])
            self.page.mouse.down()

            distance = [distance1, distance2][i]
            if distance <= 0:   # 距离不可能小于等于0
                continue

            print(f"正在进行第{i+1}次滑动")
            tracks = self.get_tracks(distance)
            for x in tracks:
                self.page.mouse.move(slider_btn_rect['x']+x, random.randint(-5, 5)+slider_btn_rect['y'])
            self.page.mouse.move(slider_btn_rect['x'] + tracks[-1] + 5, random.randint(-5, 5) + slider_btn_rect['y'])
            self.page.mouse.move(slider_btn_rect['x'] + tracks[-1] - 5, random.randint(-5, 5) + slider_btn_rect['y'])
            self.page.mouse.up()

            # 滑动结束后等待一段时间
            self.page.wait_for_timeout(2000)

            # 寻找按钮是否还存在,不存在的话表明已通过滑动验证码,存在的话尝试下一个距离
            try:
                captcha_iframe.get_by_alt_text("slider").wait_for(timeout=2000)
            except Exception as e:
                print("已通过滑动验证码")
                return True
            else:
                print(f"第{i+1}次滑动失败")

        return False

    def refresh_captcha(self):
        """刷新验证码"""
        # 获取滑动验证码所在的iframe
        print("刷新验证码")
        captcha_iframe = self.page.frame_locator("#login_frame").frame_locator("#tcaptcha_iframe_dy")
        captcha_iframe.locator("#e_reload").click()
        self.page.wait_for_timeout(2000)


if __name__ == "__main__":
    slide = QQZonSlide()
    slide.start()

运行视频如下:

用opencv-python+playwright过滑动验证

从视频中可以看出,程序一共滑动了两次,在第二次的时候成功了。不过QQ空间又弹出了点选验证码(有时候不会弹出),我们会在之后章节中讲解如何通过这种类型验证码。

总结与提高

有时候就算通过了滑动验证码,QQ空间也会提示当前网络异常或者不安全,导致这种情况出现的原因很可能是轨迹出了问题,我们可以使用更好的算法生成更真实的滑动轨迹来避免这种情况出现。文章来源地址https://www.toymoban.com/news/detail-597668.html

到了这里,关于用opencv+playwright过滑动验证码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Python如何用爬虫破解滑动验证码?这五步简单易用

    在使用python爬虫抓取数据的时候经常会遇到网站为了防止机器人登录而设置了验证,除了最基本的字符验证之外还有滑块验证、图片验证、顺序验证等等验证方式。那我们今天就来讲一下python如何用爬虫破解滑动验证码,一起来看看吧。 1.读取图片:在命令行控制台输入pip

    2024年02月14日
    浏览(41)
  • 竞赛保研 python 机器视觉 车牌识别 - opencv 深度学习 机器学习

    🔥 优质竞赛项目系列,今天要分享的是 🚩 基于python 机器视觉 的车牌识别系统 🥇学长这里给一个题目综合评分(每项满分5分) 难度系数:3分 工作量:3分 创新点:3分 🧿 更多资料, 项目分享: https://gitee.com/dancheng-senior/postgraduate 车牌识别其实是个经典的机器视觉任务了,

    2024年02月04日
    浏览(41)
  • 【毕业设计】python 机器视觉 车牌识别 - opencv 深度学习 机器学习

    🚩 基于python 机器视觉 的车牌识别系统 🥇学长这里给一个题目综合评分(每项满分5分) 难度系数:3分 工作量:3分 创新点:2分 🧿 选题指导, 项目分享: https://gitee.com/dancheng-senior/project-sharing-1/blob/master/%E6%AF%95%E8%AE%BE%E6%8C%87%E5%AF%BC/README.md 车牌识别其实是个经典的机器视觉任

    2024年02月06日
    浏览(69)
  • 挑战杯 python 机器视觉 车牌识别 - opencv 深度学习 机器学习

    🔥 优质竞赛项目系列,今天要分享的是 🚩 基于python 机器视觉 的车牌识别系统 🥇学长这里给一个题目综合评分(每项满分5分) 难度系数:3分 工作量:3分 创新点:3分 🧿 更多资料, 项目分享: https://gitee.com/dancheng-senior/postgraduate 车牌识别其实是个经典的机器视觉任务了,

    2024年02月21日
    浏览(39)
  • python 机器视觉 车牌识别 - opencv 深度学习 机器学习 计算机竞赛

    🔥 优质竞赛项目系列,今天要分享的是 🚩 基于python 机器视觉 的车牌识别系统 🥇学长这里给一个题目综合评分(每项满分5分) 难度系数:3分 工作量:3分 创新点:3分 🧿 更多资料, 项目分享: https://gitee.com/dancheng-senior/postgraduate 车牌识别其实是个经典的机器视觉任务了,

    2024年02月06日
    浏览(41)
  • 计算机竞赛 python 机器视觉 车牌识别 - opencv 深度学习 机器学习

    🔥 优质竞赛项目系列,今天要分享的是 🚩 基于python 机器视觉 的车牌识别系统 🥇学长这里给一个题目综合评分(每项满分5分) 难度系数:3分 工作量:3分 创新点:3分 🧿 更多资料, 项目分享: https://gitee.com/dancheng-senior/postgraduate 车牌识别其实是个经典的机器视觉任务了,

    2024年02月12日
    浏览(47)
  • 机器视觉毕业设计 python车牌识别系统 - opencv 深度学习 机器学习

    # 1 前言 🚩 基于python 机器视觉 的车牌识别系统 🥇学长这里给一个题目综合评分(每项满分5分) 难度系数:3分 工作量:3分 创新点:2分 车牌识别其实是个经典的机器视觉任务了,通过图像处理技术检测、定位、识别车牌上的字符,实现计算机对车牌的智能管理功能。如今在

    2024年02月13日
    浏览(37)
  • 【验证码系列】用逆向思维深度分析滑动验证码(含轨迹算法)

      验证码是机器人防护(即爬虫)常用重要手段之一!在爬虫这个领域内专精某一项( 验证码识别 、 JS或者APP逆向 )都是可以成为大牛存在的 传统的验证码我们都知道是静态图片的形式,它们包含一些阿拉伯数字跟字母加一些干扰( 现在而言的话难度系数极低 )。目前

    2024年02月06日
    浏览(33)
  • 数据集收集列表(opencv,机器学习,深度学习)持续更新

    opencv 车牌识别数据集 opencv 手写数字识别数据集 ,下载地址 ,下载

    2024年02月11日
    浏览(31)
  • 竞赛选题 机器视觉目标检测 - opencv 深度学习

    🔥 优质竞赛项目系列,今天要分享的是 🚩 机器视觉 opencv 深度学习目标检测 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐! 🥇学长这里给一个题目综合评分(每项满分5分) 难度系数:3分 工作量:3分 创新点:4分 🧿 更多资料, 项目分享: https://gitee.com/dancheng

    2024年02月07日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包