云码验证码识别_第一种验证码.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. 云码验证码识别 - 第一种验证码(顺序点击图标)
  5. 接口文档: https://www.jfbym.com/test/317.html
  6. """
  7. import base64
  8. import json
  9. import time
  10. import requests
  11. from selenium import webdriver
  12. from selenium.webdriver.common.by import By
  13. from selenium.webdriver.common.action_chains import ActionChains
  14. from selenium.webdriver.support.ui import WebDriverWait
  15. from selenium.webdriver.support import expected_conditions as EC
  16. class YunmaIconPointCaptcha:
  17. """云码顺序点击验证码识别器"""
  18. def __init__(self, token: str):
  19. """
  20. 初始化
  21. Args:
  22. token: 云码平台API Token
  23. """
  24. self.token = token
  25. self.api_url = "http://api.jfbym.com/api/YmServer/customApi"
  26. self.type_id = "317" # 顺序点击验证码接口ID
  27. def identify_type(self, driver) -> bool:
  28. """
  29. 识别是否为顺序点击验证码
  30. Args:
  31. driver: Selenium WebDriver实例
  32. Returns:
  33. bool: 是顺序点击验证码返回True
  34. """
  35. try:
  36. # 查找验证码容器
  37. captcha_container = driver.find_element(By.CLASS_NAME, "yidun")
  38. class_name = captcha_container.get_attribute("class")
  39. # 检查是否包含icon_point类名
  40. if "yidun--icon_point" in class_name:
  41. return True
  42. # 备选:检查提示文字
  43. try:
  44. tips_element = driver.find_element(By.CSS_SELECTOR, ".yidun_tips__text")
  45. tips_text = tips_element.text.strip()
  46. if tips_text.startswith("请依次点击"):
  47. return True
  48. except:
  49. pass
  50. return False
  51. except Exception as e:
  52. print(f"识别验证码类型失败: {e}")
  53. return False
  54. def extract_captcha_image(self, driver) -> str:
  55. """
  56. 提取验证码图片并转为Base64
  57. Args:
  58. driver: Selenium WebDriver实例
  59. Returns:
  60. str: Base64编码的图片数据
  61. """
  62. try:
  63. # 查找验证码背景图片
  64. img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
  65. # 获取图片src
  66. img_src = img_element.get_attribute("src")
  67. print(f"验证码图片地址: {img_src}")
  68. # 如果是base64格式
  69. if img_src.startswith("data:image"):
  70. # 提取base64数据部分
  71. base64_data = img_src.split(",")[1]
  72. return base64_data
  73. # 如果是URL,下载图片
  74. elif img_src.startswith("http"):
  75. response = requests.get(img_src)
  76. if response.status_code == 200:
  77. # 转为base64
  78. return base64.b64encode(response.content).decode()
  79. return None
  80. except Exception as e:
  81. print(f"提取验证码图片失败: {e}")
  82. return None
  83. def call_api(self, image_base64: str) -> dict:
  84. """
  85. 调用云码API识别验证码
  86. Args:
  87. image_base64: Base64编码的图片数据
  88. Returns:
  89. dict: API响应结果
  90. """
  91. payload = {
  92. "token": self.token,
  93. "type": self.type_id,
  94. "image": image_base64
  95. }
  96. headers = {
  97. "Content-Type": "application/json"
  98. }
  99. try:
  100. response = requests.post(
  101. self.api_url,
  102. json=payload,
  103. headers=headers,
  104. timeout=30
  105. )
  106. return response.json()
  107. except Exception as e:
  108. print(f"API调用失败: {e}")
  109. return {"code": -1, "msg": str(e)}
  110. def parse_result(self, api_result: dict) -> list:
  111. """
  112. 解析API返回结果
  113. Args:
  114. api_result: API响应结果
  115. Returns:
  116. list: 坐标列表 [[x1, y1], [x2, y2], ...]
  117. """
  118. try:
  119. if api_result.get("code") == 10000:
  120. # 识别成功
  121. data = api_result.get("data", [])
  122. if data:
  123. # 解析返回的坐标数据
  124. result_str = data[0].get("data", "")
  125. # 尝试解析为坐标列表
  126. if result_str.startswith("[[") and result_str.endswith("]]"):
  127. return json.loads(result_str)
  128. else:
  129. # 可能是其他格式,尝试eval
  130. try:
  131. return eval(result_str)
  132. except:
  133. print(f"无法解析返回数据: {result_str}")
  134. return []
  135. else:
  136. print(f"识别失败: {api_result.get('msg')}")
  137. return []
  138. except Exception as e:
  139. print(f"解析结果失败: {e}")
  140. return []
  141. def click_coordinates(self, driver, coordinates: list):
  142. """
  143. 执行点击操作
  144. Args:
  145. driver: Selenium WebDriver实例
  146. coordinates: 坐标列表
  147. """
  148. try:
  149. # 查找验证码图片元素
  150. img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
  151. location = img_element.location
  152. size = img_element.size
  153. # 创建Actions链
  154. actions = ActionChains(driver)
  155. for i, coord in enumerate(coordinates):
  156. x, y = coord
  157. # 移动到指定位置并点击
  158. actions.move_to_element_with_offset(img_element, x, y)
  159. actions.click()
  160. # 每次点击后等待一下
  161. if i < len(coordinates) - 1:
  162. actions.pause(0.3)
  163. actions.perform()
  164. print(f"完成点击,共点击 {len(coordinates)} 次")
  165. return True
  166. except Exception as e:
  167. print(f"点击操作失败: {e}")
  168. return False
  169. def solve(self, driver) -> bool:
  170. """
  171. 完整的验证码解决流程
  172. Args:
  173. driver: Selenium WebDriver实例
  174. Returns:
  175. bool: 解决成功返回True
  176. """
  177. print("=" * 50)
  178. print("开始处理顺序点击验证码(第一种)")
  179. print("=" * 50)
  180. # 1. 识别验证码类型
  181. if not self.identify_type(driver):
  182. print("❌ 不是顺序点击验证码")
  183. return False
  184. print("✅ 确认为顺序点击验证码")
  185. # 2. 提取验证码图片
  186. image_base64 = self.extract_captcha_image(driver)
  187. if not image_base64:
  188. print("❌ 提取验证码图片失败")
  189. return False
  190. print("✅ 验证码图片提取成功")
  191. # 3. 调用云码API
  192. print("🔄 正在调用云码API识别...")
  193. api_result = self.call_api(image_base64)
  194. if not api_result or api_result.get("code") != 10000:
  195. print(f"❌ API识别失败: {api_result}")
  196. return False
  197. print("✅ API识别成功")
  198. # 4. 解析结果
  199. coordinates = self.parse_result(api_result)
  200. if not coordinates:
  201. print("❌ 解析坐标失败")
  202. return False
  203. print(f"📍 获取到 {len(coordinates)} 个点击坐标: {coordinates}")
  204. # 5. 执行点击
  205. if self.click_coordinates(driver, coordinates):
  206. print("✅ 点击完成")
  207. return True
  208. else:
  209. print("❌ 点击失败")
  210. return False
  211. def main():
  212. """主函数示例"""
  213. # 配置
  214. TOKEN = "wVRyAIjI3hhJBicXndcs1ro9BN5UNMXIjW6SDWBFFyQ" # 云码Token
  215. # 创建识别器
  216. solver = YunmaIconPointCaptcha(TOKEN)
  217. # 使用示例
  218. driver = webdriver.Chrome()
  219. driver.get("https://buff.163.com")
  220. try:
  221. # 等待验证码出现
  222. WebDriverWait(driver, 10).until(
  223. EC.presence_of_element_located((By.CLASS_NAME, "yidun"))
  224. )
  225. # 等待验证码完全加载
  226. time.sleep(2)
  227. # 解决验证码
  228. if solver.solve(driver):
  229. print("🎉 验证码解决成功!")
  230. else:
  231. print("❌ 验证码解决失败")
  232. except Exception as e:
  233. print(f"错误: {e}")
  234. finally:
  235. input("按Enter键关闭浏览器...")
  236. driver.quit()
  237. if __name__ == "__main__":
  238. main()