众所周知,在网络爬虫横行的今天,网站为了保护自己的权益,不得不增加保护网站数据得安全措施。
源码:Q 16_90_36_19_73
好了,进入主题QAQ
一.首先就是如何生成缺口图跟带有缺口的背景图
逻辑:利用PIL库将一张图片设置成自己想要的大小,然后根据random随机在图片上裁剪出缺口,然后保存缺口图片跟被裁剪过的带缺口的背景图。同时返回缺口的坐标,方便后面跟前端返回的坐标进行比较。
from PIL import Image, ImageDraw, ImageFilter
import random
def create_slider_captcha(image_path, output_dir, block_size=40, target_size=(260, 130)):
# 打开图像并转换为 RGBA 格式
image = Image.open(image_path).convert("RGBA")
# 调整图像大小到目标尺寸
image = image.resize(target_size, Image.Resampling.LANCZOS)
width, height = target_size
# 随机选择缺口的位置
gap_left = random.randint(block_size, width - block_size * 2)
gap_top = random.randint(0, height - block_size)
# 创建图像的副本以绘制缺口
background_with_gap = image.copy()
draw = ImageDraw.Draw(background_with_gap)
# 绘制一个透明的矩形以创建缺口
draw.rectangle((gap_left, gap_top, gap_left + block_size, gap_top + block_size), fill=(255, 255, 255, 0))
# 从原始图像中裁剪出滑块
slider_block = image.crop((gap_left, gap_top, gap_left + block_size, gap_top + block_size))
# 创建一个新的黑白遮罩图像
mask = Image.new("L", (block_size, block_size), 0)
draw_mask = ImageDraw.Draw(mask)
# 在遮罩图像上绘制白色矩形
draw_mask.rectangle((0, 0, block_size, block_size), fill=255)
# 对遮罩图像应用高斯模糊
mask = mask.filter(ImageFilter.GaussianBlur(3))
# 将遮罩应用于滑块图像
slider_block.putalpha(mask)
# 保存带缺口的背景图像
background_with_gap.save(f"{output_dir}/background_with_gap.png")
# 保存滑块图像
slider_block.save(f"{output_dir}/slider_block.png")
# 返回缺口的坐标
return ('gap_left:', gap_left, 'gap_top:', gap_top)
# 示例调用
gap_coordinates = create_slider_captcha("1.jpg", r"F:\Python_files\webhtml\output\/")
print(f"缺口坐标: {gap_coordinates}")
二.将图片加载到前端,这里的逻辑应该是将图片跟x坐标从服务器加载到前端的,但是重点是实现滑块,这里就改成本地加载了
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>滑块验证码</title>
<style>
/*滑块验证的css样式*/
.captcha-container {
position: relative;
width: 260px;
height: 130px;
border: 1px solid #ccc;
margin-bottom: 1px;
}
.captcha-image {
width: 100%;
height: 130px;
}
.slider-block {
position: absolute;
top: 0;
width: 40px;
height: 40px;
z-index: 2;
}
.slider-container {
width: 260px;
height: 20px;
background-color: #eee;
border-top: 1px solid #ccc;
display: flex;
align-items: center;
position: relative;
}
.slider-container::before {
content: '向右滑动完成验证';
position: absolute;
left: 0;
right: 0;
text-align: center;
font-size: 14px;
color: #0c0c0c;
}
.slider {
width: 40px;
height: 100%;
background-color: #0dddec;
cursor: pointer;
position: absolute;
z-index: 3;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
color: #000;
}
.slider::before {
content: '>';
}
</style>
</head>
<body>
<div class="captcha-container">
<img src="output/background_with_gap.png" class="captcha-image" id="captcha-image">
<img src="output/slider_block.png" class="slider-block" id="slider-block" style="left: 0;">
</div>
<div class="slider-container">
<div class="slider" id="slider" style="left: 0;"></div>
</div>
<script>
const slider = document.getElementById('slider');
const sliderBlock = document.getElementById('slider-block');
let isDragging = false;
let startX = 0;
const gapLeft = 176; // 将此值替换为你的缺口的实际横坐标
const gapTop = 64; // 将此值替换为你的缺口的实际纵坐标
sliderBlock.style.top = gapTop + 'px';
slider.addEventListener('mousedown', function (e) {
isDragging = true;
startX = e.clientX - parseInt(slider.style.left, 10); // 记录鼠标按下时的初始位置
});
document.addEventListener('mouseup', function () {
if (isDragging) {
isDragging = false;
const currentLeft = parseInt(slider.style.left, 10);
if (Math.abs(currentLeft - gapLeft) < 5) {
alert('验证通过');
} else {
alert('验证失败');
slider.style.left = '0px';
sliderBlock.style.left = '0px';
}
}
});
document.addEventListener('mousemove', function (e) {
if (isDragging) {
const moveX = e.clientX - startX;
if (moveX >= 0 && moveX <= (260 - 40)) {
slider.style.left = moveX + 'px';
sliderBlock.style.left = moveX + 'px';
}
}
});
</script>
</body>
</html>
本地实现,将下面这两行的背景图跟滑块图路劲改成你自己的
<img src="output/background_with_gap.png" class="captcha-image" id="captcha-image">
<img src="output/slider_block.png" class="slider-block" id="slider-block" style="left: 0;">
将这里的坐标替换成你自己生成的实际的坐标
const gapLeft = 176; // 将此值替换为你的缺口的实际横坐标
const gapTop = 64; // 将此值替换为你的缺口的实际纵坐标
如果要实现服务器验证也是一样的,创建个服务,然后从服务器获取图片跟x值,加载到上面的网页中即可,例如下面创建一个Ajax
// 滑块验证
let gapTop = 0;
function initializeCaptcha() {
// 使用 Ajax 请求从后端获取 Base64 编码的图片和缺口坐标
const xhr = new XMLHttpRequest();
xhr.open('GET', '/Slide_verification/', true); // 替换为你的后端 API 端点
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText)
const response = JSON.parse(xhr.responseText);
const backgroundBase64 = response.backgroundImage;
const sliderBlockBase64 = response.sliderBlockImage;
gapTop = response.gapTop; // 后端返回的缺口纵坐标
const captchaImage = document.getElementById('captcha-image');
const sliderBlock = document.getElementById('slider-block');
captchaImage.src = `data:image/png;base64,${backgroundBase64}`;
sliderBlock.src = `data:image/png;base64,${sliderBlockBase64}`;
sliderBlock.style.top = gapTop + 'px';
resetSlider();
}
};
xhr.send();
}
const slider = document.getElementById('slider');
const sliderBlock = document.getElementById('slider-block');
let isDragging = false;
let startX = 0;
function resetSlider() {
slider.style.left = '0px';
sliderBlock.style.left = '0px';
}
slider.addEventListener('mousedown', function (e) {
isDragging = true;
startX = e.clientX - parseInt(slider.style.left, 10); // 记录鼠标按下时的初始位置
});
document.addEventListener('mouseup', function () {
if (isDragging) {
isDragging = false;
const currentLeft = parseInt(slider.style.left, 10);
// 发送验证请求到后端
const xhr = new XMLHttpRequest();
xhr.open('POST', '/Slide_verification/', true); // 替换为你的后端 API 端点
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.setRequestHeader('X-CSRFToken', getCookie('csrftoken'));
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
if (response.success) {
alert('验证通过');
// 处理返回的token,比如存储在localStorage或sessionStorage
localStorage.setItem('sliderToken', response.token);
document.getElementById('error-message-1').textContent = response.message ;
} else {
alert('验证失败');
document.getElementById('error-message-1').textContent = response.message ;
initializeCaptcha(); // 重新加载图像和初始化
}
}
};
xhr.send('gapLeft=' + currentLeft);
}
});
document.addEventListener('mousemove', function (e) {
if (isDragging) {
const moveX = e.clientX - startX;
if (moveX >= 0 && moveX <= (260 - 40)) {
slider.style.left = moveX + 'px';
sliderBlock.style.left = moveX + 'px';
}
}
});
// 初始化验证码
initializeCaptcha();
演示地址:皮卡熊
下期我将讲解文字点选验证