爬虫技术-滑块验证码

滑块验证码

1. 滑块简介

image

注:重点是识别滑块缺口,测出需要拖动的距离

1.1 核心步骤

  1. 从服务器随机取一张图片,并对图片上的随机x,y坐标和宽高一块区域抠图;

  2. 根据步骤一的坐标和宽高,使用二维数组保存原图上抠图区域的像素点坐标;

  3. 根据步骤二的坐标点,对原图的抠图区域的颜色进行处理。

  4. 完成以上步骤之后得到两张图(扣下来的方块图,带有抠图区域阴影的原图),将这两张图和抠图区域的y坐标传到前台,前端在移动方块验证时,将移动后的x坐标传递到后台与原来的x坐标作比较,如果在阈值内则验证通过。

  5. 请求验证的步骤:前台向后台发起请求,后台随机一张图片做处理将处理完的两张图片的base64,抠图y坐标和token(token为后台缓存验证码的唯一token,可以用缓存和分布式缓存)返回给前台。

  6. 前台滑动图片将x坐标和token作为参数请求后台验证,服务器根据token取出x坐标与参数的x进行比较。

1.2 突破规则

这类验证码可以使用 selenium 操作浏览器拖拽滑块来进行破解,难点两个,一个如何确定拖拽到的位置,另一个是避开人机识别(反爬虫)。

首先我们先看看,确定滑块验证码需要拖拽的位移距离

有三种方式

  • 人工智能机器学习,确定滑块位置
  • 通过完整图片与缺失滑块的图片进行像素对比,确定滑块位置
  • 边缘检测算法,确定位置

各有优缺点。人工智能机器学习,确定滑块位置,需要进行训练比较麻烦,所以我们主要看后面两种。

对比完整图片与缺失滑块的图片

1.3 验证码图片处理

# 提取背景图
document.getElementsByClassName('geetest_canvas_bg geetest_absolute')[0].toDataURL('image/png')
document.getElementsByClassName('geetest_canvas_fullbg geetest_fade geetest_absolute')[0].toDataURL('image/png')

# 保存图片
import base64,io
images = q_qt.split(',')[1]
images = base64.b64decode(images)
images = Image.open(io.BytesIO(images))
images.save('q_qg.png')

1.4 背景滑块识别

  • 这里使用开源的技术ddddocr进行滑块的识别
import ddddocr
def text_dis():
    slide = ddddocr.DdddOcr(det=False, ocr=False)
    with open('bg.png', 'rb') as f:
        target_bytes = f.read()
    with open('zg.png', 'rb') as f:
        background_bytes = f.read()
    res = slide.slide_comparison(target_bytes, background_bytes)
    print(res)

2 OPENCV处理滑块

opencv 是一个跨平台计算机视觉和机器学习软件库,支持Linux、windows操作系统。

​ 一般来说,图像是一个标准的矩形,有着宽度(width)和高度(height)。而矩阵有着行(row)和列(column),矩阵的操作在数学和计算机中的处理都很常见且成熟,于是很自然的就把图像作为一个矩阵,把对图像的操作转换成对矩阵的操作,实际上所有的图像处理工具都是这么做的

2.1 cv库讲解

2.1.1 环境安装

pip install opencv-python

运行时报错:AttributeError: partially initialized module 'cv2' has no attribute '_registerMatType' (most likely due to a circular import) 解决

原因:版本太高

解决办法:

1、首先卸载:

pip uninstall opencv-python

pip uninstall opencv-contrib-python

2、只安装低版本的opencv-contrib-python

pip install opencv_python==3.4.10.37

使用whl包安装

链接:https://pan.baidu.com/s/1KZd6VX-YsQjwiPoIXF1G5g 
提取码:jlh1

2.2 CV使用

2.2.1 读取图片
  • 防止闪退 这个函数是在一个给定的时间内(单位ms)等待用户按键触发;如果用户没有按下 键,则接续等待(循环)
def read_img():
    img = cv.imread('op_test.jpg')
    print(img.shape)
    cv.imshow('image',img)

    cv.waitKey()
2.2.2 读取灰度
def read_img1():
    img = cv.imread('op_test.jpg')
    # 图像灰度化
    image = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    print(image.shape)
    cv.imshow('gray', image)
    cv.waitKey()  # 防止闪退
2.2.3 摄像头操作
def read_video():
    cap = cv.VideoCapture(0)  # 如果有一个摄像头就用0,大于一个用1或者其他数字
    cap.set(3, 640)  # 设置长度,序号是3
    cap.set(4, 480)  # 设置宽度,序号是4
    cap.set(10, 200)  # 设置亮度,序号是10q
    while True:
        success, img = cap.read()
        if success:
            cv.imshow("Video", img)
            if cv.waitKey(1) & 0xFF == ord('q'):
                break
        else:
            cv.destroyAllWindows()
            break

2.3 使用cv处理滑块

地址:https://passport.jd.com/new/login.aspx

2.3.1 处理code
from io import BytesIO
import requests
import cv2
from PIL import Image
import numpy as np

def get_dis():
    # 京东的滑块
    res = requests.get('https://iv.jd.com/slide/g.html?appId=1604ebb2287&scene=login&product=click-bind-suspend&e=PHMXTEAUGERCMBUAY2PXCENHW4KCWFS7AGHE3JYXMA4XXOOS5Z27CTBDBGC7IDQSAPSN5MT7XMVC4R5SYEPW5TJYQI&lang=zh_CN')
    patch = res.json().get('patch')
    bg = res.json().get('bg')
    import base64
    bg1 = base64.b64decode(bg)
    patch1 = base64.b64decode(patch)
    open('jd-bg1.png','wb').write(bg1)
    # 读取二进制图片
    image = np.array(Image.open(BytesIO(bg1)))
    gap = np.array(Image.open(BytesIO(patch1)))
    # 转换颜色通道,这里为什么要BGR转为RGB,因为np.array转换pillow的图像的结果就是BGR格式的
    image1 = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    gap1 = cv2.cvtColor(gap, cv2.COLOR_BGR2RGB)
    # 转为灰度图
    image_gray = cv2.cvtColor(image1.copy(), cv2.COLOR_BGR2GRAY)
    gap_gray = cv2.cvtColor(gap1.copy(), cv2.COLOR_BGR2GRAY)

    # 模板匹配
    result = cv2.matchTemplate(image_gray, gap_gray, cv2.TM_CCOEFF_NORMED)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
    print(min_val, max_val, min_loc, max_loc)
    # 获取最大相关的x,y
    x, y = min_loc
    print(x, y)

3. 极验滑块实战

import base64
import time
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from urllib import request
import cv2,random

import ddddocr
def text_dis(bg,fg):
    slide = ddddocr.DdddOcr(det=False, ocr=False)
    with open(bg, 'rb') as f:
        target_bytes = f.read()
    with open(fg, 'rb') as f:
        background_bytes = f.read()
    res = slide.slide_comparison(target_bytes, background_bytes)
    return res.get('target')[0]

def get_slide():
    options = webdriver.ChromeOptions()
    # 对于老版本的浏览器不行
    options.add_argument('--disable-blink-features=AutomationControlled')
    driver = webdriver.Chrome(chrome_options=options)
    driver.maximize_window()
    driver.get('https://www.geetest.com/demo/slide-bind.html')
    # 输入框输入账号和密码
    driver.find_element(By.ID,'username').send_keys('13535353535')
    driver.find_element(By.ID,'password').send_keys('123123123')
    # driver.find_element(By.ID,'btn').click()
    time.sleep(2)
    driver.find_element(By.CSS_SELECTOR,'div.btn').click()
    time.sleep(2)
    img_src = driver.execute_script(
        'return document.getElementsByClassName("geetest_canvas_bg geetest_absolute")[0].toDataURL("image/png");')
    im_base64 = img_src.split(',')[1]
    im_bytes = base64.b64decode(im_base64)
    with open('./bg.png', 'wb') as f:
        f.write(im_bytes)
    temp = driver.execute_script(
        "return document.getElementsByClassName('geetest_canvas_fullbg geetest_fade geetest_absolute')[0].toDataURL('image/png');")
    temp_base64 = temp.split(',')[1]
    temp_bytes = base64.b64decode(temp_base64)
    with open('./temp.png', 'wb') as f:
        f.write(temp_bytes)

    distance = text_dis('bg.png', 'temp.png')
    # 拖动滑块
    slide = driver.find_element(By.CSS_SELECTOR, 'div.geetest_slider_button')
    action_chains = webdriver.ActionChains(driver)
    # 点击,准备拖拽
    action_chains.click_and_hold(slide)
    action_chains.pause(0.2)
    action_chains.move_by_offset(distance - 10, 0)
    action_chains.pause(0.8)
    action_chains.move_by_offset(10, 0)
    action_chains.pause(1.4)
    action_chains.move_by_offset(-10, 0)
    action_chains.release()
    action_chains.perform()
    time.sleep(20)

get_slide()


'''
2、鼠标操作
click --- 鼠标左键点击(可以指定或不指定元素对象)
click_and_hold --- 鼠标左键点击但不释放(可以指定或不指定元素对象)
release --- 释放鼠标点击动作(可以指定或不指定在目标元素对象上释放)
context_click --- 鼠标右键点击(可以指定或不指定元素对象)
double_click --- 鼠标左键双击(可以指定或不指定元素对象)
drag_and_drop --- 鼠标左键在两个元素之间拖拽
drag_and_drop_by_offset --- 鼠标左键拖拽元素到目标偏移位置
move_by_offset --- 鼠标移动指定偏移
move_to_element --- 鼠标移动到指定元素
move_to_element_with_offset --- 鼠标移动到指定元素的指定偏移位置

1、行为控制
perform --- 执行所有准备好的Action
reset_actions --- 清空所有准备好的Action  #  该方法在 selenium 3.141.0版本不生效
pause --- 设置Action之间的动作时间间隔

'''
posted @ 2022-09-09 17:55  尘世风  阅读(4098)  评论(2编辑  收藏  举报
*/