mouse_helper.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. import time
  2. import random
  3. import math
  4. def get_cubic_bezier_point(t, p0, p1, p2, p3):
  5. x = (1-t)**3 * p0[0] + 3*(1-t)**2 * t * p1[0] + 3*(1-t) * t**2 * p2[0] + t**3 * p3[0]
  6. y = (1-t)**3 * p0[1] + 3*(1-t)**2 * t * p1[1] + 3*(1-t) * t**2 * p2[1] + t**3 * p3[1]
  7. return (x, y)
  8. def ease_out_quad(x):
  9. return 1 - (1 - x) * (1 - x)
  10. def generate_human_path(start_x, start_y, end_x, end_y, steps=30):
  11. path = []
  12. dist = math.hypot(end_x - start_x, end_y - start_y)
  13. offset = dist * 0.2
  14. p0 = (start_x, start_y)
  15. p3 = (end_x, end_y)
  16. p1 = (
  17. start_x + (end_x - start_x) * 0.3 + random.uniform(-offset, offset),
  18. start_y + (end_y - start_y) * 0.3 + random.uniform(-offset, offset)
  19. )
  20. p2 = (
  21. start_x + (end_x - start_x) * 0.7 + random.uniform(-offset, offset),
  22. start_y + (end_y - start_y) * 0.7 + random.uniform(-offset, offset)
  23. )
  24. for i in range(steps + 1):
  25. t = i / steps
  26. eased_t = ease_out_quad(t)
  27. point = get_cubic_bezier_point(eased_t, p0, p1, p2, p3)
  28. jitter = 1.5
  29. final_x = point[0] + random.uniform(-jitter, jitter)
  30. final_y = point[1] + random.uniform(-jitter, jitter)
  31. if i == steps:
  32. final_x, final_y = end_x, end_y
  33. path.append((final_x, final_y))
  34. return path
  35. class HumanMouse:
  36. def __init__(self, page):
  37. self.page = page
  38. self.curr_x = random.randint(100, 500)
  39. self.curr_y = random.randint(100, 500)
  40. self.page.run_cdp('Input.dispatchMouseEvent', **{
  41. 'type': 'mouseMoved',
  42. 'x': self.curr_x,
  43. 'y': self.curr_y
  44. })
  45. def _get_center(self, ele):
  46. """兼容性获取中心点"""
  47. rect = ele.rect
  48. try:
  49. tl_x, tl_y = rect.location
  50. width, height = rect.size
  51. except AttributeError:
  52. tl_x, tl_y, width, height = rect
  53. return tl_x + (width / 2), tl_y + (height / 2)
  54. def move_to(self, ele, duration=0.5):
  55. center_x, center_y = self._get_center(ele)
  56. # 目标稍微带点随机偏移
  57. target_x = center_x + random.uniform(-3, 3)
  58. target_y = center_y + random.uniform(-3, 3)
  59. if self.curr_x == 0 and self.curr_y == 0:
  60. self.curr_x = target_x - random.randint(300, 500)
  61. self.curr_y = target_y - random.randint(300, 500)
  62. self.page.run_cdp('Input.dispatchMouseEvent', **{
  63. 'type': 'mouseMoved',
  64. 'x': self.curr_x,
  65. 'y': self.curr_y
  66. })
  67. steps = int(duration * 60)
  68. if steps < 10: steps = 10
  69. points = generate_human_path(self.curr_x, self.curr_y, target_x, target_y, steps)
  70. for x, y in points:
  71. self.page.run_cdp('Input.dispatchMouseEvent', **{
  72. 'type': 'mouseMoved',
  73. 'x': x,
  74. 'y': y
  75. })
  76. self.curr_x = x
  77. self.curr_y = y
  78. time.sleep(duration / steps * random.uniform(0.8, 1.2))
  79. def scroll_to_visible(self, ele):
  80. viewport_height = self.page.run_js("return window.innerHeight")
  81. while True:
  82. # 使用 arguments[0] 修复 run_js 参数问题
  83. rect = self.page.run_js("""
  84. var rect = arguments[0].getBoundingClientRect();
  85. return {top: rect.top, bottom: rect.bottom, height: rect.height};
  86. """, ele)
  87. element_top = rect['top']
  88. element_bottom = rect['bottom']
  89. scroll_needed = False
  90. delta_y = 0
  91. # 增加一些缓冲区,不要滚得太极限
  92. if element_top > viewport_height * 0.7:
  93. scroll_needed = True
  94. delta_y = random.randint(100, 250)
  95. elif element_bottom < viewport_height * 0.3:
  96. scroll_needed = True
  97. delta_y = -random.randint(100, 250)
  98. if not scroll_needed:
  99. break
  100. self._dispatch_scroll(delta_y)
  101. time.sleep(random.uniform(0.1, 0.2))
  102. def _dispatch_scroll(self, delta_y):
  103. self.page.run_cdp('Input.dispatchMouseEvent', **{
  104. 'type': 'mouseWheel',
  105. 'x': self.curr_x,
  106. 'y': self.curr_y,
  107. 'deltaX': 0,
  108. 'deltaY': delta_y,
  109. 'modifiers': 0,
  110. 'pointerType': 'mouse'
  111. })