DEV Community

drake
drake

Posted on

模拟动作链,绕过Cloudflare的点击人机验证

import pyautogui
import random
import time
import math
import numpy as np

"""
This script demonstrates techniques to make pyautogui automation more human-like
to potentially reduce detection by Cloudflare and similar bot detection systems.
"""

# Disable pyautogui's built-in safety feature for this example
pyautogui.FAILSAFE = True

# Get screen size for reference
screen_width, screen_height = pyautogui.size()


def human_like_mouse_movement(start_x, start_y, end_x, end_y, duration=None):
    """
    Move the mouse in a human-like manner between two points.
    Uses Bezier curves to create natural movement arcs.
    """
    # Calculate distance between points
    distance = math.sqrt((end_x - start_x) ** 2 + (end_y - start_y) ** 2)

    # Set a reasonable duration based on distance if not specified
    if duration is None:
        # Longer distance = longer time, but not linear
        duration = 0.6 + (distance / 500) * random.uniform(0.5, 1.5)

    # Add slight randomness to end coordinates (humans aren't pixel-perfect)
    end_x += random.randint(-2, 2)
    end_y += random.randint(-2, 2)

    # Create a Bezier curve for more natural movement
    # More control points = more complex and potentially more natural curve
    control_points = []

    # Start point
    control_points.append((start_x, start_y))

    # Add 1-3 control points for complexity
    num_control_points = random.randint(1, 3)
    for i in range(num_control_points):
        # Create control points that deviate from the straight line
        # The longer the distance, the more deviation we allow
        deviation = distance * 0.1 * random.uniform(0.5, 1.5)

        # Calculate a point along the direct path
        ratio = (i + 1) / (num_control_points + 1)
        point_x = start_x + (end_x - start_x) * ratio
        point_y = start_y + (end_y - start_y) * ratio

        # Add some randomized deviation
        point_x += random.uniform(-deviation, deviation)
        point_y += random.uniform(-deviation, deviation)

        control_points.append((point_x, point_y))

    # End point
    control_points.append((end_x, end_y))

    # Generate points along the Bezier curve
    steps = int(duration * 60)  # 60 points per second for smooth movement
    points = bezier_curve(control_points, steps)

    # Move the mouse along the curve with variable speed
    last_time = time.time()
    for i, (x, y) in enumerate(points):
        # Calculate progress ratio (0 to 1)
        progress = i / (steps - 1)

        # Apply easing function for natural acceleration/deceleration
        # Ease in at the beginning, ease out at the end
        if progress < 0.2:
            # Ease in - gradually accelerate
            speed_factor = progress / 0.2
        elif progress > 0.8:
            # Ease out - gradually decelerate
            speed_factor = (1 - progress) / 0.2
        else:
            # Maintain relatively consistent speed in the middle
            speed_factor = 1.0

        # Add slight random variations to speed
        speed_factor *= random.uniform(0.9, 1.1)
        print(f'duration: {duration} steps: {steps} speed_factor: {speed_factor}')
        if speed_factor == 0.0:
            continue
        # Calculate the ideal time we should be at this point
        ideal_time = last_time + (duration / steps) / speed_factor
        # Wait until we reach the ideal time
        current_time = time.time()
        if current_time < ideal_time:
            time.sleep(ideal_time - current_time)

        # Move to the point
        pyautogui.moveTo(int(x), int(y))
        last_time = time.time()


def bezier_curve(control_points, steps):
    """
    Generate a Bezier curve based on control points.
    Returns a list of points along the curve.
    """
    n = len(control_points) - 1
    points = []

    for t in np.linspace(0, 1, steps):
        point = [0, 0]
        for i in range(n + 1):
            binomial = math.comb(n, i)
            point[0] += binomial * (1 - t) ** (n - i) * t ** i * control_points[i][0]
            point[1] += binomial * (1 - t) ** (n - i) * t ** i * control_points[i][1]
        points.append(point)

    return points


def human_like_click(x=None, y=None, button='left'):
    """
    Perform a human-like click operation.
    If x and y are not provided, clicks at current position.
    """
    # If coordinates are provided, move to that position first
    if x is not None and y is not None:
        current_x, current_y = pyautogui.position()
        human_like_mouse_movement(current_x, current_y, x, y)

    # Add a small delay before clicking (humans pause before clicking)
    time.sleep(random.uniform(0.05, 0.15))

    # Click with variable duration
    click_duration = random.uniform(0.05, 0.15)
    pyautogui.mouseDown(button=button)
    time.sleep(click_duration)
    pyautogui.mouseUp(button=button)

    # Add a small delay after clicking (humans pause after clicking)
    time.sleep(random.uniform(0.05, 0.1))


def human_like_scroll(clicks, direction='down'):
    """
    Scroll in a human-like manner.
    """
    # Humans don't scroll at a constant speed
    for _ in range(clicks):
        # Variable scroll amount
        scroll_amount = random.randint(1, 5)

        if direction == 'down':
            pyautogui.scroll(-scroll_amount)
        else:
            pyautogui.scroll(scroll_amount)

        # Random pause between scrolls
        time.sleep(random.uniform(0.05, 0.3))


def human_like_type(text, interval=None):
    """
    Type text with variable speed like a human.
    """
    for char in text:
        # Humans type at variable speeds
        if interval is None:
            # Different characters take different times to type
            if char in '.,?!':
                # Longer pauses for punctuation
                typing_speed = random.uniform(0.1, 0.3)
            elif char == ' ':
                # Spaces are typically faster
                typing_speed = random.uniform(0.05, 0.15)
            else:
                # Normal characters
                typing_speed = random.uniform(0.05, 0.2)
        else:
            # Use provided interval with some randomness
            typing_speed = interval * random.uniform(0.8, 1.2)

        pyautogui.typewrite(char)
        time.sleep(typing_speed)

        # Occasionally add a longer pause (as if thinking)
        if random.random() < 0.02:
            time.sleep(random.uniform(0.5, 1.0))


def add_natural_pauses():
    """
    Add a natural pause as if the user is reading or thinking.
    Call this function between logical actions.
    """
    # Determine pause type (short, medium, long)
    pause_type = random.choices(
        ['short', 'medium', 'long', 'very_long'],
        weights=[0.6, 0.3, 0.08, 0.02],
        k=1
    )[0]

    if pause_type == 'short':
        time.sleep(random.uniform(0.5, 1.5))
    elif pause_type == 'medium':
        time.sleep(random.uniform(1.5, 3.0))
    elif pause_type == 'long':
        time.sleep(random.uniform(3.0, 6.0))
    else:  # very_long
        time.sleep(random.uniform(6.0, 10.0))


def random_mouse_movement():
    """
    Occasionally move the mouse randomly as humans do when thinking or reading.
    """
    current_x, current_y = pyautogui.position()

    # Small random movement around current position
    offset_x = random.randint(-100, 100)
    offset_y = random.randint(-100, 100)

    # Ensure we stay within screen bounds
    target_x = max(0, min(screen_width - 1, current_x + offset_x))
    target_y = max(0, min(screen_height - 1, current_y + offset_y))

    # Move to the random position
    human_like_mouse_movement(current_x, current_y, target_x, target_y)


# Example usage
if __name__ == "__main__":
    # Give user time to switch to the target window
    print("Switch to your target window. Starting in 3 seconds...")
    time.sleep(3)

    # Example: Navigate to a website and interact with it
    def example_automation():
        # Simulate opening a new tab (Ctrl+T)
        pyautogui.hotkey('ctrl', 't')
        add_natural_pauses()

        # Type a URL
        # human_like_type("https://visa.vfsglobal.com/chn/zh/can/login")
        # add_natural_pauses()

        # Press Enter
        # pyautogui.press('enter')

        # Wait for page to load with natural variation
        time.sleep(random.uniform(2.0, 3.5))

        # Scroll down a bit
        human_like_scroll(random.randint(3, 7))
        add_natural_pauses()

        # Move to a random position as if reading
        # random_mouse_movement()
        # add_natural_pauses()

        # Scroll more
        # human_like_scroll(random.randint(2, 5))
        # add_natural_pauses()

        # Click somewhere (simulating clicking a link)
        current_x, current_y = pyautogui.position()
        # 695 508
        print(current_x, current_y)
        target_x = current_x + random.randint(-100, 100)
        target_y = current_y + random.randint(-50, 50)
        # 模拟点击cloud flare
        human_like_click(695, 508)

    # Uncomment to run the example
    example_automation()

    print("Example complete. You can modify this script for your specific needs.")


Enter fullscreen mode Exit fullscreen mode

Top comments (0)