前提
上一篇文章介绍了使用Template Matching定位到缺口位置,再模拟鼠标去滑动元素就可以了。
但是我遇到一个有趣的案例:
如这个视频所示,滑块和拼图的移动关系并非线性:先慢、再均速、最后快。但它们之间肯定存在某种数学关系。
采集数据
在浏览器上使用JS采集滑块和拼图两个元素对应的移动数据(style:left:xxpx):
const slider = document.querySelector('#captcha-sliding-slider');
const puzzle = document.querySelector('#captcha-puzzle');
let recordedData = [];
let sliderObserver = null;
function recordPositions() {
if (!slider || !puzzle) return;
const slider_x = parseFloat(slider.style.left) || 0;
const puzzle_x = parseFloat(puzzle.style.left) || 0;
recordedData.push({
slider_x: slider_x,
puzzle_x: puzzle_x,
});
}
document.addEventListener('mousedown', () => {
if (!slider) return;
recordedData = [];
sliderObserver = new MutationObserver(recordPositions);
sliderObserver.observe(slider, { attributes: true, attributeFilter: ['style'] });
});
document.addEventListener('mouseup', () => {
if (sliderObserver) {
sliderObserver.disconnect();
sliderObserver = null;
console.log(`记录完成: ${recordedData.length} 条`);
}
});
function exportToCSV() {
if (recordedData.length === 0) return;
const headers = ['Slider_Left(px)', 'Puzzle_Left(px)'];
const csvRows = [headers.join(',')];
recordedData.forEach(row => {
csvRows.push([
row.slider_x,
row.puzzle_x,
].join(','));
});
const blob = new Blob(['\ufeff' + csvRows.join('\n')], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = `positions_${Date.now()}.csv`;
link.click();
}
function viewData() {
console.table(recordedData);
}推算公式
使用AI帮我推算出公式:
def calculate_puzzle_x(slider_x):
"""从 slider_x 计算 puzzle_x(优化浮点精度)"""
# 使用 Horner 方法重写多项式,减少浮点误差
# 原式: -0.0000020625 + 0.0769232487*x + 0.0035502933*x² + 0.000000000006490*x³
# Horner: -0.0000020625 + x*(0.0769232487 + x*(0.0035502933 + x*(0.000000000006490)))
result = -0.0000020625 + slider_x * (0.0769232487 + slider_x * (0.0035502933 + slider_x * (0.000000000006490)))
return round(result, 2)生成逆公式
还是使用AI生成逆公式,根据 puzzle_x 距离计算出 slider_x 实际要拖动的距离:
def calculate_slider_x(puzzle_x, initial_guess=None):
"""
从 puzzle_x 计算 slider_x(逆函数 - 最高精度)
使用牛顿迭代法从精确公式反推,优化浮点精度
参数:
puzzle_x: 目标 puzzle_x 值
initial_guess: 初始猜测值(可选,默认使用 puzzle_x 作为初始值)
返回:
slider_x: 计算出的 slider_x 值
"""
# 初始猜测:使用 puzzle_x 作为起点(通常接近真实值)
x = initial_guess if initial_guess is not None else puzzle_x
# 牛顿迭代法求解方程:calculate_puzzle_x(x) - puzzle_x = 0
tolerance = 1e-12 # 提高收敛精度
max_iterations = 100
for i in range(max_iterations):
# f(x) = calculate_puzzle_x(x) - puzzle_x
fx = calculate_puzzle_x(x) - puzzle_x
if abs(fx) < tolerance:
return round(x, 2)
# f'(x) = 导数,使用 Horner 方法优化
# 原式: 0.0769232487 + 2*0.0035502933*x + 3*0.000000000006490*x²
# Horner: 0.0769232487 + x*(2*0.0035502933 + x*3*0.000000000006490)
fpx = 0.0769232487 + x * (0.0071005866 + x * (0.00000000001947))
if abs(fpx) < 1e-15: # 避免除以零
break
# 牛顿迭代:x_new = x - f(x)/f'(x)
x_new = x - fx / fpx
# 检查收敛(相对变化)
if abs(x_new - x) < tolerance * (1.0 + abs(x)):
return round(x_new, 2)
x = x_new
return round(x, 2)