目录
一、滑块验证码
滑块验证码有:滑动缺口验证码、滑动滚动条验证码,滚动条比较简单,只需要计算右侧(通常是往右滑动)的距离。需要用户拖动解决的,还有旋转验证码等变体,本文注意介绍通过使用DrissionPage及ddddocr来破解缺口类型的验证码。
二、破解思路
需要破解滑动验证码,首先要确定缺口目标及背景板,然后计算出缺口在背景图上的坐标位置,同时为了更好的计算坐标位置,有种方法是对背景缺口进行切割,切割掉缺口的右侧,这样会使坐标计算更准确。在python中常用的方式有自动化、接口破解相关方式。
三、代码实现
3.1、自动化方式
python自动化工具有selenium、drissionpage等,本文演示使用drissionpage来操作自动化模拟滑块验证码操作。
python
from DrissionPage import ChromiumPage, ChromiumOptions
import base64, ddddocr, time, random
from io import BytesIO
from PIL import Image
import json, traceback
co = ChromiumOptions()
co.no_imgs(False)
# Linux必需配置
co.set_argument('--headless')
co.set_argument('--no-sandbox')
co.set_argument('--disable-gpu')
co.set_argument('--disable-dev-shm-usage')
co.set_argument('--disk-cache-dir=/tmp/chrome_cache')
# 解决端口冲突问题
co.set_argument('--remote-debugging-port=0') # 让系统自动分配可用端口
co.set_argument('--remote-allow-origins=*')
# 确保使用headless模式
co.set_argument('--headless=new')
# 模拟真实浏览器行为
co.set_argument('--disable-blink-features=AutomationControlled')
co.set_argument('--disable-infobars')
co.set_argument('--disable-web-security')
co.set_argument('--allow-running-insecure-content')
co.set_user_agent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36')
# 禁用自动化标志
co.set_pref('excludeSwitches', ['enable-automation'])
co.set_pref('useAutomationExtension', False)
page = ChromiumPage(co)
# 监听的验证码接口
page.listen.start('/api/captcha/get')
# 登录以触发验证码
page.ele('@placeholder=请输入账号').input(self.username)
page.ele('@placeholder=请输入密码').input(self.password)
page.ele('登 录').click()
time.sleep(1)
packet = page.listen.wait(timeout=30) # 等待验证码接口响应
if not packet:
raise Except('未获取到验证码接口响应')
# 验证码接口返回
originalImageBase64 = packet.response.body['data']['repData']['originalImageBase64']
jigsawImageBase64 = packet.response.body['data']['repData']['jigsawImageBase64']
target_bytes = base64.b64decode(jigsawImageBase64)
background_bytes = base64.b64decode(originalImageBase64)
# 借助ddddocr计算滑块距离
det = ddddocr.DdddOcr(det=False, ocr=False, show_ad=False)
det.slide_match(target_bytes, background_bytes)
target_x = res['target'][0]
# 定位到滑块元素
slider_ele = page.ele('@class=verify-left-bar')
if not slider_ele:
raise Except('未定位到滑动元素')
# 滑动滑块,随机抖动,模拟人为操作
random_list = [3, 2, 1, 0, -1, -2, -3]
"""
这里offset_x的取值是因为ddddocr识别的是根据背景图的x、y来计算的,与实际页面值不一样,所以这里需要对计算出来的值进行转换。其中380为背景图宽度、360为实际页面滑块区域可拖动宽度(真实场景下应该定位到元素求出真实的宽度,而不是写死)。常用转换公式有以下几种:
1、int(x * image_width / track_width)
2、x * image_width // track_width
"""
page.actions.move_to(slider_ele ).hold().move(offset_x=x * 380 // 360 + random.choice(random_list), duration=.3).release()
# 后续再通过监听login接口或者定位登录成功后的元素来判断是否成功。
3.2、接口方式
大体逻辑一致,只是不通过自动化工具启动的浏览器,更轻量化,更推荐使用的。同样要借助ddddocr来识别缺口位置,然后调用接口来完成无感滑动。
python
import base64
from typing import Tuple
import ddddocr
from PIL import Image
import io
import base64
from six import BytesIO
def calculate_min_xy_coordinates(jigsaw_image_bytes: bytes) -> Tuple[int, int]:
"""
计算滑块图片的最小 x 和 y 坐标(有效区域的最左侧和最上侧)。
"""
try:
image_stream = BytesIO(jigsaw_image_bytes)
img = Image.open(image_stream)
width, height = img.size
min_x = float("inf")
min_y = float("inf")
for x in range(width):
for y in range(height):
pixel = img.getpixel((x, y))
if len(pixel) == 4 and pixel[3] > 0:
min_x = min(min_x, x)
min_y = min(min_y, y)
return (
min_x if min_x != float("inf") else 0,
min_y if min_y != float("inf") else 0,
)
except Exception as exc:
print(f"处理滑块图像出错: {exc}")
return 0, 0
# 1、请求滑块验证码接口,拿到缺口图及背景图(为节省代码,这里假设已经拿到了)
target_bytes = base64.b64decode(jigsaw_base64)
background_bytes = base64.b64decode(original_base64)
# 2、计算滑块(应对有干扰滑块时,切掉头顶及右侧干扰区域,目的为了ddddocr能识别的更准确)
slider_img_x_px, slider_img_y_px = calculate_min_xy_coordinates(target_bytes )
with Image.open(io.BytesIO(background_bytes)) as img:
width, height = img.size
cropped_background = img.crop((0, slider_img_y_px , width, height))
cropped_background_bytes = BytesIO()
cropped_background.save(cropped_background_bytes, format="PNG")
cropped_background_bytes.seek(0)
# 3、识别距离
"""
其实第二步可以不用做,直接使用缺口图和背景图,ddddocr也可以精准识别(🤡)
"""
det = ddddocr.DdddOcr(det=False, ocr=False, show_ad=False)
result = det.slide_match(target_bytes, cropped_background_bytes.read())
origin_x = result ["target"][0]
x = origin_x - slider_img_x_px
offset_x = x * 380 // 360
print(f"经ddddocr计算,得出滑块与缺口间距离:{offset_x}(px)")
return int(offset_x)
四、总结
| 功能类别 | 解决的问题 | 对应的 ddddocr 方法 | 典型应用场景 |
|---|---|---|---|
| 图片验证码 | 识别图片中的内容 | classification() | 识别数字、字母、中英文混合的静态图片验证码 |
| 滑块验证码 | 计算滑块缺口的位置 | slide_match() | 拖动滑块至缺口处完成验证 |
| 点选验证码 | 检测并定位图中多个目标的坐标 | detection() | 按顺序点击图片中的文字或物体(如"请依次点击:书、杯子") |
| 计算验证码 | 识别数学算式并计算结果 | classification() + eval()处理 |
识别并计算出"23 + 9 = ?"这类验证码的结果 |
| [ddddocr 核心功能一览(AI回答)] |