ブラウザ自動化において、機械的な直線移動は真っ先にbotとして疑われる原因になります。本記事では、数学的な「ベジェ曲線」を活用して、人間特有の「ゆらぎ」を再現するコードを紹介します。
1. なぜ「人間らしさ」が必要なのか?
最新のbot検知システム(reCAPTCHA v3など)は、クリックの正確さだけでなく、「カーソルの軌跡」を分析しています。
-
直線移動: 数学的に正解すぎて不自然。
-
等速移動: 物理的な慣性がなく、プログラム特有の動き。
これらを回避するために、滑らかな曲線とランダムな加減速を実装します。
2. 実装のコア:3次ベジェ曲線
2つの点(始点と終点)の間に、2つの「制御点」をランダムに配置することで、毎回異なる自然なカーブを生成します。
3. すぐに使えるフルコード
以下のコードは、マウスの動きを可視化するデモ機能付きのクラスです。pyautoguiを利用して実際の操作に使うことも、tkinterで動きを確認することも可能です。
import numpy as np
import time
import random
import tkinter as tk
from tkinter import messagebox
class HumanMouseAutomation:
def __init__(self):
# 3次ベジェ曲線の計算
pass
def calculate_bezier_path(self, start_pos, end_pos, num_points=50):
"""
始点と終点の間にランダムな制御点を2つ生成し、ベジェ曲線の座標リストを返す
"""
p0 = np.array(start_pos)
p3 = np.array(end_pos)
# 距離に応じたオフセット範囲の計算
dist = np.linalg.norm(p3 - p0)
offset_range = dist * 0.3
# ランダムな制御点 P1, P2 の生成
p1 = p0 + (p3 - p0) * 0.3 + np.random.uniform(-offset_range, offset_range, 2)
p2 = p0 + (p3 - p0) * 0.7 + np.random.uniform(-offset_range, offset_range, 2)
# ベジェ曲線の生成
t = np.linspace(0, 1, num_points)
path = (
np.outer((1 - t)**3, p0) +
np.outer(3 * (1 - t)**2 * t, p1) +
np.outer(3 * (1 - t) * t**2, p2) +
np.outer(t**3, p3)
)
return path
# --- デモ表示用のGUIクラス ---
class MouseVisualizerApp:
def __init__(self, root):
self.root = root
self.root.title("人間らしいマウス移動デモ")
self.automation = HumanMouseAutomation()
# キャンバス設定
self.canvas = tk.Canvas(root, width=800, height=500, bg="#ffffff")
self.canvas.pack(pady=20)
# ステータス表示
self.info_label = tk.Label(root, text="「移動実行」を押すと、ランダムな軌跡を描画します", font=("MS Gothic", 10))
self.info_label.pack()
# ボタン
btn_frame = tk.Frame(root)
btn_frame.pack(pady=10)
tk.Button(btn_frame, text="人間らしい移動を実行", command=self.run_demo, bg="#4CAF50", fg="white", padx=10).pack(side=tk.LEFT, padx=5)
tk.Button(btn_frame, text="クリア", command=self.clear_canvas).pack(side=tk.LEFT, padx=5)
self.current_pos = (50, 450)
self.pointer = self.canvas.create_oval(45, 445, 55, 455, fill="red")
def clear_canvas(self):
self.canvas.delete("path")
def run_demo(self):
target_pos = (random.randint(100, 700), random.randint(50, 450))
# 座標リストの取得
steps = random.randint(40, 70)
path = self.automation.calculate_bezier_path(self.current_pos, target_pos, num_points=steps)
# イージング(動き出しと終わりをゆっくりに)
t_values = np.linspace(0, np.pi, steps)
delays = (np.sin(t_values) * 0.02) + 0.005 # 速度のゆらぎ
for i in range(len(path) - 1):
p1 = path[i]
p2 = path[i+1]
# 軌跡を描画
self.canvas.create_line(p1[0], p1[1], p2[0], p2[1], fill="blue", width=2, tags="path")
# ポインターを移動
self.canvas.coords(self.pointer, p2[0]-5, p2[1]-5, p2[0]+5, p2[1]+5)
self.root.update()
time.sleep(delays[i])
self.current_pos = target_pos
if __name__ == "__main__":
# 必要なライブラリのチェック
try:
import numpy
except ImportError:
print("Error: numpy がインストールされていません。'pip install numpy' を実行してください。")
else:
root = tk.Tk()
app = MouseVisualizerApp(root)
root.mainloop()
4. このライブラリのポイント
-
3次ベジェ曲線: 直線ではなく、自然なカーブを描きます。
-
動的な制御点: 移動距離に合わせてカーブの大きさを自動調整します。
-
速度のイージング:
np.sinを利用して、動き出しと停止直前をゆっくりにする「慣性」を再現しています。 -
ゆらぎの追加:
delaysに微小なランダム性を加えることで、一定のFPSで動く機械っぽさを排除しています。
5. 実際の自動化ツールへの組み込み
実際のブラウザ操作(PlaywrightやSelenium)で使用する場合は、canvas.create_line の代わりに、各ライブラリのマウス移動関数(例: page.mouse.move(x, y))をループ内で呼び出すだけでOKです。


コメント