【Python】ブラウザ自動化で『人間らしい』マウス操作を再現してbot検知を回避

Python

ブラウザ自動化において、機械的な直線移動は真っ先に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. このライブラリのポイント

  1. 3次ベジェ曲線: 直線ではなく、自然なカーブを描きます。

  2. 動的な制御点: 移動距離に合わせてカーブの大きさを自動調整します。

  3. 速度のイージング: np.sin を利用して、動き出しと停止直前をゆっくりにする「慣性」を再現しています。

  4. ゆらぎの追加: delays に微小なランダム性を加えることで、一定のFPSで動く機械っぽさを排除しています。

5. 実際の自動化ツールへの組み込み

実際のブラウザ操作(PlaywrightやSelenium)で使用する場合は、canvas.create_line の代わりに、各ライブラリのマウス移動関数(例: page.mouse.move(x, y))をループ内で呼び出すだけでOKです。

コメント

タイトルとURLをコピーしました