【基础】【Python网络爬虫】【10.验证码处理】OCR识别,Tesseract ,ddddocn识别,打码平台,滑块验证码(附大量案例代码)(建议收藏)

Python网络爬虫基础

  • 验证码处理
  • 一. OCR识别
    • 1. Tesseract 引擎的安装
      • windows引擎环境安装
      • Mac系统引擎环境安装
        • 安装 tesseract
        • 查看 tesseract 版本
        • 安装过程遇到的报错解决方法
        • 下载中文包
        • 中文包存放目录
        • 查看全部语言库
        • python 安装 pytesseract 和 pillow
        • 识别图片中文字体
      • Linux系统引擎环境安装
    • 2. Tesseract 中文语言模型配置
      • Windows配置中文模型
      • linux配置中文模型
    • 3. Tesserract识别
      • 识别英文
      • 识别中文
      • 识别验证码
  • 二. ddddocn 识别
    • 1. 图片的形式和转化
      • 图片在网页页面中的形式
      • 如何进行图片形式的转化
    • 2. 字符串图片转化
    • 3. ddddcon识别
    • 4. 青灯论坛模拟登录对接
    • 5. ddddcon中文识别
  • 三、打码平台识别
        • 案例 - (古诗文)验证码图片识别
    • 1. 快识别打码平台测试
    • 2. 凤凰网登录
    • 3. B站点选验证码
  • 四、滑块验证码
    • 1. 简单滑块
    • 2. 极验验证
      • 分段类型
        • 获取验证码图片
          • 修改网页前端属性显示全部图片
          • 下载验证码
          • 还原验证码图片
        • 获取验证码缺口距离
        • 动验证码模拟滑动
        • 练习案例
      • canvas类型处理
        • 获取验证码图片
        • 获取验证码缺口距离
        • 滑动验证码模拟滑动
        • 练习案例

验证码处理

一. OCR识别

OCR(Optical Character Recognition)是指使用扫描仪或数码相机对文本资料进行扫描成图像文件,然后对图像文件进行分析处理,自动识别获取文字信息及版面信息的软件

在读取和处理图像、图像相关的机器学习以及创建图像等任务中,Python 一直都是非常出色的语言。虽然有很多库可以进行图像处理,但在这里只重点介绍:Tesseract

1. Tesseract 引擎的安装

windows引擎环境安装

安装包下载链接:
https://wwae.lanzoub.com/b04k0prcj
密码:26yo

  • 下载引擎安装包,直接安装。安装方式:–>直接双击安装包 --> 选择安装路径 --> 后续所有选项点 “下一步” 安装。
  • 配置环境变量,安装完后如果要在计算机正常使用需要在计算机中配置环境变量,步骤如下所示:
    • 右键点击此电脑, 选择属性
      在这里插入图片描述
    • 选择点击**高级系统设置
      在这里插入图片描述
    • 选择点击**环境变量
      在这里插入图片描述
    • 系统变量中双击Path
      在这里插入图片描述
    • Tesseract引擎安装的根路径添加到环境变量中去
      在这里插入图片描述
    • 依次点击确定使环境变量生效
      在这里插入图片描述
    • 打开cmd验证,输入**tesseract -v **验证,出现如下说明你配置好了
      在这里插入图片描述

Mac系统引擎环境安装

安装 tesseract
brew install tesseract

==> Installing dependencies for tesseract: libarchive
==> Installing tesseract dependency: libarchive
==> Pouring libarchive-3.6.1.catalina.bottle.tar.gz
🍺  /usr/local/Cellar/libarchive/3.6.1: 62 files, 3.6MB
==> Installing tesseract
==> Pouring tesseract--5.1.0.catalina.bottle.tar.gz
==> Caveats
This formula contains only the "eng", "osd", and "snum" language data files.
If you need any other supported languages, run `brew install tesseract-lang`.
==> Summary
🍺  /usr/local/Cellar/tesseract/5.1.0: 58 files, 30.0MB
==> Caveats
==> tesseract
This formula contains only the "eng", "osd", and "snum" language data files.
If you need any other supported languages, run `brew install tesseract-lang`.
查看 tesseract 版本

成功安装后查看 tesseract 版本

tesseract --version
tesseract 5.1.0
 leptonica-1.82.0
  libgif 5.2.1 : libjpeg 9e : libpng 1.6.37 : libtiff 4.3.0 : zlib 1.2.11 : libwebp 1.2.2 : libopenjp2 2.4.0
 Found AVX2
 Found AVX
 Found FMA
 Found SSE4.1
 Found libarchive 3.6.1 zlib/1.2.11 liblzma/5.2.5 bz2lib/1.0.6 liblz4/1.9.3 libzstd/1.5.2
 Found libcurl/7.64.1 SecureTransport (LibreSSL/2.8.3) zlib/1.2.11 nghttp2/1.39.2
安装过程遇到的报错解决方法

错误一:

  • 安装tesseract的过程中报缺少依赖的错误
  • Error: No such file or directory @ rb_sysopen - /Users/f/Library/Caches/Homebrew/downloads/266702d9bc59c9dfde27ce555b4a3f9ed9d0de770ba697e62a111d74ee0a4231–openjpeg-2.4.0.catalina.bottle.tar.gz
  • 针对这类错误单独安装缺少的包即可
  • brew install openjpeg

错误二:

  • 单独安装依赖出现如下提示:
  • Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP. Hide these hints with HOMEBREW_NO_ENV_HINTS (see man brew).
  • 执行如下命令即可: export HOMEBREW_NO_INSTALL_CLEANUP=TRUE
下载中文包
  • tesseract默认不支持中文,需要单独下载中文包
  • 中文包下载地址: https://tesseract-ocr.github.io/tessdoc/Data-Files

在这里插入图片描述

中文包存放目录

/usr/local/Cellar/tesseract/{tesseract版本}/share/tessdata

cd /usr/local/Cellar/tesseract/5.1.0/share/tessdata

在这里插入图片描述

查看全部语言库
tesseract --list-langs
List of available languages in "/usr/local/share/tessdata/" (4):
chi_sim
eng
osd
snum
python 安装 pytesseract 和 pillow
pip install pytesseract
pip install pillow
识别图片中文字体
import pytesseract
from PIL import Image
# 读取图片  # 打开对应图片的文件路径
im = Image.open('/Users/f/PycharmProjects/firstProject/a/a.png')
# 识别文字,并指定语言
string = pytesseract.image_to_string(im, lang='chi_sim')
print(string)

Linux系统引擎环境安装

可以通过指令在线安装

sudo apt-get update
sudo apt-get install tesseract-ocr

2. Tesseract 中文语言模型配置

Tesseract各个国家语言地区文字模型在GitHub可以自行下载, 以下是下载链接:

  • 中文语言模型下载网址:https://github.com/tesseract-ocr/tessdata

在这里插入图片描述

Windows配置中文模型

中文语言模型配置主要是要将模型文件放到引擎的安装目录下

将中文模型放到引擎的 安装目录/tessdata 的目录下
在这里插入图片描述

linux配置中文模型

Linux 系统下通过apt get指令安装的工具默认是在~/etc下,进入到tesseract安装路径下share下面的tessdata文件夹下,将中文语言包拷贝进去即可

3. Tesserract识别

识别英文

from PIL import Image  # pillow模块  安装名和导入名不一样
import pytesseract

# 当找不到识别引擎的情况下, 加一下代码
# pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'

# 打开加载英文内容图片
image = Image.open('test_english.png')

# 识别图片中的文字, 默认语言模型是英文模型
result = pytesseract.image_to_string(image)

# 打印识别结果
print(result)

"""
默认情况下只要是计算机敲出来的文字, 用这个方法识别率是 100%
"""

识别中文

''' 首先要中文语言模型配置 '''
from PIL import Image  # pillow模块  安装名和导入名不一样
import pytesseract

# 打开加载图片
image = Image.open('test_chinese.png')

# 识别图片中的文字
#  lang="chi_sim" 指定中文语言模型进行识别
result = pytesseract.image_to_string(image, lang='chi_sim')

# 打印识别结果
print(result)

识别验证码

from PIL import Image  # pillow模块  安装名和导入名不一样
import pytesseract

# 打开加载图片
image = Image.open('yzm1.png')

# 识别图片中的文字
#  lang="chi_sim" 指定中文语言模型进行识别
result = pytesseract.image_to_string(image)

# 打印识别结果
print(result)

"""
因为我们没有验证码模型, 所以识别验证码图片会识别率会非常低
"""

二. ddddocn 识别

1. 图片的形式和转化

图片在网页页面中的形式

图片在网页页面中的形式一般就两种:一种是以一个链接形式存在html中,另外一种是以字符串的形式存在于html中。

以链接形式存在于html中的图片,意味着每次浏览器执行渲染的时候会发送图片链接地址请求,请求到了之后再渲染到页面。目前大部分网站都是以这种形式去加载图片的。之前我们也学习过通过获取图片的链接地址就可以请求图片数据。
在这里插入图片描述
这样的形式存在于页面当中的图片,如果一旦页面图片很多的话,就意味着需要发送很多次网络请求,去请求图片数据。

以字符串形式存在于html中的图片,和上述形式有明显的区别。这样形式的图片在html页面中是以字符串的形式去展示图片,而不是一个链接。
在这里插入图片描述

  • 这样的好处是加载页面的时候,不用发送网络请求去请求图片数据。因为我们在请求html页面的时候就已经拿到图片的字符串数据,浏览器只需要转换图片为二进制形式展示就可以了。
  • 有利也有弊,这样做虽然网络请求的次数少了,但是我们需要把图片转换成字符串形式才可以放到标签里面。如果图片数据过大,那么转换之后的字符长度就会很长。所以一般网页中字符串形式的图片都是数据量比较小的图片,比如验证码。

如何进行图片形式的转化

前面我们讲了图片有两种形式,链接形式的图片我们通过requests发送请求就可以拿到图片的二进制数据,保存下来就可以用看图软件查看。这种形式的图片处理就不做赘述了。

  • 那么字符串的形式的图片我们应该如何处理呢?
  • 字符串形式的图片我们可以借助 base64 模块进行处理。
# base64.b64decode()

  把字符串形式的图片转化为二进制的数据, 传入图片的字符串数据

# base64.b64encode()

  把二进制形式的图片转化为字符串数据, 传入图片的二进制数据

2. 字符串图片转化

import base64  # 内置模块

"""把字符串形式的图片转化成二进制"""
img_str = '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'

# b64decode() 把字符串形式的图片转化成二进制
bytes_img = base64.b64decode(img_str)
print(bytes_img)

with open('yzm.png', mode='wb') as f:
    f.write(bytes_img)

""" 把二进制形式的图片转换成字符串 """
with open('yzm.png', mode='rb') as f:
    img_bytes_data = f.read()
    print('读取出的二进制形式的图片: ', img_bytes_data)

# b64encode() 把二进制形式的图片成字符串
str_img = base64.b64encode(img_bytes_data).decode()
print('转换之后的字符串图片: ', str_img)

3. ddddcon识别

import ddddocr

"""创建识别对象"""
ocr = ddddocr.DdddOcr(beta=True)  # beta=True 自动识别模型

"""打开图片数据"""
with open('yzm.png', mode='rb') as f:
    image = f.read()

"""识别验证码"""
result = ocr.classification(image)
print(result)

"""
只要验证码没有覆盖, 识别率还行
"""
# 数字验证码
ocr = ddddocr.DdddOcr(beta=True)
with open('test.png', mode='rb') as f:
    image = f.read()
result2 = ocr.classification(image)
print(result2)

# 英文验证码
ocr = ddddocr.DdddOcr(beta=True)
with open('test2.png', mode='rb') as f:
    image = f.read()
result2 = ocr.classification(image)
print(result2)

# 英数混合验证码
ocr = ddddocr.DdddOcr(beta=True)
with open('test3.png', mode='rb') as f:
    image = f.read()
result3 = ocr.classification(image)
print(result3)

# 验证码覆盖识别测试
ocr = ddddocr.DdddOcr(beta=True)
with open('验证码4.png', mode='rb') as f:
    image = f.read()
result4 = ocr.classification(image)
print(result4)

4. 青灯论坛模拟登录对接

"""
时间戳: 格林威治时间1970年1月1日0时0分0秒开始 到 目前 位置所消耗的时间数
    秒级时间戳: 10为数字
    毫秒级时间戳: 13为数字
    微秒级时间戳: 16为数字
"""
import time

import ddddocr
import requests


def get_time():
    """获取时间戳的函数"""
    now_time = str(int(time.time() * 1000))
    print('当前时间戳为:', now_time)
    return now_time


cookies = {'seesion': 'vnrasebgvi'}

# 创建一个会话位置对象
session = requests.Session()

"""请求验证码, 保存"""
img_time = get_time()
img_url = 'http://118.126.88.143:5000/login/captcha?image_code=' + img_time
print('图片地址:', img_url)

# 使用回话维持对象发送请求
img_response = session.get(url=img_url, cookies=cookies).content
with open('yzm.png', mode='wb') as f:
    f.write(img_response)

# # 手动识别验证码
# img_code = input('请输入验证码:')
# print('您输入的验证码为:', img_code)
"""ddddocr识别"""
ocr = ddddocr.DdddOcr(beta=True)
with open('yzm.png', mode='rb') as f:
    image = f.read()
img_code = ocr.classification(image)
print(img_code)

"""构建登录请求"""
login_url = 'http://118.126.88.143:5000/api/private/v1/login'
json_data = {
    "image_code": img_time,
    "username": "admin",
    "password": "123456",
    "captcha_code": img_code  # 手动验证码
}

# 使用回话维持对象维持用户的登录状态
login_response = session.post(url=login_url, json=json_data)
print(login_response.cookies.get_dict())
print(login_response.json())

# 其他网站构建请求联系, 一般是通过cookies字段

5. ddddcon中文识别

import time

import ddddocr
import requests

"""ddddocr识别"""
ocr = ddddocr.DdddOcr(beta=True)
with open('test_chinese.png', mode='rb') as f:
    image = f.read()
img_code = ocr.classification(image)
print(img_code)

# 后续可以识别字体图片

三、打码平台识别

  • 图鉴平台:http://www.ttshitu.com/ (推荐)
  • 使用图鉴识别古诗文网登录中的验证码
'''
使用流程:
- 注册登录图鉴平台
- 登录后,点击开发文档,提取识别的源代码
- 模块(tujian.py)的封装:
'''
import base64
import json
import requests
# 一、图片文字类型(默认 3 数英混合):
# 1 : 纯数字
# 1001:纯数字2
# 2 : 纯英文
# 1002:纯英文2
# 3 : 数英混合
# 1003:数英混合2
#  4 : 闪动GIF
# 7 : 无感学习(独家)
# 11 : 计算题
# 1005:  快速计算题
# 16 : 汉字
# 32 : 通用文字识别(证件、单据)
# 66:  问答题
# 49 :recaptcha图片识别
# 二、图片旋转角度类型:
# 29 :  旋转类型
#
# 三、图片坐标点选类型:
# 19 :  1个坐标
# 20 :  3个坐标
# 21 :  3 ~ 5个坐标
# 22 :  5 ~ 8个坐标
# 27 :  1 ~ 4个坐标
# 48 : 轨迹类型
#
# 四、缺口识别
# 18 : 缺口识别(需要2张图 一张目标图一张缺口图)
# 33 : 单缺口识别(返回X轴坐标 只需要1张图)
# 五、拼图识别
# 53:拼图识别
# 函数实现忽略
def base64_api(uname, pwd, img, typeid):
    with open(img, 'rb') as f:
        base64_data = base64.b64encode(f.read())
        b64 = base64_data.decode()
    data = {"username": uname, "password": pwd, "typeid": typeid, "image": b64}
    result = json.loads(requests.post("http://api.ttshitu.com/predict", json=data).text)
    if result['success']:
        return result["data"]["result"]
    else:
        return result["message"]
    return ""


def getImgCodeText(imgPath, imgType):  # 直接返回验证码内容
    # imgPath:验证码图片地址
    # imgType:验证码图片类型
    result = base64_api(uname='bb328410948', pwd='bb328410948', img=imgPath, typeid=imgType)
    return result
案例 - (古诗文)验证码图片识别
from lxml import etree
import requests
import tujian

headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'
}
# 将验证码图片请求后保存到本地
login_url = 'https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx'
page_text = requests.get(url=login_url, headers=headers).text
tree = etree.HTML(page_text)
img_src = 'https://so.gushiwen.cn' + tree.xpath('//*[@id="imgCode"]/@src')[0]
code_data = requests.get(url=img_src, headers=headers).content
with open('./code.jpg', 'wb') as fp:
    fp.write(code_data)

# 识别验证码图片内容
result = tujian.getImgCodeText('./code.jpg', 3)

print(result)

在抓包工具里定位点击登录按钮后对应的数据包:

  • 只要数据包的请求参数中包含用户名,密码和验证码则该数据包就是我们要定位的
  • 首次模拟登录操作:
from lxml import etree
import requests
import tujian

headers = {
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'
}
# 将验证码图片请求后保存到本地
login_url = 'https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx'
page_text = requests.get(url=login_url, headers=headers).text
tree = etree.HTML(page_text)
img_src = 'https://so.gushiwen.cn' + tree.xpath('//*[@id="imgCode"]/@src')[0]
code_data = requests.get(url=img_src, headers=headers).content
with open('./code.jpg', 'wb') as fp:
    fp.write(code_data)

# 识别验证码图片内容
result = tujian.getImgCodeText('./code.jpg', 3)
print(result)
# 模拟登录
url = 'https://so.gushiwen.cn/user/login.aspx?from=http%3a%2f%2fso.gushiwen.cn%2fuser%2fcollect.aspx'
data = {
    "__VIEWSTATE": "opfVI7oolwkr7MLRVzsNSMASqLRUuO1dg5ZP5EIRa4FyM+mOYKEs6KWEKQKaba2ulLoZQIaLFiKK4mr5K3ci1v8ua28wtcRtabKWjOtJtU/i2etH+zSduegTMcg=",
    "__VIEWSTATEGENERATOR": "C93BE1AE",
    "from": "http://so.gushiwen.cn/user/collect.aspx",
    "email": "15027900535",
    "pwd": "bobo@15027900535",
    "code": result,
    "denglu": "登录"
}
# 获取了登录成功后的页面源码数据
login_page_text = requests.post(url=url, headers=headers, data=data).text
with open('wushiwen.html', 'w') as fp:
    fp.write(login_page_text)

1. 快识别打码平台测试

import base64
import json
import requests
from constants import KUAI_USERNAME, KUAI_PASSWORD

def base64_api(uname, pwd, img, typeid):
    """
    识别验证码的函数
    :param uname: 用户名
    :param pwd: 密码
    :param img: 图片路径
    :param typeid: 识别类型
    :return:
    """
    with open(img, 'rb') as f:
        # 打开图片图片, 把图片转换成字符串形式
        base64_data = base64.b64encode(f.read())
        b64 = base64_data.decode()

    data = {"username": uname, "password": pwd, "typeid": typeid, "image": b64}

    result = requests.post("http://api.ttshitu.com/predict", json=data).json()
    print('识别返回结果:', result)

    if result['success']:
        return result["data"]["result"]
    else:
        # !!!!!!!注意:返回 人工不足等 错误情况 请加逻辑处理防止脚本卡死 继续重新 识别
        return result["message"]


if __name__ == "__main__":
    img_path = "验证码4.png"
    result = base64_api(uname=KUAI_USERNAME, pwd=KUAI_PASSWORD, img=img_path, typeid=7)
    print(result)

    # 当我们识别错误可以, 可以换识别类型

2. 凤凰网登录

import base64
import time

from selenium import webdriver
from selenium.webdriver.common.by import By
from constants import FENG_USERNAME, FENG_PASSWORD

driver = webdriver.Chrome()
driver.get('https://www.ifeng.com/')
driver.implicitly_wait(10)
driver.maximize_window()

"""找到账号登录点击"""
driver.find_element(By.CSS_SELECTOR, '.login_in_2x-3NxtSKIw').click()
time.sleep(2)

"""注意一定要进入嵌套网页"""
iframe_label = driver.find_element(By.CSS_SELECTOR, '.box-1pZSPyeN>div:nth-child(2)>iframe')
driver.switch_to.frame(iframe_label)

"""点击账号登录"""
driver.find_element(By.CSS_SELECTOR, '.index_tab_FDzng>span:nth-child(1)').click()
time.sleep(1)

"""找用户名和密码框, 输入数据"""
driver.find_element(By.NAME, 'text').send_keys(FENG_USERNAME)
time.sleep(0.5)

driver.find_element(By.NAME, 'password').send_keys(FENG_PASSWORD)
time.sleep(0.5)

"""获取验证码"""
img_label = driver.find_element(By.CSS_SELECTOR, '.index_codeImg_6TcnD>img')
img_str = img_label.get_attribute('src')
print('全部的图片标签数据:', img_str)
base64_str = img_str.split(',')[-1]
print('base64字符串形式的图片:', base64_str)

# 把字符串形式的图片转换成二进制
bytes_img = base64.b64decode(base64_str)
print(bytes_img)

with open('yzm.png', mode='wb') as f:
    f.write(bytes_img)
    print('验证码保存完毕')

"""调用打码平台识别验证码"""
from img_api import base64_api

code_result = base64_api('yzm.png', 7)
print('验证码识别结果:', code_result)

"""输入验证码"""
driver.find_element(By.CSS_SELECTOR, '.index_input_Lm1EX input').send_keys(code_result)
time.sleep(2)

# 点击登录
driver.find_element(By.CSS_SELECTOR, '.index_submmitBtn_Xd39V').click()
time.sleep(2)

input()
driver.quit()

3. B站点选验证码

import base64
import time

from selenium import webdriver
from selenium.webdriver.common.by import By
from constants import BILIBILI_USERNAME, BILIBILI_PASSWORD
from selenium.webdriver import ActionChains

driver = webdriver.Chrome()
driver.get('https://passport.bilibili.com/login')
driver.implicitly_wait(10)
driver.maximize_window()

"""找用户名和密码框, 输入数据"""
driver.find_element(By.XPATH, '//input[@placeholder="请输入账号"]').send_keys(BILIBILI_USERNAME)
time.sleep(2)
driver.find_element(By.XPATH, '//input[@placeholder="请输入密码"]').send_keys(BILIBILI_PASSWORD)
time.sleep(2)

"""点击登录按钮"""
driver.find_element(By.CSS_SELECTOR, '.btn_wp>div:nth-child(2)').click()
time.sleep(2)

"""使用selenium标签对象保存图片"""
img_label = driver.find_element(By.CSS_SELECTOR, 'body>div:last-of-type .geetest_holder.geetest_silver')
img_label.screenshot('yzm2.png')
print('正在保存验证码...')

"""识别图验证码"""
from img_api import base64_api

code_result_list = base64_api('yzm2.png', 21)
print('验证码识别结果为:', code_result_list)  # 173,262|112,139|254,224

result_list = code_result_list.split('|')  # ['173,262', '112,139', '254,224']

"""
4.0+版本的move_to_element_with_offset方法会以元素中心为基准进行偏移,而4.0版本会以左上角顶点为基准进行偏移
既然是基于中心的偏移,我们只需要获取页面点选图片元素后,获取其长度和宽度,
再在move_to_element_with_offset的后两个参数中,减去一半的长度、宽度再进行偏移就好了。
"""
code_label_half_width = img_label.rect['width'] / 2  # 验证码标签对象一半的宽度
code_label_half_height = img_label.rect['height'] / 2  # 验证码标签对象一半的宽度

for result in result_list:
    x = int(result.split(',')[0])  # x轴  str 转 int
    y = int(result.split(',')[1])  # y轴  str 转 int

    # # move_to_element_with_offset  根据元素执行点击操作
    # move_to_element_with_offset 会以元素中心为基准进行偏移
    ActionChains(driver).move_to_element_with_offset(
        img_label,  # 验证码标签对象
        x - code_label_half_width,  # 计算 x 轴点的位置
        y - code_label_half_height  # 计算 y 轴点的位置
    ).click().perform()

# 点击确认
driver.find_element(By.CSS_SELECTOR, '.geetest_commit_tip').click()

input()
driver.quit()

四、滑块验证码

1. 简单滑块

import time
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
# 绕过检测的代码要放到实例化浏览器对象下面
# 修改selenium打开浏览器的属性特征
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
    "source": """
    Object.defineProperty(navigator, 'webdriver', {
      get: () => false
    })
  """
})
driver.get('https://kyfw.12306.cn/otn/resources/login.html')
driver.implicitly_wait(10)
driver.maximize_window()

"""模拟用户名和密码输入"""
driver.find_element(By.CSS_SELECTOR, '#J-userName').send_keys('19999999999')
driver.find_element(By.CSS_SELECTOR, '#J-password').send_keys('1234567890')
driver.find_element(By.CSS_SELECTOR, '#J-login').click()
time.sleep(1)

# 定位滑块元素
h = driver.find_element(By.CSS_SELECTOR, '#nc_1_n1z')

"""使用鼠标动作链滑动"""
action = ActionChains(driver)
action.click_and_hold(h)  # click_and_hold 点击按住元素并且保持
action.move_by_offset(340, 0)
# action.move_by_offset(85, 0)
# action.move_by_offset(85, 0)
# action.move_by_offset(85, 0)
# release() 松开鼠标
action.release().perform()

input('阻塞, 回车继续:')
driver.quit()

"""
如果加常规的染过检测的代码还是过不了滑块
那么可以使用本地浏览器 或者 用绕过流行特征的方式, 染过服务器检测
参考: https://blog.csdn.net/weixin_45081575/article/details/126585575


绕过检测方式:
    1. driver.execute_cdp_cmd("
    2. 刷js文件, 将浏览器的其他特征刷掉
    3. 操作本地浏览器
"""

2. 极验验证

分段类型

获取验证码图片
修改网页前端属性显示全部图片

因为在此案例网站中, 滑动验证码的图片默认是隐藏的,所以不方便我们查看分析。可以去进行验证码的滑动,查看在滑动的过程中前端的标签属性哪里会发生变化,然后手动修改变化后的属性值,让验证码显示在页面上来。

  • 没滑动前标签样式是这样的:注意查看 div标签的 gt_widget gt_hide 这个类属性

在这里插入图片描述

  • 滑动后div标签属性发生了变化,其中gt_hide属性变成了gt_show

在这里插入图片描述

基于上述情况, 可以在Elements元素面板中将gt_hide修改成gt_show即可查看到显示出来的滑动验证码,鼠标双击此属性即可键入修改。
但是修改后有时效性,后续有需要的话需要重复多次修改

下载验证码

这种类型的滑块验证码有一个特点,在前端中整个滑动验证码将图片分割成了若干份,并且在前端中将图片顺序打乱了。那么问题来了,为什么在前端网页中我们看到的图片是一个正常的图片呢?原因在于前端中将乱序的图片通过css偏移,让每个分割的图片显示到了特定的位置,从而用户看到的是一个正常的图片。

  • 当使用元素选择工具选取验证码部分标签对象的时候,看到的就是一个一个分块的图片

在这里插入图片描述

  • 在每个分块图片的div标签中,包含了整张图片的链接地址、分块图片的偏移数据,因此我们需要提取这些数据。整张图片的链接地址用于请求下载图片,下载的图片是乱序的,因此要根据分块图片的偏移量数据还原。

在这里插入图片描述

  • 以下代码是下载验证码,并且提取分块图片偏移量的代码
def get_image(div_path, file_name):
    """
    下载错乱的图片  获得所有图片的偏移值
    :param div_path: 根据xpath提取图片,传入xpath规则
    :param file_name: 保存的图片的文件名
    :return: 返回图片的偏移值
    """
    background_images = driver.find_elements(By.CSS_SELECTOR, div_path)  # 根据css语法提取图片所在的标签

    location_list = []  # 定义一个空列表, 后期存放所有图片偏移值的信息
    for background_image in background_images:
        location = {}  # 定义一个空字典, 后以键值对的形式记录每一张图片的偏移量
        result = re.findall('background-image: url\("(.*?)"\); background-position: (.*?)px (.*?)px;',
                            background_image.get_attribute('style'))  # 根据每一个片段的图片对应得标签

        location['x'] = int(result[0][1])  # 第一个偏移量用 x 做字典的第一个键
        location['y'] = int(result[0][2])  # 第二个偏移量用 y 做字典的第二个键

        image_url = result[0][0]  # 取出匹配结果中图片的url地址, 后期需要下载图片

        location_list.append(location)

    # 替换图片 url 后缀, 避免在pycharm显示不了
    image_url = image_url.replace('webp', 'jpg')

    image_result = requests.get(image_url).content  # 请求图片数据
    with open(file_name, 'wb') as f:
        f.write(image_result)

    return location_list
  • 要下载两张验证码图片, 一张是完整的验证码图片,一张是带缺口的验证码图片

    • 以下标签是完整验证码图片对应的标签
      在这里插入图片描述
    • 以下标签是带缺口的验证码图片对应的标签
      在这里插入图片描述
  • 下载好的图片是乱序的图片

    • 完整验证码如下所示:
      在这里插入图片描述
    • 带缺口的验证码如下所示:
      在这里插入图片描述
    • 验证码会实时刷新,所以课件中所示验证码不一样
还原验证码图片
  • 使用pillow模块对乱序图片进行分割,按照偏移量规则进行还原

根据上述操作咱们已经获取到两张验证码图片,但是图片显示出来是错乱乱序的。我们可以根据提取到的css偏移量对图片进行裁剪,然后贴到一张空白图像上对图片进行还原。具体代码如下:

def merge_image(image_file, location_list, restore_name):
    """
    还原图片数据 还原图片
    :param image_file: 传递一个图片文件路径
    :param location_list: 传递图片的偏移量数据
    :param restore_name: 还原图片后保存的文件名
    :return: None
    """
    im = Image.open(image_file)  # 打开传入的文件对象
    new_im = Image.new('RGB', (260, 116))  # 创建一个空白的图片

    im_list_upper = []  # 存放上半部分的图片
    im_list_down = []  # 存放下半部分的图片

    for location in location_list:  # 遍历 location_list 列表中的所有字典
        # print('乱序图片的偏移量', location)
        print(location)
        if location['y'] == -58:  # 上半边
            # crop() 方法返一个图像的矩形区域,需要指定 "左上顶点" 和 "右下点" 的坐标,返回一个指定区域的图片对象
            # abs()  方法是取数字的绝对值
            im_list_upper.append(im.crop((abs(location['x']), 58, abs(location['x']) + 10, 116)))
        if location['y'] == 0:  # 下半边
            im_list_down.append(im.crop((abs(location['x']), 0, abs(location['x']) + 10, 58)))

    x_offset = 0
    for im in im_list_upper:
        new_im.paste(im, (x_offset, 0))  # 把小图片放到 新的空白图片上 放上半部分
        x_offset += im.size[0]

    x_offset = 0
    for im in im_list_down:
        new_im.paste(im, (x_offset, 58))  # 把小图片放到 新的空白图片上 放下半部分
        x_offset += im.size[0]

    new_im.save(restore_name)
    return None
  • 完整滑动验证码图片和带缺口的验证码图片都需要还原
    • 还原后的完整验证码图片如下:
      在这里插入图片描述
    • 还原后的带缺口的验证码图片如下:
      在这里插入图片描述
获取验证码缺口距离

对比两张验证码图片,使用ddddocr调用接口识别出缺口距离,代码如下所示:

def get_gap(notch_img, all_img):
    """
    验证码缺口距离识别, 获取缺口偏移量
    :param notch_img: 带缺口的验证码文件路径
    :param all_img: 完整的验证码文件路径
    :return: 验证码缺口横向距离
    """
    slide = ddddocr.DdddOcr(beta=True)  # beta=True 通用识别

    # 打开带缺口的图片
    with open(notch_img, 'rb') as f:
        target_bytes = f.read()
    # 打开完整图片
    with open(all_img, 'rb') as f:
        background_bytes = f.read()
    # slide_comparison(二进制带缺口图片, 二进制完整图片)  --> 识别方法
    res = slide.slide_comparison(target_bytes, background_bytes)

    if res:
        # 识别结果样式: {'target': [117, 72]} --> 其中列表中第一个数字就是缺口的横向距离
        print("识别结果: ", res)
        print("缺口的横向距离: ", res['target'][0])
    else:
        raise Exception('验证码识别失败')

    return res['target'][0]
动验证码模拟滑动

使用pyautogui自动化工具做模拟滑动

PyAutoGUI 是一个面向人类的跨平台 GUI 自动化 Python 模块。用于以编程方式控制鼠标和键盘。

  • 安装:pip install pyautogui
  • 英文文档:https://pyautogui.readthedocs.org
  • 中文示例:https://github.com/asweigart/pyautogui/blob/master/docs/simplified-chinese.ipynb
  • 依赖关系
macOS 需要安装 pyobjc-core 和 pyobjc 模块(按顺序)。

Linux 需要安装 python3-xlib(或 Python 2 的 python-xlib)模块。

需要安装 Pillow,在 Linux 上您可能需要安装额外的库以确保 Pillow 的 PNG/JPEG 正常工作
  • 为什么使用pyautogui

    极验的滑动验证码并不是简单的将滑块滑动到缺口位置就能成功的,滑动到缺口位置只是其中一个必要条件,除此以外极验滑动验证码还会校验用户的滑动轨迹,如果滑动的轨迹校验不是一个正常的人为滑动轨迹,即使滑动到了缺口位置也不会成功通过。

    鼠标动作链(ActionChains)是一个不错的选择,但是操作的是浏览器对象,即使轨迹构建好滑到缺口位置也会经常性的不能通过极验滑动验证码。

    因此使用pyautogui操作系统的鼠标,构建好滑动轨迹,那么一般情况下极验验证码就很难做识别了。

  • 具体具体代码实现如下:

def move_slide(offset_x, offset_y, left):
    """
    执行滑块的移动
    :param offset_x: 滑块的x轴坐标
    :param offset_y: 滑块的y轴坐标
    :param left: 需要移动的距离
    :return:
    """

    # 移动到滑块的位置
    # duration为持续时间
    # random.uniform(参数1,参数2) 返回参数1和参数2之间的任意值
    pyautogui.moveTo(
        offset_x,
        offset_y,
        duration=0.1 + random.uniform(0, 0.1 + random.randint(1, 100) / 100))

    # 按下鼠标 准备开始滑动
    pyautogui.mouseDown()
    # random.randint(参数1, 参数2) 函数返回参数1和参数2之间的任意整数
    offset_y += random.randint(9, 19)
    pyautogui.moveTo(
        offset_x + int(left * random.randint(15, 25) / 20),
        offset_y,
        duration=0.28)

    offset_y += random.randint(-9, 0)
    pyautogui.moveTo(
        offset_x + int(left * random.randint(18, 22) / 20),
        offset_y,
        duration=random.randint(19, 31) / 100)

    offset_y += random.randint(0, 8)
    pyautogui.moveTo(
        offset_x + int(left * random.randint(19, 21) / 20),
        offset_y,
        duration=random.randint(20, 40) / 100)

    offset_y += random.randint(-3, 3)
    pyautogui.moveTo(
        left + offset_x + random.randint(-3, 3),
        offset_y,
        duration=0.5 + random.randint(-10, 10) / 100)

    offset_y += random.randint(-2, 2)
    pyautogui.moveTo(
        left + offset_x + random.randint(-2, 2),
        offset_y,
        duration=0.5 + random.randint(-3, 3) / 100)

    # 释放鼠标
    pyautogui.mouseUp()
    time.sleep(3)
练习案例
import random
import re
import time

import ddddocr
import requests
from PIL import Image
from selenium import webdriver
from selenium.webdriver.common.by import By
import pyautogui


def get_image(div_path, file_name):
    """
    下载乱序图片, 获取每张小图的偏移量
    :param div_path: 解析语法定位到图片的标签对象
    :param file_name: 保存的文件名字
    :return: 返回每一个小图的偏移量数据
    """
    # 根据解析语法定位标签
    background_images = driver.find_elements(By.CSS_SELECTOR, div_path)

    location_list = []  # 定义空列表, 存放所有图片偏移量信息
    for background_image in background_images:
        location = {}  # 定义一个空字典, 后续以键值对的形式记录每一个小图的哦爱你一辆

        # 提取图片地址, 每一个小图的偏移量数据
        result = re.findall('background-image: url\("(.*?)"\); background-position: (.*?)px (.*?)px;',
                            background_image.get_attribute('style'))

        location['x'] = int(result[0][1])  # 第一个偏移量用 x 做字典的第一个键
        location['y'] = int(result[0][2])  # 第二个偏移量用 y 做字典的第二个键

        image_url = result[0][0]  # 取出匹配到的图片链接, 后续请求保存图片

        location_list.append(location)

    # 替换图片 url 后缀, 避免在pycharm显示不了
    image_url = image_url.replace('webp', 'jpg')

    image_result = requests.get(image_url).content  # 请求图片数据
    with open(file_name, 'wb') as f:
        f.write(image_result)

    return location_list  # 返回当前图片的偏移量信息


def merge_image(image_file, location_list, restore_name):
    """
    还原乱序的图片
    :param image_file: 文件名路径
    :param location_list: 偏移量规则
    :param restore_name: 还原后保存的图片名字
    :return: None
    """

    im = Image.open(image_file)  # 打开传入的文件对象
    new_im = Image.new('RGB', (260, 116))  # 创建一个空白的图片

    im_list_upper = []  # 存放上半部分的图片
    im_list_down = []  # 存放下半部分的图片

    for location in location_list:

        print(location)
        if location['y'] == -58:  # 上半边
            # crop() 方法返一个图像的矩形区域,需要指定 "左上顶点" 和 "右下点" 的坐标,返回一个指定区域的图片对象
            # abs()  方法是取数字的绝对值
            im_list_upper.append(im.crop((abs(location['x']), 58, abs(location['x']) + 10, 116)))
        if location['y'] == 0:  # 下半边
            im_list_down.append(im.crop((abs(location['x']), 0, abs(location['x']) + 10, 58)))

    x_offset = 0  # 初始值
    for im in im_list_upper:
        #
        new_im.paste(im, (x_offset, 0))  # 把小图片放到 新的空白图片上 放上半部分
        x_offset += im.size[0]

    x_offset = 0
    for im in im_list_down:
        new_im.paste(im, (x_offset, 58))  # 把小图片放到 新的空白图片上 放下半部分
        x_offset += im.size[0]

    new_im.save(restore_name)  # 保存还原图片
    return None


def get_gap(notch_img, all_img):
    """
    验证码缺口距离识别, 获取缺口偏移量
    :param notch_img: 带缺口的验证码文件路径
    :param all_img: 完整的验证码文件路径
    :return: 验证码缺口横向距离
    """

    slide = ddddocr.DdddOcr(beta=True)  # beta=True 通用识别

    # 打开带缺口的图片
    with open(notch_img, 'rb') as f:
        target_bytes = f.read()
    # 打开完整图片
    with open(all_img, 'rb') as f:
        background_bytes = f.read()

    # slide_comparison(二进制带缺口图片, 二进制完整图片)  --> 识别方法
    res = slide.slide_comparison(target_bytes, background_bytes)

    if res:
        # 识别结果样式: {'target': [117, 72]} --> 其中列表中第一个数字就是缺口的横向距离
        print("识别结果: ", res)
        print("缺口的横向距离: ", res['target'][0])
    else:
        raise Exception('验证码识别失败')

    return res['target'][0]


def move_slide(offset_x, offset_y, left):
    """
    执行滑块的移动
    :param offset_x: 滑块的x轴坐标
    :param offset_y: 滑块的y轴坐标
    :param left: 需要移动的距离
    :return:
    """
    # 鼠标移动到显示器窗口指定的坐标位置, 后续自己电脑的位置, 需要重新定位
    pyautogui.moveTo(
        offset_x,
        offset_y,
        # 设置移动时间
        duration=0.1 + random.uniform(0, 0.1 + random.randint(1, 100) / 100))

    pyautogui.mouseDown()  # 按下鼠标

    offset_y += random.randint(9, 19)  # 随机偏移Y轴位置
    pyautogui.moveTo(
        offset_x + int(left * random.randint(15, 25) / 20),
        offset_y,
        duration=0.28)

    offset_y += random.randint(-9, 0)
    pyautogui.moveTo(
        offset_x + int(left * random.randint(18, 22) / 20),
        offset_y,
        duration=random.randint(19, 31) / 100)

    offset_y += random.randint(0, 8)
    pyautogui.moveTo(
        offset_x + int(left * random.randint(19, 21) / 20),
        offset_y,
        duration=random.randint(20, 40) / 100)

    offset_y += random.randint(-3, 3)
    pyautogui.moveTo(
        left + offset_x + random.randint(-3, 3),
        offset_y,
        duration=0.5 + random.randint(-10, 10) / 100)

    offset_y += random.randint(-2, 2)
    pyautogui.moveTo(
        left + offset_x + random.randint(-2, 2),
        offset_y,
        duration=0.5 + random.randint(-3, 3) / 100)

    pyautogui.mouseUp()  # 松开鼠标
    time.sleep(3)


if __name__ == '__main__':
    # 实例化一个浏览器对象
    driver = webdriver.Chrome()
    # 打开浏览器页面, 请求url地址
    driver.get('http://www.cnbaowen.net/api/geetest/')
    driver.maximize_window()
    driver.implicitly_wait(10)

    time.sleep(2)
    """获取到两张图片(一个是有缺口的图片, 一个是完整的图片)"""
    image1_location = get_image('.gt_cut_fullbg.gt_show>div', 'split-1.png')  # 获取完整的图片
    print('正在保存完整图片......')

    image2_location = get_image('.gt_cut_bg.gt_show>div', 'split-2.png')  # 获取带缺口的图片
    print('正在保存带有缺口的图片......\n')
    print('偏移量规则:', image2_location)

    """还原图片"""
    merge_image('split-1.png', image1_location, 'output-all.png')
    print('正在还原完整的验证码图片......')
    merge_image('split-2.png', image2_location, 'output-notch.png')
    print('正在还原带缺口的验证码图片......\n')

    """调用ddddocr识别滑块距离"""
    # 滑块距离不一定对, 可能会存在偏差
    distance = get_gap('output-notch.png', 'output-all.png')

    # 鼠标动作链滑动极验验证码经常会被检测到

    # 实际滑动距离需要做细微的调整
    # distance += 2

    """调用移动滑块的函数"""
    # 当调用滑动的时候, 不要动你的鼠标
    move_slide(665, 390, distance)

    input('阻塞, 回车继续:')
    driver.quit()

canvas类型处理

获取验证码图片

目标网址中,滑动验证码图片有三张图片,每张图片对应的标签分别是三个 <canvas> ,并且在前端样式经过处理后,不管选中哪个 <canvas> 标签,选择的都是滑动验证码图片同一块标签区域。

在这里插入图片描述
根据分析三张图依次对应的验证码图像如下所示:
在这里插入图片描述
基于这样的情况,那么我们怎样去截取验证码图片呢?咱们在前端可以修改 <canvas> 标签的样式,让想要保存的验证码图片显示出来,不想要的验证码图片通过样式修改可以将其隐藏。我们只需要获取带缺口的验证码图片和完整验证码图片就可以了,可以通过执行js代码修改标签样式,如下所示:

通过在Console控制台调试js代码

  • document.querySelectorAll("canvas")
    • 上述js代码可以获取到三个 标签,对应的索引分别是 0 - 1 - 2,其中0代表缺口图,1代表滑块图,2代表完整图
      在这里插入图片描述
  • document.querySelectorAll("canvas")[1].style="display: none;"
    • 上述js代码是将索引为 [1] 的**<canvas>**标签通过.style修改其属性display为隐藏,执行后可以看到滑动验证码的滑块图片就被隐藏了
      在这里插入图片描述
    • 当滑块隐藏后那么我们可以将带缺口的验证码截图并保存下来。按照上述相同的逻辑,我们可以把完整的滑动验证码通过修改标签属性显示在前端后,截图保存下来。
      js代码:document.querySelectorAll("canvas")[2].style="",此js逻辑是移除了索引为 [2] 的**<canvas>**标签的style属性。执行js后如下所示:
      在这里插入图片描述

获取验证码图片的代码逻辑如下:

def get_captcha():
    """保存验证码的缺口图片和完整的图片"""
    wait.until(
        EC.text_to_be_present_in_element((By.CSS_SELECTOR, ".geetest_radar_tip_content"), "点击按钮进行验证")
    )

    driver.find_element(By.CSS_SELECTOR, ".geetest_radar_tip_content").click()

    # 确定滑块加载出来之后 再进行后续的操作
    wait.until(
        EC.text_to_be_present_in_element((By.CSS_SELECTOR, ".geetest_slider_tip.geetest_fade"), "拖动滑块完成拼图")
    )
    time.sleep(1)
    print("滑块加载完成")

    # 执行以下是隐藏滑块图片 [0]是缺口图片; [1]是滑块图片; [2]是完整图片
    driver.execute_script('document.querySelectorAll("canvas")[1].style="display: none;"')

    """标签元素截图保存验证码"""
    captcha_tag = driver.find_element(By.CSS_SELECTOR, ".geetest_window")
    # 获取有缺口的验证码
    captcha_tag.screenshot('output-notch.png')
    print('正在保存带缺口的验证码......')

    time.sleep(1)
    # 先修改CSS样式 得到完整验证码图片, 不带缺口的; 修改后不需要恢复, 因为当后续滑动的时候,验证码前端样式会自动修改
    driver.execute_script('document.querySelectorAll("canvas")[2].style=""')
    captcha_tag.screenshot('output-all.png')
    print('正在保存不带缺口的完整的验证码......')

    time.sleep(1)
    # 为了更加清楚的看到滑动的效果 恢复一下之前修改的css样式
    driver.execute_script('document.querySelectorAll("canvas")[1].style="opacity: 1; display: block;"')
获取验证码缺口距离

对比两张验证码图片,使用ddddocr调用接口识别出缺口距离,代码如下所示:

def get_gap(notch_img, all_img):
    """
    验证码缺口距离识别, 获取缺口偏移量
    :param notch_img: 带缺口的验证码文件路径
    :param all_img: 完整的验证码文件路径
    :return: 验证码缺口横向距离
    """
    slide = ddddocr.DdddOcr(beta=True)  # beta=True 通用识别

    # 打开带缺口的图片
    with open(notch_img, 'rb') as f:
        target_bytes = f.read()
    # 打开完整图片
    with open(all_img, 'rb') as f:
        background_bytes = f.read()
    # slide_comparison(二进制带缺口图片, 二进制完整图片)  --> 识别方法
    res = slide.slide_comparison(target_bytes, background_bytes)

    if res:
        # 识别结果样式: {'target': [117, 72]} --> 其中列表中第一个数字就是缺口的横向距离
        print("识别结果: ", res)
        print("缺口的横向距离: ", res['target'][0])
    else:
        raise Exception('验证码识别失败')

    return res['target'][0]
滑动验证码模拟滑动

同样使用pyautogui模拟鼠标对验证码进行滑动, 代码如下所示:

def move_slide(offset_x, offset_y, left):
    """
    执行滑块的移动
    :param offset_x: 滑块的x轴坐标
    :param offset_y: 滑块的y轴坐标
    :param left: 需要移动的距离
    :return:
    """

    # duration为持续时间
    # random.uniform(参数1,参数2) 返回参数1和参数2之间的任意值
    pyautogui.moveTo(
        offset_x,
        offset_y,
        duration=0.1 + random.uniform(0, 0.1 + random.randint(1, 100) / 100))

    # 按下鼠标 准备开始滑动
    pyautogui.mouseDown()
    # random.randint(参数1, 参数2) 函数返回参数1和参数2之间的任意整数
    offset_y += random.randint(9, 19)
    pyautogui.moveTo(
        offset_x + int(left * random.randint(15, 25) / 20),
        offset_y,
        duration=0.28)

    offset_y += random.randint(-9, 0)
    pyautogui.moveTo(
        offset_x + int(left * random.randint(18, 22) / 20),
        offset_y,
        duration=random.randint(19, 31) / 100)

    offset_y += random.randint(0, 8)
    pyautogui.moveTo(
        offset_x + int(left * random.randint(19, 21) / 20),
        offset_y,
        duration=random.randint(20, 40) / 100)

    offset_y += random.randint(-3, 3)
    pyautogui.moveTo(
        left + offset_x + random.randint(-3, 3),
        offset_y,
        duration=0.5 + random.randint(-10, 10) / 100)

    offset_y += random.randint(-2, 2)
    pyautogui.moveTo(
        left + offset_x + random.randint(-2, 2),
        offset_y,
        duration=0.5 + random.randint(-3, 3) / 100)

    # 释放鼠标
    pyautogui.mouseUp()
    time.sleep(3)
练习案例
import random
import time
import ddddocr
import pyautogui
from selenium import webdriver
from selenium.webdriver.common.by import By

def get_captcha():
    time.sleep(1)
    # 点击人机校验
    driver.find_element(By.CSS_SELECTOR, ".geetest_radar_tip_content").click()
    time.sleep(2)

    print("滑块加载完成")

    # 如果不确定下表索引是从零开始的 还是1开始的 可以简单测试一下:从0开始的
    # 执行以下是隐藏滑块图片 [0]是缺口图片; [1]是滑块图片; [2]是完整图片
    driver.execute_script('document.querySelectorAll("canvas")[1].style="display: none;"')

    """标签元素截图保存验证码"""
    captcha_tag = driver.find_element(By.CSS_SELECTOR, ".geetest_window")

    captcha_tag.screenshot('output-notch.png')
    print('正在保存带缺口的验证码......')

    time.sleep(1)

    # 执行一下js代码, 可以显示完整图片
    driver.execute_script('document.querySelectorAll("canvas")[2].style=""')
    captcha_tag.screenshot('output-all.png')
    print('正在保存不带缺口的完整的验证码......')

    time.sleep(1)

    # 为了更加清楚的看到滑动的效果 恢复一下之前修改的css样式; opacity: 1 --> 不透明; display: block --> 显示块级元素
    driver.execute_script('document.querySelectorAll("canvas")[1].style="opacity: 1; display: block;"')


def get_gap(notch_img, all_img):
    """
    验证码缺口距离识别, 获取缺口偏移量
    :param notch_img: 带缺口的验证码文件路径
    :param all_img: 完整的验证码文件路径
    :return: 验证码缺口横向距离
    """

    slide = ddddocr.DdddOcr(beta=True)  # beta=True 通用识别

    # 打开带缺口的图片
    with open(notch_img, 'rb') as f:
        target_bytes = f.read()
    # 打开完整图片
    with open(all_img, 'rb') as f:
        background_bytes = f.read()

    # slide_comparison(二进制带缺口图片, 二进制完整图片)  --> 识别方法
    res = slide.slide_comparison(target_bytes, background_bytes)

    if res:
        # 识别结果样式: {'target': [117, 72]} --> 其中列表中第一个数字就是缺口的横向距离
        print("识别结果: ", res)
        print("缺口的横向距离: ", res['target'][0])
    else:
        raise Exception('验证码识别失败')

    return res['target'][0]


def move_slide(offset_x, offset_y, left):
    """
    执行滑块的移动
    :param offset_x: 滑块的x轴坐标
    :param offset_y: 滑块的y轴坐标
    :param left: 需要移动的距离
    :return:
    """
    # 鼠标移动到显示器窗口指定的坐标位置, 后续自己电脑的位置, 需要重新定位
    pyautogui.moveTo(
        offset_x,
        offset_y,
        # 设置移动时间
        duration=0.1 + random.uniform(0, 0.1 + random.randint(1, 100) / 100))

    pyautogui.mouseDown()  # 按下鼠标

    offset_y += random.randint(9, 19)  # 随机偏移Y轴位置
    pyautogui.moveTo(
        offset_x + int(left * random.randint(15, 25) / 20),
        offset_y,
        duration=0.28)

    offset_y += random.randint(-9, 0)
    pyautogui.moveTo(
        offset_x + int(left * random.randint(18, 22) / 20),
        offset_y,
        duration=random.randint(19, 31) / 100)

    offset_y += random.randint(0, 8)
    pyautogui.moveTo(
        offset_x + int(left * random.randint(19, 21) / 20),
        offset_y,
        duration=random.randint(20, 40) / 100)

    offset_y += random.randint(-3, 3)
    pyautogui.moveTo(
        left + offset_x + random.randint(-3, 3),
        offset_y,
        duration=0.5 + random.randint(-10, 10) / 100)

    offset_y += random.randint(-2, 2)
    pyautogui.moveTo(
        left + offset_x + random.randint(-2, 2),
        offset_y,
        duration=0.5 + random.randint(-3, 3) / 100)

    pyautogui.mouseUp()  # 松开鼠标
    time.sleep(3)


if __name__ == '__main__':
    driver = webdriver.Chrome()
    driver.get("https://www.geetest.com/demo/slide-float.html")
    driver.maximize_window()  # 最大化
    driver.implicitly_wait(10)

    # 调用保存验证码的函数
    get_captcha()

    """调用ddddocr识别滑块距离"""
    distance = get_gap('output-notch.png', 'output-all.png')
    # 实际滑动距离需要做细微的调整
    distance -= 5

    """调用移动滑块的函数"""
    move_slide(937, 475, distance)

    input("阻塞:")
    driver.quit()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/283124.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK设置相机本身的数据保存(CustomData)功能(C#)

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK设置相机本身的数据保存&#xff08;CustomData&#xff09;功能&#xff08;C#&#xff09; Baumer工业相机Baumer工业相机的数据保存&#xff08;CustomData&#xff09;功能的技术背景CameraExplorer如何使用图像剪切&#xff…

【42页动态规划学习笔记分享】动态规划核心原理详解及27道LeetCode相关经典题目汇总

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能AI、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推荐--…

MIT线性代数笔记-第35讲-期末复习

目录 35.期末复习打赏 35.期末复习 已知一个矩阵 A A A满足 A x ⃗ [ 1 0 0 ] A \vec{x} \begin{bmatrix} 1 \\ 0 \\ 0 \end{bmatrix} Ax ​100​ ​无解且 A x ⃗ [ 0 1 0 ] A \vec{x} \begin{bmatrix} 0 \\ 1 \\ 0 \end{bmatrix} Ax ​010​ ​仅有一个解 (1)求 A A A的…

Linux驱动学习—pinctl和gpio子系统

1、pinctl和gpio子系统&#xff08;一&#xff09; 1.1pinctrl 子系统主要工作内容 <1>获取设备树中 pin 信息&#xff0c;管理系统中所有的可以控制的 pin&#xff0c; 在系统初始化的时候&#xff0c; 枚举所有可以控制的 pin&#xff0c; 并标识这些 pin。 <2>…

burpsuite模块介绍之compare

导语 Burp Comparer是Burp Suite中的一个工具&#xff0c;主要提供一个可视化的差异比对功能&#xff0c;可以用于分析比较两次数据之间的区别。它的应用场景包括但不限于&#xff1a; 枚举用户名过程中&#xff0c;对比分析登陆成功和失败时&#xff0c;服务器端反馈结果的区…

python脚本实现一次提取多个文件下的图片

problem formulation 有时候下载的数据集如下&#xff0c;就很烦&#xff0c;一个里面就一张图片 code import os import shutil# 定义源目录和目标目录 source_dir ./dataset/data/Detection destination_dir ./dataset/data/img# 确保目标目录存在&#xff0c;如果不存…

初识大数据,一文掌握大数据必备知识文集(7)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

pycharm python环境安装

目录 1.Python安装 2.PyQt5介绍 3.安装pyuic 4.启动designer.exe 5.pyinstaller(打包发布程序) 6.指定源安装 7.PyQt5-tools安装失败处理 8.控件介绍 9.错误记录 1.NameError: name reload is not defined 10.开发记录 重写报文输出和文件 ​编辑 1.Python安装 点…

BFS

目录 BFS 走迷宫 BFS 算法特点 优先考虑宽度&#xff0c;换句话说就是按层推进&#xff0c;直到最后一层。 空间复杂度&#xff1a;O&#xff08;2^h&#xff09; BFS是按宽度搜索&#xff0c;所以可以找到最短路&#xff0c;适用于解决像最短路&#xff0c;最少之类的问题…

Python装饰器的专业解释

装饰器&#xff0c;其实是用到了闭包的原理来进行操作的。 单个装饰器&#xff1a; 以下是一个简单的例子&#xff1a; def outer(func):print("OUTER enter ...")def wrapper(*args, **kwargs):print("调用之前......")result func(*args, **kwargs)p…

【YOLO系列】yolo V1 ,V3,V5,V8 解释

文章目录 yolo V1 模型结构图通道数 的 物理意义是什么&#xff1f;输出 7730 怎么理解&#xff1f;YOLO v1 损失函数LOSS yolo V3yolo V5yolo V8 视频来源&#xff1a;https://www.bilibili.com/video/BV13K411t7Zs/ AI视频小助理 一、YOLO系列的目标检测算法&#xff0c;其中…

【操作系统】存储器管理

目录 4.1 存储器的层次结构 4.1.1 多级存储结构 4.1. 2 可执行存储器 4.1.3 高速缓存和磁盘缓存 4.2 程序的装入和链接 4.2.1 程序的装入 4.2.2 程序的链接 1.静态链接(Static Linking)方式 (1) 对相对地址进行修改。 (2) 变换外部调用符号。 2. 装入时动态链接(Load-t…

CodeWave赋能创新的全功能技术平台

目录 前言1 应用中心2 资产中心&#xff1a;汇聚创新能量&#xff0c;提供开发加速3 集成中心3.1 API管理3.2 报表管理 4 运维中心4.1 资源监控4.2 用户管理4.3 权限管理4.4 日志与监控 5 配置中心5.1 源码配置5.2 镜像仓库配置5.3 数据库配置5.4 报表配置5.5 资产配置5.6 品牌…

JavaFX:MVC模式学习01-使用PropertyValueFactory将模型与视图绑定

PropertyValueFactory类是“TableColumn cell value factory”,绑定创建列表中的项。示例如下&#xff1a; TableColumn<Person,String> firstNameCol new TableColumn<Person,String>("First Name");firstNameCol.setCellValueFactory(new PropertyVal…

安装torch(GPU版本)并在Pycharm中配置

零.前置环境 1.NVIDIA GPU Computing Toolkit已安装 版本为&#xff1a;11.6 已添加到环境变量 C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.6\bin C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.6\libnvvp 在cmd中查看cuda版本 方法1&#xff1a…

详解Vue3中的鼠标事件mousemove、mouseover和mouseout

本文主要介绍Vue3中的常见鼠标事件mousemove、mouseover和mouseout。 目录 一、mousemove——鼠标移动事件二、mouseover——鼠标移入事件三、mouseout——鼠标移出事件 下面是Vue 3中常用的鼠标事件mousemove、mouseover和mouseout的详解。 一、mousemove——鼠标移动事件 鼠…

图神经网络--GNN从入门到精通

图神经网络--GNN从入门到精通 一、图的基本表示和特征工程1.1 什么是图1.2 图的基本表示1.3 图的性质--度&#xff08;degree)1.4 连通图&#xff0c;连通分量1.5有向图连通性1.6图直径1.7度中心性1.7特征中心性&#xff08; Eigenvector Centrality&#xff09;1.8中介中心性 …

年度总结 | 回味2023不平凡的一年

目录 前言1. 平台成就2. 自我提升3. Bug连连4. 个人展望 前言 每年CSDN的总结都不能落下&#xff0c;回顾去年&#xff1a;年度总结 | 回味2022不平凡的一年&#xff0c;在回忆今年&#xff0c;展望下年 1. 平台成就 平台造就我&#xff08;我也造就平台哈哈&#xff09; 每…

汇川PLC(H5U):定时器指令

一、H5U系列的定时器种类 H5U系列PLC的定时器指令都封装成指令块了&#xff0c;共4种类型&#xff1a;脉冲定时器、接通延时定时器、关断延时定时器、时间累加定时器。 H5U系列PLC的定时器时间基准是1ms&#xff0c;在IN引脚的执行指令有效的时候开始跟新计数器的值。 我们知…

门控循环单元(GRU)-多输入时序预测

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、部分代码&#xff1a; 四、完整代码数据下载&#xff1a; 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 本代码基于Matlab平台编译…