DrissionPage官网

验证码

普通文本验证码

img_bytes=page.ele('tag:img@src:image/png;base64').src()
ocr= ddddocr.DdddOcr()
yzm=ocr.classification(img_bytes)
page.ele('#loginCode').input(yzm)

滑块验证码

'''删除img文件夹'''
import os
import shutil

def delete_img_folder():
    folder_name = 'img'

    # 获取当前工作目录
    current_directory = os.getcwd()

    # 构造要删除的文件夹的完整路径
    folder_path = os.path.join(current_directory, folder_name)

    try:
        # 删除文件夹及其内容
        shutil.rmtree(folder_path)
        # print(f"成功删除文件夹: {folder_path}")
    except FileNotFoundError:
        # print(f"文件夹 '{folder_path}' 不存在")
        pass
    except Exception as e:
        print(f"发生错误: {e}")

import PIL
import cv2
import numpy as np
from pathlib import Path
def pil_to_cv2(img):
    """
    pil转cv2图片
    :param img: pil图像, <type 'PIL.JpegImagePlugin.JpegImageFile'>
    :return: cv2图像, <type 'numpy.ndarray'>
    """
    img = cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
    return img


def bytes_to_cv2(img):
    """
    二进制图片转cv2
    :param img: 二进制图片数据, <type 'bytes'>
    :return: cv2图像, <type 'numpy.ndarray'>
    """
    # 将图片字节码bytes, 转换成一维的numpy数组到缓存中
    img_buffer_np = np.frombuffer(img, dtype=np.uint8)
    # 从指定的内存缓存中读取一维numpy数据, 并把数据转换(解码)成图像矩阵格式
    img_np = cv2.imdecode(img_buffer_np, 1)
    return img_np


def cv2_open(img, flag=None):
    """
    统一输出图片格式为cv2图像, <type 'numpy.ndarray'>
    :param img: <type 'bytes'/'numpy.ndarray'/'str'/'Path'/'PIL.JpegImagePlugin.JpegImageFile'>
    :param flag: 颜色空间转换类型, default: None
        eg: cv2.COLOR_BGR2GRAY(灰度图)
    :return: cv2图像, <numpy.ndarray>
    """
    if isinstance(img, bytes):
        img = bytes_to_cv2(img)
    elif isinstance(img, (str, Path)):
        img = cv2.imread(str(img))
    elif isinstance(img, np.ndarray):
        img = img
    elif isinstance(img, PIL.Image):
        img = pil_to_cv2(img)
    else:
        raise ValueError(f'输入的图片类型无法解析: {type(img)}')
    if flag is not None:
        img = cv2.cvtColor(img, flag)
    return img


def get_distance(bg, tp, save_path=None):
    """
    :param bg: 背景图路径或Path对象或图片二进制
        eg: 'assets/bg.jpg'
            Path('assets/bg.jpg')
    :param tp: 缺口图路径或Path对象或图片二进制
        eg: 'assets/tp.jpg'
            Path('assets/tp.jpg')
    :param save_path: 保存路径, <type 'str'/'Path'>; default: None
    :return: 缺口位置
    """
    # 读取图片
    bg_gray = cv2_open(bg, flag=cv2.COLOR_BGR2GRAY)
    tp_gray = cv2_open(tp, flag=cv2.COLOR_BGR2GRAY)
    # 边缘检测
    tp_gray = cv2.Canny(tp_gray, 255, 255)
    bg_gray = cv2.Canny(bg_gray, 255, 255)
    # 目标匹配
    result = cv2.matchTemplate(bg_gray, tp_gray, cv2.TM_CCOEFF_NORMED)
    # 解析匹配结果
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
    distance = max_loc[0] + 8
    if save_path:
        # 需要绘制的方框高度和宽度
        tp_height, tp_width = tp_gray.shape[:2]
        # 矩形左上角点位置
        x, y = max_loc
        # 矩形右下角点位置
        _x, _y = x + tp_width, y + tp_height
        # 绘制矩形
        bg_img = cv2_open(bg)
        cv2.rectangle(bg_img, (x, y), (_x, _y), (0, 0, 255), 2)
        # 保存缺口识别结果到背景图
        if save_path:
            save_path = Path(save_path).resolve()
            save_path = save_path.parent / f"{save_path.stem}.{distance}{save_path.suffix}"
            save_path = save_path.__str__()
            cv2.imwrite(save_path, bg_img)
    return distance


'''使用ddddocr计算缺口距离'''
def get_distance_by_ddddocr():
    '''
    直接使用img文件夹下的target.png和background.png
    target.png:指的是小滑块图片
    background.png:带坑位的背景图
    '''
    det = ddddocr.DdddOcr(det=False, ocr=False)
    with open('./img/target.png', 'rb') as f:
        target_bytes = f.read()
    with open('./img/background.png', 'rb') as f:
        background_bytes = f.read()
    res = det.slide_match(target_bytes, background_bytes)
    x_distance = res["target"][0]
    return x_distance

'''据偏移量获取移动轨迹'''
import random
def get_tracks(distance):
    """

    :param distance: 缺口距离
    :return: 轨迹
    """
    # 分割加减速路径的阀值
    value = round(random.uniform(0.55, 0.75), 2)
    # 划过缺口 20 px
    #distance += 20
    # 初始速度,初始计算周期, 累计滑动总距
    v, t, sum = 0, 0.3, 0
    # 轨迹记录
    plus = []
    # 将滑动记录分段,一段加速度,一段减速度
    mid = distance * value
    while sum < distance:
        if sum < mid:
            # 指定范围随机产生一个加速度
            a = round(random.uniform(2.5, 3.5), 1)
        else:
            # 指定范围随机产生一个减速的加速度
            a = -round(random.uniform(2.0, 3.0), 1)
        s = v * t + 0.5 * a * (t ** 2)
        v = v + a * t
        sum += s
        plus.append(round(s))

    # end_s = sum - distance
    # plus.append(round(-end_s))

    # 手动制造回滑的轨迹累积20px
    # reduce = [-3, -3, -2, -2, -2, -2, -2, -1, -1, -1]
    reduce = [-6, -4, -6, -4]
    return {'plus': plus, 'reduce': reduce}

'''嵌入式滑块'''
page.get("https://castatic.fengkongcloud.cn/pr/v1.0.4/demo.html")
delete_img_folder()
demo = page.ele("@text()=返回demo列表",timeout=1)
if demo:
    demo.click()
page.ele("@text()=嵌入式(embed)").click()
page.ele('@name=account').input("test")
page.ele('@name=password').input("test")
page.ele('.shumei_captcha_loaded_img_fg').save(path="./img/",name='target.png')
page.ele('.shumei_captcha_loaded_img_bg').save(path="./img/",name='background.png')
# 在网址上计算的距离必须除2,可能是分辨率导致的

x_distance =  get_distance_by_ddddocr() / 2
trajectory = get_tracks(x_distance)
move_to_gap(slide_ele =".shumei_captcha_slide_btn_icon sm-iconfont",tracks = trajectory)
time.sleep(0.2)
gugu = page.ele('#shumei_form_captcha_wrapper')
if gugu:
    gugu.get_screenshot(path="./img/captcha.png")

定位

page.ele('x://*[@id="app"]/div/div[1]/div/div/div[2]/div[2]/div[2]/form/div[1]/div/div/div/div[1]/input').input(username)

注意

  1. 加载网页最好设置好超时时间,以免等待过久

co = ChromiumOptions(timeout=5)
  1. HTML文档通常是从上到下顺序加载和解析的。浏览器会从文档的顶部开始,逐行解析HTML内容,并在遇到各种资源(如CSS、JavaScript、图片等)时进行相应的处理。