captcha_deal.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import base64
  2. import random
  3. import time
  4. from urllib.parse import quote
  5. import requests
  6. from DrissionPage import ChromiumPage, ChromiumOptions
  7. from PIL import Image
  8. token = "zPzmt1mG1ouCU6GTzsZN2Lmm8pdZypapPcLJTBRETco"
  9. proxyUrl = "117.26.226.31:40030"
  10. proxies = {
  11. "http": "http://" + proxyUrl,
  12. "https": "http://" + proxyUrl,
  13. }
  14. chrome_path = r"C:\Program Files\Google\Chrome\Application\chrome.exe"
  15. class CaptchaDeal(object):
  16. def __init__(self):
  17. self.page = self.init_chromepage()
  18. def init_chromepage(self):
  19. # 初始化ChromiumPage
  20. co = ChromiumOptions().auto_port().set_browser_path(chrome_path)
  21. co.headless(False) # 可视化模式便于调试
  22. # co.set_proxy("http://" + proxyUrl)
  23. page = ChromiumPage(co)
  24. return page
  25. def get_page(self):
  26. """解析验证码界面"""
  27. url = "https://cfe.m.jd.com/privatedomain/risk_handler/03101900/?returnurl=https%3A%2F%2Fsearch.jd.com%2FSearch%3Fkeyword%3D%25E5%25A4%25A9%25E5%25A3%25AB%25E5%258A%259B%25E5%2585%25BB%25E8%25A1%2580%25E6%25B8%2585%25E8%2584%2591%25E9%25A2%2597%25E7%25B2%25924g%26enc%3Dutf-8%26wq%3D%25E5%25A4%25A9%25E5%25A3%25AB%25E5%258A%259B%25E5%2585%25BB%25E8%25A1%2580%25E6%25B8%2585%25E8%2584%2591%25E9%25A2%2597%25E7%25B2%25924g&rqhost=https%3A%2F%2Fapi.m.jd.com&rpid=rp-112038530-10428-1774874729116&evtype=2&evapi=color_pc_search_searchWare&source=1&forceCurrentView=1&evsid=AASWz-Ccn-fmDu0QbI5tPvKhPINYZNJcz6vQ-TCMSXxv5jRuoOMT4SCV_hpjQ0UyoEjA5OF-"
  28. self.page.get(url)
  29. self.page.wait.doc_loaded()
  30. time.sleep(3)
  31. self.page.ele("xpath://div[@class='verifyBtn']").click()
  32. self.page.wait.doc_loaded()
  33. time.sleep(4)
  34. print("为滑块验证码")
  35. capt_ele = self.page.ele('xpath://*[@id="captcha_modal"]', timeout=2)
  36. capt_ele.get_screenshot('./element_screenshot.png')
  37. distance = self.verify(2)
  38. print(f"滑块距离:{distance}")
  39. slider_element = self.page.ele(
  40. "xpath://img[@class='move-img']")
  41. self.simulate_slider_drag(slider_element, float(distance))
  42. # 滑块验证处理
  43. time.sleep(5)
  44. print(self.page.url, self.page.title)
  45. def verify(self, type_num):
  46. """调用云码平台服务"""
  47. with open('element_screenshot.png', 'rb') as f:
  48. b = base64.b64encode(f.read()).decode()
  49. url = "http://api.jfbym.com/api/YmServer/customApi"
  50. if type_num == 1:
  51. # 坐标类型
  52. data = {
  53. "token": token,
  54. "type": "30332",
  55. "direction": "top",
  56. "click_num": 3,
  57. "image": b,
  58. }
  59. else:
  60. # 滑块类型
  61. data = {
  62. "token": token,
  63. "type": "22222",
  64. "image": b,
  65. }
  66. _headers = {
  67. "Content-Type": "application/json"
  68. }
  69. response = requests.request("POST", url, headers=_headers, json=data).json()
  70. print(response)
  71. return response["data"]["data"]
  72. def generate_human_track(self, distance):
  73. """
  74. 生成人类拖动的轨迹
  75. :param distance: 需要拖动的距离(像素)
  76. :return: 轨迹点列表,每个点包含(x偏移, y偏移, 延迟时间)
  77. """
  78. tracks = []
  79. current = 0
  80. mid = distance * 0.7 # 70%处开始减速
  81. t = 0.2
  82. v = 0
  83. move_points = []
  84. # 第一阶段:加速
  85. while current < mid:
  86. a = random.uniform(2, 4)
  87. v0 = v
  88. v = v0 + a * t
  89. move = v0 * t + 0.5 * a * t * t
  90. current += move
  91. move_points.append(move)
  92. # 第二阶段:减速
  93. while current < distance:
  94. a = -random.uniform(0.5, 1.5)
  95. v0 = v
  96. v = v0 + a * t
  97. if v < 0.5: # 防止速度过小
  98. v = 0.5
  99. move = v0 * t + 0.5 * a * t * t
  100. current += move
  101. move_points.append(move)
  102. # 添加随机性并生成最终轨迹
  103. total_points = len(move_points)
  104. for i, move in enumerate(move_points):
  105. x_offset = move
  106. # 添加垂直抖动(模拟手抖)
  107. if i % random.randint(2, 4) == 0:
  108. y_offset = random.randint(-2, 2)
  109. else:
  110. y_offset = 0
  111. # 时间间隔(模拟人类反应)
  112. if i < total_points * 0.3: # 开始阶段较快
  113. duration = random.uniform(0.01, 0.03)
  114. elif i > total_points * 0.7: # 结束阶段较慢
  115. duration = random.uniform(0.03, 0.08)
  116. else: # 中间阶段
  117. duration = random.uniform(0.02, 0.05)
  118. # 随机添加微小停顿
  119. if random.random() < 0.05:
  120. duration += random.uniform(0.05, 0.1)
  121. tracks.append((x_offset, y_offset, duration))
  122. # 最终微调:到达终点后轻微回拉
  123. if random.random() < 0.7:
  124. tracks.append((-random.randint(1, 3), 0, 0.05))
  125. return tracks
  126. def simulate_slider_drag(self, slider_element, target_distance):
  127. """
  128. 模拟人类拖动滑块
  129. """
  130. # 移动到滑块并按住
  131. self.page.actions.move_to(slider_element).hold()
  132. # 生成轨迹
  133. tracks = self.generate_human_track(target_distance)
  134. # 按轨迹拖动
  135. for track in tracks:
  136. offset_x, offset_y, duration = track
  137. self.page.actions.move(offset_x, offset_y, duration=duration / 1000)
  138. # 释放鼠标
  139. self.page.actions.release()
  140. def run(self):
  141. self.get_page()
  142. if __name__ == '__main__':
  143. captcga_deal = CaptchaDeal()
  144. captcga_deal.run()