zlwu 8 ヶ月 前
親
コミット
713fc538e9
44 ファイル変更、6352 行追加、0 行削除
  1. BIN
      .DS_Store
  2. 0 0
      .history/buff平台两种验证码/第一种验证码02_20260204161142.html
  3. 0 0
      .history/buff平台两种验证码/第一种验证码02_20260204161204.html
  4. 0 0
      .history/buff平台两种验证码/第一种验证码03_20260204161214.html
  5. 0 0
      .history/buff平台两种验证码/第一种验证码03_20260204161236.html
  6. 0 0
      .history/buff平台两种验证码/第一种验证码_20260204155059.html
  7. 0 0
      .history/buff平台两种验证码/第一种验证码_20260204155129.html
  8. 0 0
      .history/buff平台两种验证码/第一种验证码云码接口说明_20260204163007.md
  9. 57 0
      .history/buff平台两种验证码/第一种验证码云码接口说明_20260204163029.md
  10. 0 0
      .history/buff平台两种验证码/第二种验证码02_20260204161321.html
  11. 0 0
      .history/buff平台两种验证码/第二种验证码02_20260204161343.html
  12. 0 0
      .history/buff平台两种验证码/第二种验证码03_20260204161351.html
  13. 0 0
      .history/buff平台两种验证码/第二种验证码03_20260204161413.html
  14. 0 0
      .history/buff平台两种验证码/第二种验证码_20260204155119.html
  15. 0 0
      .history/buff平台两种验证码/第二种验证码_20260204155149.html
  16. 0 0
      .history/buff平台两种验证码/第二种验证码云码接口说明_20260204162910.md
  17. 50 0
      .history/buff平台两种验证码/第二种验证码云码接口说明_20260204162932.md
  18. 304 0
      .history/云码验证码识别_第一种验证码_20260204162956.py
  19. 304 0
      .history/云码验证码识别_第一种验证码_20260204163229.py
  20. 304 0
      .history/云码验证码识别_第一种验证码_20260204163330.py
  21. 304 0
      .history/云码验证码识别_第一种验证码_20260204163407.py
  22. 337 0
      .history/云码验证码识别_综合版_20260204163331.py
  23. 337 0
      .history/云码验证码识别_综合版_20260204163352.py
  24. 341 0
      .history/云码验证码识别_综合版_20260204163449.py
  25. 482 0
      .history/云码验证码识别_综合版_简化版_20260204165631.py
  26. 482 0
      .history/云码验证码识别_综合版_简化版_20260204170044.py
  27. 482 0
      .history/云码验证码识别_综合版_简化版_20260204170143.py
  28. 512 0
      .history/云码验证码识别_综合版_简化版_20260204170903.py
  29. BIN
      SendMsg/.DS_Store
  30. BIN
      buff/.DS_Store
  31. BIN
      buff/BUFF/.DS_Store
  32. 0 0
      buff平台两种验证码/第一种验证码01.html
  33. 0 0
      buff平台两种验证码/第一种验证码02.html
  34. 0 0
      buff平台两种验证码/第一种验证码03.html
  35. 57 0
      buff平台两种验证码/第一种验证码云码接口说明.md
  36. 0 0
      buff平台两种验证码/第二种验证码01.html
  37. 0 0
      buff平台两种验证码/第二种验证码02.html
  38. 0 0
      buff平台两种验证码/第二种验证码03.html
  39. 50 0
      buff平台两种验证码/第二种验证码云码接口说明.md
  40. 304 0
      云码验证码识别_第一种验证码.py
  41. 329 0
      云码验证码识别_第二种验证码.py
  42. 341 0
      云码验证码识别_综合版.py
  43. 419 0
      云码验证码识别_综合版_Playwright.py
  44. 556 0
      云码验证码识别_综合版_简化版.py

BIN
.DS_Store


+ 0 - 0
.history/buff平台两种验证码/第一种验证码02_20260204161142.html


ファイルの差分が大きいため隠しています
+ 0 - 0
.history/buff平台两种验证码/第一种验证码02_20260204161204.html


+ 0 - 0
.history/buff平台两种验证码/第一种验证码03_20260204161214.html


ファイルの差分が大きいため隠しています
+ 0 - 0
.history/buff平台两种验证码/第一种验证码03_20260204161236.html


+ 0 - 0
.history/buff平台两种验证码/第一种验证码_20260204155059.html


ファイルの差分が大きいため隠しています
+ 0 - 0
.history/buff平台两种验证码/第一种验证码_20260204155129.html


+ 0 - 0
.history/buff平台两种验证码/第一种验证码云码接口说明_20260204163007.md


+ 57 - 0
.history/buff平台两种验证码/第一种验证码云码接口说明_20260204163029.md

@@ -0,0 +1,57 @@
+价格: 0.01元/次
+本接口只支持3小时内报错!
+类型: 通用图标点选类型
+
+说明:
+
+1.本接口是通用图标点选接口,只能传一张图(截图或者拼接图),图上需要有如何点击的信息;
+
+2.本接口有个必填参数direction,如果待识别图上告知如何点击的信息在图片上部,参数值为top,在下则值为bottom;
+
+3.图上最好不要有其他的无关干扰,只需要附上如何点击的顺序图标即可。
+
+4.click_num参数是可选参数,指的是,需要返回的坐标个数。
+
+只支持HTTP中的Post方法,同时Content-Type请设置为application/json或者application/x-www-form-urlencoded
+
+跨平台http接口,请仔细查看各类型的http接口,仅支持post传参
+
+http api 接口 文档
+
+1.1.请求地址
+
+http://api.jfbym.com/api/YmServer/customApi
+
+1.2.参数列表
+
+参数名称	传值类型	说明	是否必须
+image	string	需要识别图片的base64字符串	是
+direction	string	bottom/top(提示信息相对位置,)	是
+click_num	string	需要返回坐标数,非必填	是
+token	string	用户中心Token	是
+type	string	30332	是
+ 
+
+2.1.反参
+ 
+
+参数名称	传值类型	说明
+code	int	状态值
+msg	string	请求说明
+data	array	打码数据
+--code	int	打码服务状态
+--data	string	识别结果
+--time	string	打码服务时长
+2.2.code列表
+code	说明
+10000	识别成功
+10001	参数错误
+10002	余额不足
+10003	无此访问权限(未获取到有效token)
+10004	无此验证类型(type参数值不对)
+10005	网络拥塞
+10006	数据包过载
+10007	服务繁忙(接口模型无法处理图片参数)
+10008	网络错误,请稍后重试
+10009	结果准备中,请稍后再试
+10010	请求结束

+ 0 - 0
.history/buff平台两种验证码/第二种验证码02_20260204161321.html


ファイルの差分が大きいため隠しています
+ 0 - 0
.history/buff平台两种验证码/第二种验证码02_20260204161343.html


+ 0 - 0
.history/buff平台两种验证码/第二种验证码03_20260204161351.html


ファイルの差分が大きいため隠しています
+ 0 - 0
.history/buff平台两种验证码/第二种验证码03_20260204161413.html


+ 0 - 0
.history/buff平台两种验证码/第二种验证码_20260204155119.html


ファイルの差分が大きいため隠しています
+ 0 - 0
.history/buff平台两种验证码/第二种验证码_20260204155149.html


+ 0 - 0
.history/buff平台两种验证码/第二种验证码云码接口说明_20260204162910.md


+ 50 - 0
.history/buff平台两种验证码/第二种验证码云码接口说明_20260204162932.md

@@ -0,0 +1,50 @@
+价格: 0.012元/次
+本接口只支持3小时内报错!
+注意:
+
+类型: 定制-空间推理点选1(xd,原图,extra="请点击xxx")
+
+说明:带有定制前缀的,是搜集该类图片训练的机器学习模型;请用户检查自己的打码图是否和图例类似,  测试无误后可使用.
+
+1.如遇到服务繁忙,大概率是图片base64的问题,请看常见问题里的:关于base64格式图片的问题;如果确认图片base64无问题,请联系工作人员解决.
+
+2.如遇到无权限访问,用户中心秘钥重置下,再填到代码里的token试试;如果还是不行,请联系工作人员解决.
+
+只支持HTTP中的Post方法,同时Content-Type请设置为application/json或者application/x-www-form-urlencoded
+
+跨平台http接口,请仔细查看各类型的http接口,仅支持post传参
+
+http api 接口 文档
+
+1.1.请求地址
+
+http://api.jfbym.com/api/YmServer/customApi
+
+1.2.参数列表
+
+参数名称	传值类型	说明	是否必须
+image	string	需要识别图片的base64字符串	是
+extra	string	描述内容	是
+token	string	用户中心Token	是
+type	string	30109	是
+2.1.反参
+参数名称	传值类型	说明
+code	int	状态值
+msg	string	请求说明
+data	array	打码数据
+--code	int	打码服务状态
+--data	string	数英计算:答案; 滑块:像素距离; 旋转:角度; 坐标类型:按顺序坐标...
+--time	string	打码服务时长
+2.2.code列表
+code	说明
+10000	识别成功
+10001	参数错误
+10002	余额不足
+10003	无此访问权限
+10004	无此验证类型
+10005	网络拥塞
+10006	数据包过载
+10007	服务繁忙
+10008	网络错误,请稍后重试
+10009	结果准备中,请稍后再试
+10010	请求结束

+ 304 - 0
.history/云码验证码识别_第一种验证码_20260204162956.py

@@ -0,0 +1,304 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+云码验证码识别 - 第一种验证码(顺序点击图标)
+接口文档: https://www.jfbym.com/test/317.html
+"""
+
+import base64
+import json
+import time
+import requests
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.common.action_chains import ActionChains
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+
+
+class YunmaIconPointCaptcha:
+    """云码顺序点击验证码识别器"""
+
+    def __init__(self, token: str):
+        """
+        初始化
+        
+        Args:
+            token: 云码平台API Token
+        """
+        self.token = token
+        self.api_url = "http://api.jfbym.com/api/YmServer/customApi"
+        self.type_id = "317"  # 顺序点击验证码接口ID
+
+    def identify_type(self, driver) -> bool:
+        """
+        识别是否为顺序点击验证码
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            bool: 是顺序点击验证码返回True
+        """
+        try:
+            # 查找验证码容器
+            captcha_container = driver.find_element(By.CLASS_NAME, "yidun")
+            class_name = captcha_container.get_attribute("class")
+            
+            # 检查是否包含icon_point类名
+            if "yidun--icon_point" in class_name:
+                return True
+            
+            # 备选:检查提示文字
+            try:
+                tips_element = driver.find_element(By.CSS_SELECTOR, ".yidun_tips__text")
+                tips_text = tips_element.text.strip()
+                if tips_text.startswith("请依次点击"):
+                    return True
+            except:
+                pass
+            
+            return False
+            
+        except Exception as e:
+            print(f"识别验证码类型失败: {e}")
+            return False
+
+    def extract_captcha_image(self, driver) -> str:
+        """
+        提取验证码图片并转为Base64
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            str: Base64编码的图片数据
+        """
+        try:
+            # 查找验证码背景图片
+            img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
+            
+            # 获取图片src
+            img_src = img_element.get_attribute("src")
+            print(f"验证码图片地址: {img_src}")
+            
+            # 如果是base64格式
+            if img_src.startswith("data:image"):
+                # 提取base64数据部分
+                base64_data = img_src.split(",")[1]
+                return base64_data
+            
+            # 如果是URL,下载图片
+            elif img_src.startswith("http"):
+                response = requests.get(img_src)
+                if response.status_code == 200:
+                    # 转为base64
+                    return base64.b64encode(response.content).decode()
+            
+            return None
+            
+        except Exception as e:
+            print(f"提取验证码图片失败: {e}")
+            return None
+
+    def call_api(self, image_base64: str) -> dict:
+        """
+        调用云码API识别验证码
+        
+        Args:
+            image_base64: Base64编码的图片数据
+            
+        Returns:
+            dict: API响应结果
+        """
+        payload = {
+            "token": self.token,
+            "type": self.type_id,
+            "image": image_base64
+        }
+        
+        headers = {
+            "Content-Type": "application/json"
+        }
+        
+        try:
+            response = requests.post(
+                self.api_url, 
+                json=payload, 
+                headers=headers,
+                timeout=30
+            )
+            
+            return response.json()
+            
+        except Exception as e:
+            print(f"API调用失败: {e}")
+            return {"code": -1, "msg": str(e)}
+
+    def parse_result(self, api_result: dict) -> list:
+        """
+        解析API返回结果
+        
+        Args:
+            api_result: API响应结果
+            
+        Returns:
+            list: 坐标列表 [[x1, y1], [x2, y2], ...]
+        """
+        try:
+            if api_result.get("code") == 10000:
+                # 识别成功
+                data = api_result.get("data", [])
+                if data:
+                    # 解析返回的坐标数据
+                    result_str = data[0].get("data", "")
+                    
+                    # 尝试解析为坐标列表
+                    if result_str.startswith("[[") and result_str.endswith("]]"):
+                        return json.loads(result_str)
+                    else:
+                        # 可能是其他格式,尝试eval
+                        try:
+                            return eval(result_str)
+                        except:
+                            print(f"无法解析返回数据: {result_str}")
+                            return []
+                
+            else:
+                print(f"识别失败: {api_result.get('msg')}")
+                return []
+                
+        except Exception as e:
+            print(f"解析结果失败: {e}")
+            return []
+
+    def click_coordinates(self, driver, coordinates: list):
+        """
+        执行点击操作
+        
+        Args:
+            driver: Selenium WebDriver实例
+            coordinates: 坐标列表
+        """
+        try:
+            # 查找验证码图片元素
+            img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
+            location = img_element.location
+            size = img_element.size
+            
+            # 创建Actions链
+            actions = ActionChains(driver)
+            
+            for i, coord in enumerate(coordinates):
+                x, y = coord
+                
+                # 移动到指定位置并点击
+                actions.move_to_element_with_offset(img_element, x, y)
+                actions.click()
+                
+                # 每次点击后等待一下
+                if i < len(coordinates) - 1:
+                    actions.pause(0.3)
+            
+            actions.perform()
+            print(f"完成点击,共点击 {len(coordinates)} 次")
+            
+            return True
+            
+        except Exception as e:
+            print(f"点击操作失败: {e}")
+            return False
+
+    def solve(self, driver) -> bool:
+        """
+        完整的验证码解决流程
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            bool: 解决成功返回True
+        """
+        print("=" * 50)
+        print("开始处理顺序点击验证码(第一种)")
+        print("=" * 50)
+        
+        # 1. 识别验证码类型
+        if not self.identify_type(driver):
+            print("❌ 不是顺序点击验证码")
+            return False
+        
+        print("✅ 确认为顺序点击验证码")
+        
+        # 2. 提取验证码图片
+        image_base64 = self.extract_captcha_image(driver)
+        if not image_base64:
+            print("❌ 提取验证码图片失败")
+            return False
+        
+        print("✅ 验证码图片提取成功")
+        
+        # 3. 调用云码API
+        print("🔄 正在调用云码API识别...")
+        api_result = self.call_api(image_base64)
+        
+        if not api_result or api_result.get("code") != 10000:
+            print(f"❌ API识别失败: {api_result}")
+            return False
+        
+        print("✅ API识别成功")
+        
+        # 4. 解析结果
+        coordinates = self.parse_result(api_result)
+        if not coordinates:
+            print("❌ 解析坐标失败")
+            return False
+        
+        print(f"📍 获取到 {len(coordinates)} 个点击坐标: {coordinates}")
+        
+        # 5. 执行点击
+        if self.click_coordinates(driver, coordinates):
+            print("✅ 点击完成")
+            return True
+        else:
+            print("❌ 点击失败")
+            return False
+
+
+def main():
+    """主函数示例"""
+    # 配置
+    TOKEN = "YOUR_YUNMA_TOKEN_HERE"  # 替换为你的云码Token
+    
+    # 创建识别器
+    solver = YunmaIconPointCaptcha(TOKEN)
+    
+    # 使用示例
+    driver = webdriver.Chrome()
+    driver.get("https://buff.163.com")
+    
+    try:
+        # 等待验证码出现
+        WebDriverWait(driver, 10).until(
+            EC.presence_of_element_located((By.CLASS_NAME, "yidun"))
+        )
+        
+        # 等待验证码完全加载
+        time.sleep(2)
+        
+        # 解决验证码
+        if solver.solve(driver):
+            print("🎉 验证码解决成功!")
+        else:
+            print("❌ 验证码解决失败")
+            
+    except Exception as e:
+        print(f"错误: {e}")
+        
+    finally:
+        input("按Enter键关闭浏览器...")
+        driver.quit()
+
+
+if __name__ == "__main__":
+    main()

+ 304 - 0
.history/云码验证码识别_第一种验证码_20260204163229.py

@@ -0,0 +1,304 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+云码验证码识别 - 第一种验证码(顺序点击图标)
+接口文档: https://www.jfbym.com/test/317.html
+"""
+
+import base64
+import json
+import time
+import requests
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.common.action_chains import ActionChains
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+
+
+class YunmaIconPointCaptcha:
+    """云码顺序点击验证码识别器"""
+
+    def __init__(self, token: str):
+        """
+        初始化
+        
+        Args:
+            token: 云码平台API Token
+        """
+        self.token = token
+        self.api_url = "http://api.jfbym.com/api/YmServer/customApi"
+        self.type_id = "317"  # 顺序点击验证码接口ID
+
+    def identify_type(self, driver) -> bool:
+        """
+        识别是否为顺序点击验证码
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            bool: 是顺序点击验证码返回True
+        """
+        try:
+            # 查找验证码容器
+            captcha_container = driver.find_element(By.CLASS_NAME, "yidun")
+            class_name = captcha_container.get_attribute("class")
+            
+            # 检查是否包含icon_point类名
+            if "yidun--icon_point" in class_name:
+                return True
+            
+            # 备选:检查提示文字
+            try:
+                tips_element = driver.find_element(By.CSS_SELECTOR, ".yidun_tips__text")
+                tips_text = tips_element.text.strip()
+                if tips_text.startswith("请依次点击"):
+                    return True
+            except:
+                pass
+            
+            return False
+            
+        except Exception as e:
+            print(f"识别验证码类型失败: {e}")
+            return False
+
+    def extract_captcha_image(self, driver) -> str:
+        """
+        提取验证码图片并转为Base64
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            str: Base64编码的图片数据
+        """
+        try:
+            # 查找验证码背景图片
+            img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
+            
+            # 获取图片src
+            img_src = img_element.get_attribute("src")
+            print(f"验证码图片地址: {img_src}")
+            
+            # 如果是base64格式
+            if img_src.startswith("data:image"):
+                # 提取base64数据部分
+                base64_data = img_src.split(",")[1]
+                return base64_data
+            
+            # 如果是URL,下载图片
+            elif img_src.startswith("http"):
+                response = requests.get(img_src)
+                if response.status_code == 200:
+                    # 转为base64
+                    return base64.b64encode(response.content).decode()
+            
+            return None
+            
+        except Exception as e:
+            print(f"提取验证码图片失败: {e}")
+            return None
+
+    def call_api(self, image_base64: str) -> dict:
+        """
+        调用云码API识别验证码
+        
+        Args:
+            image_base64: Base64编码的图片数据
+            
+        Returns:
+            dict: API响应结果
+        """
+        payload = {
+            "token": self.token,
+            "type": self.type_id,
+            "image": image_base64
+        }
+        
+        headers = {
+            "Content-Type": "application/json"
+        }
+        
+        try:
+            response = requests.post(
+                self.api_url, 
+                json=payload, 
+                headers=headers,
+                timeout=30
+            )
+            
+            return response.json()
+            
+        except Exception as e:
+            print(f"API调用失败: {e}")
+            return {"code": -1, "msg": str(e)}
+
+    def parse_result(self, api_result: dict) -> list:
+        """
+        解析API返回结果
+        
+        Args:
+            api_result: API响应结果
+            
+        Returns:
+            list: 坐标列表 [[x1, y1], [x2, y2], ...]
+        """
+        try:
+            if api_result.get("code") == 10000:
+                # 识别成功
+                data = api_result.get("data", [])
+                if data:
+                    # 解析返回的坐标数据
+                    result_str = data[0].get("data", "")
+                    
+                    # 尝试解析为坐标列表
+                    if result_str.startswith("[[") and result_str.endswith("]]"):
+                        return json.loads(result_str)
+                    else:
+                        # 可能是其他格式,尝试eval
+                        try:
+                            return eval(result_str)
+                        except:
+                            print(f"无法解析返回数据: {result_str}")
+                            return []
+                
+            else:
+                print(f"识别失败: {api_result.get('msg')}")
+                return []
+                
+        except Exception as e:
+            print(f"解析结果失败: {e}")
+            return []
+
+    def click_coordinates(self, driver, coordinates: list):
+        """
+        执行点击操作
+        
+        Args:
+            driver: Selenium WebDriver实例
+            coordinates: 坐标列表
+        """
+        try:
+            # 查找验证码图片元素
+            img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
+            location = img_element.location
+            size = img_element.size
+            
+            # 创建Actions链
+            actions = ActionChains(driver)
+            
+            for i, coord in enumerate(coordinates):
+                x, y = coord
+                
+                # 移动到指定位置并点击
+                actions.move_to_element_with_offset(img_element, x, y)
+                actions.click()
+                
+                # 每次点击后等待一下
+                if i < len(coordinates) - 1:
+                    actions.pause(0.3)
+            
+            actions.perform()
+            print(f"完成点击,共点击 {len(coordinates)} 次")
+            
+            return True
+            
+        except Exception as e:
+            print(f"点击操作失败: {e}")
+            return False
+
+    def solve(self, driver) -> bool:
+        """
+        完整的验证码解决流程
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            bool: 解决成功返回True
+        """
+        print("=" * 50)
+        print("开始处理顺序点击验证码(第一种)")
+        print("=" * 50)
+        
+        # 1. 识别验证码类型
+        if not self.identify_type(driver):
+            print("❌ 不是顺序点击验证码")
+            return False
+        
+        print("✅ 确认为顺序点击验证码")
+        
+        # 2. 提取验证码图片
+        image_base64 = self.extract_captcha_image(driver)
+        if not image_base64:
+            print("❌ 提取验证码图片失败")
+            return False
+        
+        print("✅ 验证码图片提取成功")
+        
+        # 3. 调用云码API
+        print("🔄 正在调用云码API识别...")
+        api_result = self.call_api(image_base64)
+        
+        if not api_result or api_result.get("code") != 10000:
+            print(f"❌ API识别失败: {api_result}")
+            return False
+        
+        print("✅ API识别成功")
+        
+        # 4. 解析结果
+        coordinates = self.parse_result(api_result)
+        if not coordinates:
+            print("❌ 解析坐标失败")
+            return False
+        
+        print(f"📍 获取到 {len(coordinates)} 个点击坐标: {coordinates}")
+        
+        # 5. 执行点击
+        if self.click_coordinates(driver, coordinates):
+            print("✅ 点击完成")
+            return True
+        else:
+            print("❌ 点击失败")
+            return False
+
+
+def main():
+    """主函数示例"""
+    # 配置
+    TOKEN = "YOUR_YUNMA_TOKEN_HERE"  # 替换为你的云码Token
+    
+    # 创建识别器
+    solver = YunmaIconPointCaptcha(TOKEN)
+    
+    # 使用示例
+    driver = webdriver.Chrome()
+    driver.get("https://buff.163.com")
+    
+    try:
+        # 等待验证码出现
+        WebDriverWait(driver, 10).until(
+            EC.presence_of_element_located((By.CLASS_NAME, "yidun"))
+        )
+        
+        # 等待验证码完全加载
+        time.sleep(2)
+        
+        # 解决验证码
+        if solver.solve(driver):
+            print("🎉 验证码解决成功!")
+        else:
+            print("❌ 验证码解决失败")
+            
+    except Exception as e:
+        print(f"错误: {e}")
+        
+    finally:
+        input("按Enter键关闭浏览器...")
+        driver.quit()
+
+
+if __name__ == "__main__":
+    main()

+ 304 - 0
.history/云码验证码识别_第一种验证码_20260204163330.py

@@ -0,0 +1,304 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+云码验证码识别 - 第一种验证码(顺序点击图标)
+接口文档: https://www.jfbym.com/test/317.html
+"""
+
+import base64
+import json
+import time
+import requests
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.common.action_chains import ActionChains
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+
+
+class YunmaIconPointCaptcha:
+    """云码顺序点击验证码识别器"""
+
+    def __init__(self, token: str):
+        """
+        初始化
+        
+        Args:
+            token: 云码平台API Token
+        """
+        self.token = token
+        self.api_url = "http://api.jfbym.com/api/YmServer/customApi"
+        self.type_id = "317"  # 顺序点击验证码接口ID
+
+    def identify_type(self, driver) -> bool:
+        """
+        识别是否为顺序点击验证码
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            bool: 是顺序点击验证码返回True
+        """
+        try:
+            # 查找验证码容器
+            captcha_container = driver.find_element(By.CLASS_NAME, "yidun")
+            class_name = captcha_container.get_attribute("class")
+            
+            # 检查是否包含icon_point类名
+            if "yidun--icon_point" in class_name:
+                return True
+            
+            # 备选:检查提示文字
+            try:
+                tips_element = driver.find_element(By.CSS_SELECTOR, ".yidun_tips__text")
+                tips_text = tips_element.text.strip()
+                if tips_text.startswith("请依次点击"):
+                    return True
+            except:
+                pass
+            
+            return False
+            
+        except Exception as e:
+            print(f"识别验证码类型失败: {e}")
+            return False
+
+    def extract_captcha_image(self, driver) -> str:
+        """
+        提取验证码图片并转为Base64
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            str: Base64编码的图片数据
+        """
+        try:
+            # 查找验证码背景图片
+            img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
+            
+            # 获取图片src
+            img_src = img_element.get_attribute("src")
+            print(f"验证码图片地址: {img_src}")
+            
+            # 如果是base64格式
+            if img_src.startswith("data:image"):
+                # 提取base64数据部分
+                base64_data = img_src.split(",")[1]
+                return base64_data
+            
+            # 如果是URL,下载图片
+            elif img_src.startswith("http"):
+                response = requests.get(img_src)
+                if response.status_code == 200:
+                    # 转为base64
+                    return base64.b64encode(response.content).decode()
+            
+            return None
+            
+        except Exception as e:
+            print(f"提取验证码图片失败: {e}")
+            return None
+
+    def call_api(self, image_base64: str) -> dict:
+        """
+        调用云码API识别验证码
+        
+        Args:
+            image_base64: Base64编码的图片数据
+            
+        Returns:
+            dict: API响应结果
+        """
+        payload = {
+            "token": self.token,
+            "type": self.type_id,
+            "image": image_base64
+        }
+        
+        headers = {
+            "Content-Type": "application/json"
+        }
+        
+        try:
+            response = requests.post(
+                self.api_url, 
+                json=payload, 
+                headers=headers,
+                timeout=30
+            )
+            
+            return response.json()
+            
+        except Exception as e:
+            print(f"API调用失败: {e}")
+            return {"code": -1, "msg": str(e)}
+
+    def parse_result(self, api_result: dict) -> list:
+        """
+        解析API返回结果
+        
+        Args:
+            api_result: API响应结果
+            
+        Returns:
+            list: 坐标列表 [[x1, y1], [x2, y2], ...]
+        """
+        try:
+            if api_result.get("code") == 10000:
+                # 识别成功
+                data = api_result.get("data", [])
+                if data:
+                    # 解析返回的坐标数据
+                    result_str = data[0].get("data", "")
+                    
+                    # 尝试解析为坐标列表
+                    if result_str.startswith("[[") and result_str.endswith("]]"):
+                        return json.loads(result_str)
+                    else:
+                        # 可能是其他格式,尝试eval
+                        try:
+                            return eval(result_str)
+                        except:
+                            print(f"无法解析返回数据: {result_str}")
+                            return []
+                
+            else:
+                print(f"识别失败: {api_result.get('msg')}")
+                return []
+                
+        except Exception as e:
+            print(f"解析结果失败: {e}")
+            return []
+
+    def click_coordinates(self, driver, coordinates: list):
+        """
+        执行点击操作
+        
+        Args:
+            driver: Selenium WebDriver实例
+            coordinates: 坐标列表
+        """
+        try:
+            # 查找验证码图片元素
+            img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
+            location = img_element.location
+            size = img_element.size
+            
+            # 创建Actions链
+            actions = ActionChains(driver)
+            
+            for i, coord in enumerate(coordinates):
+                x, y = coord
+                
+                # 移动到指定位置并点击
+                actions.move_to_element_with_offset(img_element, x, y)
+                actions.click()
+                
+                # 每次点击后等待一下
+                if i < len(coordinates) - 1:
+                    actions.pause(0.3)
+            
+            actions.perform()
+            print(f"完成点击,共点击 {len(coordinates)} 次")
+            
+            return True
+            
+        except Exception as e:
+            print(f"点击操作失败: {e}")
+            return False
+
+    def solve(self, driver) -> bool:
+        """
+        完整的验证码解决流程
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            bool: 解决成功返回True
+        """
+        print("=" * 50)
+        print("开始处理顺序点击验证码(第一种)")
+        print("=" * 50)
+        
+        # 1. 识别验证码类型
+        if not self.identify_type(driver):
+            print("❌ 不是顺序点击验证码")
+            return False
+        
+        print("✅ 确认为顺序点击验证码")
+        
+        # 2. 提取验证码图片
+        image_base64 = self.extract_captcha_image(driver)
+        if not image_base64:
+            print("❌ 提取验证码图片失败")
+            return False
+        
+        print("✅ 验证码图片提取成功")
+        
+        # 3. 调用云码API
+        print("🔄 正在调用云码API识别...")
+        api_result = self.call_api(image_base64)
+        
+        if not api_result or api_result.get("code") != 10000:
+            print(f"❌ API识别失败: {api_result}")
+            return False
+        
+        print("✅ API识别成功")
+        
+        # 4. 解析结果
+        coordinates = self.parse_result(api_result)
+        if not coordinates:
+            print("❌ 解析坐标失败")
+            return False
+        
+        print(f"📍 获取到 {len(coordinates)} 个点击坐标: {coordinates}")
+        
+        # 5. 执行点击
+        if self.click_coordinates(driver, coordinates):
+            print("✅ 点击完成")
+            return True
+        else:
+            print("❌ 点击失败")
+            return False
+
+
+def main():
+    """主函数示例"""
+    # 配置
+    TOKEN = "wVRyAIjI3hhJBicXndcs1ro9BN5UNMXIjW6SDWBFFyQ"  # 云码Token
+    
+    # 创建识别器
+    solver = YunmaIconPointCaptcha(TOKEN)
+    
+    # 使用示例
+    driver = webdriver.Chrome()
+    driver.get("https://buff.163.com")
+    
+    try:
+        # 等待验证码出现
+        WebDriverWait(driver, 10).until(
+            EC.presence_of_element_located((By.CLASS_NAME, "yidun"))
+        )
+        
+        # 等待验证码完全加载
+        time.sleep(2)
+        
+        # 解决验证码
+        if solver.solve(driver):
+            print("🎉 验证码解决成功!")
+        else:
+            print("❌ 验证码解决失败")
+            
+    except Exception as e:
+        print(f"错误: {e}")
+        
+    finally:
+        input("按Enter键关闭浏览器...")
+        driver.quit()
+
+
+if __name__ == "__main__":
+    main()

+ 304 - 0
.history/云码验证码识别_第一种验证码_20260204163407.py

@@ -0,0 +1,304 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+云码验证码识别 - 第一种验证码(顺序点击图标)
+接口文档: https://www.jfbym.com/test/317.html
+"""
+
+import base64
+import json
+import time
+import requests
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.common.action_chains import ActionChains
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+
+
+class YunmaIconPointCaptcha:
+    """云码顺序点击验证码识别器"""
+
+    def __init__(self, token: str):
+        """
+        初始化
+        
+        Args:
+            token: 云码平台API Token
+        """
+        self.token = token
+        self.api_url = "http://api.jfbym.com/api/YmServer/customApi"
+        self.type_id = "317"  # 顺序点击验证码接口ID
+
+    def identify_type(self, driver) -> bool:
+        """
+        识别是否为顺序点击验证码
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            bool: 是顺序点击验证码返回True
+        """
+        try:
+            # 查找验证码容器
+            captcha_container = driver.find_element(By.CLASS_NAME, "yidun")
+            class_name = captcha_container.get_attribute("class")
+            
+            # 检查是否包含icon_point类名
+            if "yidun--icon_point" in class_name:
+                return True
+            
+            # 备选:检查提示文字
+            try:
+                tips_element = driver.find_element(By.CSS_SELECTOR, ".yidun_tips__text")
+                tips_text = tips_element.text.strip()
+                if tips_text.startswith("请依次点击"):
+                    return True
+            except:
+                pass
+            
+            return False
+            
+        except Exception as e:
+            print(f"识别验证码类型失败: {e}")
+            return False
+
+    def extract_captcha_image(self, driver) -> str:
+        """
+        提取验证码图片并转为Base64
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            str: Base64编码的图片数据
+        """
+        try:
+            # 查找验证码背景图片
+            img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
+            
+            # 获取图片src
+            img_src = img_element.get_attribute("src")
+            print(f"验证码图片地址: {img_src}")
+            
+            # 如果是base64格式
+            if img_src.startswith("data:image"):
+                # 提取base64数据部分
+                base64_data = img_src.split(",")[1]
+                return base64_data
+            
+            # 如果是URL,下载图片
+            elif img_src.startswith("http"):
+                response = requests.get(img_src)
+                if response.status_code == 200:
+                    # 转为base64
+                    return base64.b64encode(response.content).decode()
+            
+            return None
+            
+        except Exception as e:
+            print(f"提取验证码图片失败: {e}")
+            return None
+
+    def call_api(self, image_base64: str) -> dict:
+        """
+        调用云码API识别验证码
+        
+        Args:
+            image_base64: Base64编码的图片数据
+            
+        Returns:
+            dict: API响应结果
+        """
+        payload = {
+            "token": self.token,
+            "type": self.type_id,
+            "image": image_base64
+        }
+        
+        headers = {
+            "Content-Type": "application/json"
+        }
+        
+        try:
+            response = requests.post(
+                self.api_url, 
+                json=payload, 
+                headers=headers,
+                timeout=30
+            )
+            
+            return response.json()
+            
+        except Exception as e:
+            print(f"API调用失败: {e}")
+            return {"code": -1, "msg": str(e)}
+
+    def parse_result(self, api_result: dict) -> list:
+        """
+        解析API返回结果
+        
+        Args:
+            api_result: API响应结果
+            
+        Returns:
+            list: 坐标列表 [[x1, y1], [x2, y2], ...]
+        """
+        try:
+            if api_result.get("code") == 10000:
+                # 识别成功
+                data = api_result.get("data", [])
+                if data:
+                    # 解析返回的坐标数据
+                    result_str = data[0].get("data", "")
+                    
+                    # 尝试解析为坐标列表
+                    if result_str.startswith("[[") and result_str.endswith("]]"):
+                        return json.loads(result_str)
+                    else:
+                        # 可能是其他格式,尝试eval
+                        try:
+                            return eval(result_str)
+                        except:
+                            print(f"无法解析返回数据: {result_str}")
+                            return []
+                
+            else:
+                print(f"识别失败: {api_result.get('msg')}")
+                return []
+                
+        except Exception as e:
+            print(f"解析结果失败: {e}")
+            return []
+
+    def click_coordinates(self, driver, coordinates: list):
+        """
+        执行点击操作
+        
+        Args:
+            driver: Selenium WebDriver实例
+            coordinates: 坐标列表
+        """
+        try:
+            # 查找验证码图片元素
+            img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
+            location = img_element.location
+            size = img_element.size
+            
+            # 创建Actions链
+            actions = ActionChains(driver)
+            
+            for i, coord in enumerate(coordinates):
+                x, y = coord
+                
+                # 移动到指定位置并点击
+                actions.move_to_element_with_offset(img_element, x, y)
+                actions.click()
+                
+                # 每次点击后等待一下
+                if i < len(coordinates) - 1:
+                    actions.pause(0.3)
+            
+            actions.perform()
+            print(f"完成点击,共点击 {len(coordinates)} 次")
+            
+            return True
+            
+        except Exception as e:
+            print(f"点击操作失败: {e}")
+            return False
+
+    def solve(self, driver) -> bool:
+        """
+        完整的验证码解决流程
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            bool: 解决成功返回True
+        """
+        print("=" * 50)
+        print("开始处理顺序点击验证码(第一种)")
+        print("=" * 50)
+        
+        # 1. 识别验证码类型
+        if not self.identify_type(driver):
+            print("❌ 不是顺序点击验证码")
+            return False
+        
+        print("✅ 确认为顺序点击验证码")
+        
+        # 2. 提取验证码图片
+        image_base64 = self.extract_captcha_image(driver)
+        if not image_base64:
+            print("❌ 提取验证码图片失败")
+            return False
+        
+        print("✅ 验证码图片提取成功")
+        
+        # 3. 调用云码API
+        print("🔄 正在调用云码API识别...")
+        api_result = self.call_api(image_base64)
+        
+        if not api_result or api_result.get("code") != 10000:
+            print(f"❌ API识别失败: {api_result}")
+            return False
+        
+        print("✅ API识别成功")
+        
+        # 4. 解析结果
+        coordinates = self.parse_result(api_result)
+        if not coordinates:
+            print("❌ 解析坐标失败")
+            return False
+        
+        print(f"📍 获取到 {len(coordinates)} 个点击坐标: {coordinates}")
+        
+        # 5. 执行点击
+        if self.click_coordinates(driver, coordinates):
+            print("✅ 点击完成")
+            return True
+        else:
+            print("❌ 点击失败")
+            return False
+
+
+def main():
+    """主函数示例"""
+    # 配置
+    TOKEN = "wVRyAIjI3hhJBicXndcs1ro9BN5UNMXIjW6SDWBFFyQ"  # 云码Token
+    
+    # 创建识别器
+    solver = YunmaIconPointCaptcha(TOKEN)
+    
+    # 使用示例
+    driver = webdriver.Chrome()
+    driver.get("https://buff.163.com")
+    
+    try:
+        # 等待验证码出现
+        WebDriverWait(driver, 10).until(
+            EC.presence_of_element_located((By.CLASS_NAME, "yidun"))
+        )
+        
+        # 等待验证码完全加载
+        time.sleep(2)
+        
+        # 解决验证码
+        if solver.solve(driver):
+            print("🎉 验证码解决成功!")
+        else:
+            print("❌ 验证码解决失败")
+            
+    except Exception as e:
+        print(f"错误: {e}")
+        
+    finally:
+        input("按Enter键关闭浏览器...")
+        driver.quit()
+
+
+if __name__ == "__main__":
+    main()

+ 337 - 0
.history/云码验证码识别_综合版_20260204163331.py

@@ -0,0 +1,337 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+综合验证码识别器 - 自动识别并调用云码接口
+支持两种BUFF验证码类型:
+1. 顺序点击验证码(type=317)
+2. 文字特征点选验证码(type=94)
+"""
+
+import base64
+import json
+import time
+import requests
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.common.action_chains import ActionChains
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+
+
+class BuffCaptchaSolver:
+    """BUFF验证码综合识别器"""
+
+    def __init__(self, token: str):
+        """
+        初始化
+        
+        Args:
+            token: 云码平台API Token
+        """
+        self.token = token
+        self.api_url = "http://api.jfbym.com/api/YmServer/customApi"
+
+    def detect_captcha_type(self, driver) -> str:
+        """
+        检测验证码类型
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            str: 'icon_point' - 顺序点击验证码
+                 'space' - 文字特征点选验证码
+                 None - 未检测到验证码
+        """
+        try:
+            # 等待验证码容器出现
+            wait = WebDriverWait(driver, 10)
+            captcha_container = wait.until(
+                EC.presence_of_element_located((By.CLASS_NAME, "yidun"))
+            )
+
+            # 方法1:检查CSS类名(最可靠)
+            class_name = captcha_container.get_attribute("class")
+            if "yidun--icon_point" in class_name:
+                return "icon_point"
+            elif "yidun--space" in class_name:
+                return "space"
+
+            # 方法2:检查提示文字
+            try:
+                tips_element = driver.find_element(By.CSS_SELECTOR, ".yidun_tips__text")
+                tips_text = tips_element.text.strip()
+
+                if tips_text.startswith("请依次点击"):
+                    return "icon_point"
+                elif tips_text.startswith("请点击"):
+                    return "space"
+            except:
+                pass
+
+            return None
+
+        except Exception as e:
+            print(f"验证码检测失败: {e}")
+            return None
+
+    def extract_captcha_image(self, driver) -> str:
+        """
+        提取验证码图片并转为Base64
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            str: Base64编码的图片数据
+        """
+        try:
+            img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
+            img_src = img_element.get_attribute("src")
+            print(f"验证码图片地址: {img_src}")
+
+            if img_src.startswith("data:image"):
+                base64_data = img_src.split(",")[1]
+                return base64_data
+            elif img_src.startswith("http"):
+                response = requests.get(img_src)
+                if response.status_code == 200:
+                    return base64.b64encode(response.content).decode()
+
+            return None
+
+        except Exception as e:
+            print(f"提取验证码图片失败: {e}")
+            return None
+
+    def extract_tip_text(self, driver) -> str:
+        """
+        提取验证码提示文字
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            str: 提示文字
+        """
+        try:
+            tips_element = driver.find_element(By.CSS_SELECTOR, ".yidun_tips__text")
+            tip_text = tips_element.text.strip()
+            print(f"验证码提示文字: {tip_text}")
+            return tip_text
+        except:
+            return ""
+
+    def call_api(self, image_base64: str, captcha_type: str, tip_text: str = "") -> dict:
+        """
+        调用云码API
+        
+        Args:
+            image_base64: Base64编码的图片数据
+            captcha_type: 验证码类型
+            tip_text: 提示文字
+            
+        Returns:
+            dict: API响应结果
+        """
+        # 根据类型选择接口ID
+        type_ids = {
+            "icon_point": "317",
+            "space": "94"
+        }
+
+        type_id = type_ids.get(captcha_type)
+        if not type_id:
+            return {"code": -1, "msg": "未知的验证码类型"}
+
+        payload = {
+            "token": self.token,
+            "type": type_id,
+            "image": image_base64
+        }
+
+        # 文字点选类型可能需要传递提示文字
+        if captcha_type == "space" and tip_text:
+            payload["extra"] = tip_text
+
+        headers = {"Content-Type": "application/json"}
+
+        try:
+            response = requests.post(
+                self.api_url,
+                json=payload,
+                headers=headers,
+                timeout=30
+            )
+            return response.json()
+
+        except Exception as e:
+            print(f"API调用失败: {e}")
+            return {"code": -1, "msg": str(e)}
+
+    def parse_result(self, api_result: dict) -> list:
+        """
+        解析API返回结果
+        
+        Args:
+            api_result: API响应结果
+            
+        Returns:
+            list: 坐标列表
+        """
+        try:
+            if api_result.get("code") == 10000:
+                data = api_result.get("data", [])
+                if data:
+                    result_str = data[0].get("data", "")
+                    if result_str.startswith("[") and result_str.endswith("]"):
+                        return json.loads(result_str)
+                    else:
+                        try:
+                            return eval(result_str)
+                        except:
+                            print(f"无法解析返回数据: {result_str}")
+                            return []
+            else:
+                print(f"识别失败: {api_result.get('msg')}")
+                return []
+
+        except Exception as e:
+            print(f"解析结果失败: {e}")
+            return []
+
+    def click_coordinates(self, driver, coordinates: list):
+        """
+        执行点击操作
+        
+        Args:
+            driver: Selenium WebDriver实例
+            coordinates: 坐标列表
+        """
+        try:
+            img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
+
+            actions = ActionChains(driver)
+
+            for i, coord in enumerate(coordinates):
+                x, y = coord
+                actions.move_to_element_with_offset(img_element, x, y)
+                actions.click()
+
+                if i < len(coordinates) - 1:
+                    actions.pause(0.3)
+
+            actions.perform()
+            print(f"完成点击,共点击 {len(coordinates)} 次")
+
+            return True
+
+        except Exception as e:
+            print(f"点击操作失败: {e}")
+            return False
+
+    def solve(self, driver) -> bool:
+        """
+        完整的验证码解决流程
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            bool: 解决成功返回True
+        """
+        # 1. 检测验证码类型
+        captcha_type = self.detect_captcha_type(driver)
+
+        if captcha_type == "icon_point":
+            type_name = "顺序点击验证码(第一种)"
+            type_id = "317"
+        elif captcha_type == "space":
+            type_name = "文字特征点选验证码(第二种)"
+            type_id = "94"
+        else:
+            print("❌ 无法识别验证码类型或未检测到验证码")
+            return False
+
+        print("=" * 60)
+        print(f"✅ 检测到 {type_name}")
+        print(f"   云码接口ID: {type_id}")
+        print("=" * 60)
+
+        # 2. 提取验证码图片
+        image_base64 = self.extract_captcha_image(driver)
+        if not image_base64:
+            print("❌ 提取验证码图片失败")
+            return False
+
+        print("✅ 验证码图片提取成功")
+
+        # 3. 提取提示文字(仅第二种需要)
+        tip_text = ""
+        if captcha_type == "space":
+            tip_text = self.extract_tip_text(driver)
+
+        # 4. 调用云码API
+        print("🔄 正在调用云码API识别...")
+        api_result = self.call_api(image_base64, captcha_type, tip_text)
+
+        if not api_result or api_result.get("code") != 10000:
+            print(f"❌ API识别失败: {api_result}")
+            return False
+
+        print("✅ API识别成功")
+
+        # 5. 解析结果
+        coordinates = self.parse_result(api_result)
+        if not coordinates:
+            print("❌ 解析坐标失败")
+            return False
+
+        print(f"📍 获取到 {len(coordinates)} 个点击坐标: {coordinates}")
+
+        # 6. 执行点击
+        if self.click_coordinates(driver, coordinates):
+            print("✅ 点击完成")
+            return True
+        else:
+            print("❌ 点击失败")
+            return False
+
+
+def main():
+    """主函数示例"""
+    # 配置
+    TOKEN = "wVRyAIjI3hhJBicXndcs1ro9BN5UNMXIjW6SDWBFFyQ"  # 云码Token
+
+    # 创建识别器
+    solver = BuffCaptchaSolver(TOKEN)
+
+    # 使用示例
+    driver = webdriver.Chrome()
+    driver.get("https://buff.163.com")
+
+    try:
+        # 等待验证码出现(最多30秒)
+        WebDriverWait(driver, 30).until(
+            EC.presence_of_element_located((By.CLASS_NAME, "yidun"))
+        )
+
+        # 等待验证码完全加载
+        time.sleep(2)
+
+        # 自动识别并解决验证码
+        if solver.solve(driver):
+            print("\n🎉 验证码解决成功!")
+        else:
+            print("\n❌ 验证码解决失败")
+
+    except Exception as e:
+        print(f"错误: {e}")
+
+    finally:
+        input("\n按Enter键关闭浏览器...")
+        driver.quit()
+
+
+if __name__ == "__main__":
+    main()

+ 337 - 0
.history/云码验证码识别_综合版_20260204163352.py

@@ -0,0 +1,337 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+综合验证码识别器 - 自动识别并调用云码接口
+支持两种BUFF验证码类型:
+1. 顺序点击验证码(type=317)
+2. 文字特征点选验证码(type=94)
+"""
+
+import base64
+import json
+import time
+import requests
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.common.action_chains import ActionChains
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+
+
+class BuffCaptchaSolver:
+    """BUFF验证码综合识别器"""
+
+    def __init__(self, token: str):
+        """
+        初始化
+        
+        Args:
+            token: 云码平台API Token
+        """
+        self.token = token
+        self.api_url = "http://api.jfbym.com/api/YmServer/customApi"
+
+    def detect_captcha_type(self, driver) -> str:
+        """
+        检测验证码类型
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            str: 'icon_point' - 顺序点击验证码
+                 'space' - 文字特征点选验证码
+                 None - 未检测到验证码
+        """
+        try:
+            # 等待验证码容器出现
+            wait = WebDriverWait(driver, 10)
+            captcha_container = wait.until(
+                EC.presence_of_element_located((By.CLASS_NAME, "yidun"))
+            )
+
+            # 方法1:检查CSS类名(最可靠)
+            class_name = captcha_container.get_attribute("class")
+            if "yidun--icon_point" in class_name:
+                return "icon_point"
+            elif "yidun--space" in class_name:
+                return "space"
+
+            # 方法2:检查提示文字
+            try:
+                tips_element = driver.find_element(By.CSS_SELECTOR, ".yidun_tips__text")
+                tips_text = tips_element.text.strip()
+
+                if tips_text.startswith("请依次点击"):
+                    return "icon_point"
+                elif tips_text.startswith("请点击"):
+                    return "space"
+            except:
+                pass
+
+            return None
+
+        except Exception as e:
+            print(f"验证码检测失败: {e}")
+            return None
+
+    def extract_captcha_image(self, driver) -> str:
+        """
+        提取验证码图片并转为Base64
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            str: Base64编码的图片数据
+        """
+        try:
+            img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
+            img_src = img_element.get_attribute("src")
+            print(f"验证码图片地址: {img_src}")
+
+            if img_src.startswith("data:image"):
+                base64_data = img_src.split(",")[1]
+                return base64_data
+            elif img_src.startswith("http"):
+                response = requests.get(img_src)
+                if response.status_code == 200:
+                    return base64.b64encode(response.content).decode()
+
+            return None
+
+        except Exception as e:
+            print(f"提取验证码图片失败: {e}")
+            return None
+
+    def extract_tip_text(self, driver) -> str:
+        """
+        提取验证码提示文字
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            str: 提示文字
+        """
+        try:
+            tips_element = driver.find_element(By.CSS_SELECTOR, ".yidun_tips__text")
+            tip_text = tips_element.text.strip()
+            print(f"验证码提示文字: {tip_text}")
+            return tip_text
+        except:
+            return ""
+
+    def call_api(self, image_base64: str, captcha_type: str, tip_text: str = "") -> dict:
+        """
+        调用云码API
+        
+        Args:
+            image_base64: Base64编码的图片数据
+            captcha_type: 验证码类型
+            tip_text: 提示文字
+            
+        Returns:
+            dict: API响应结果
+        """
+        # 根据类型选择接口ID
+        type_ids = {
+            "icon_point": "317",
+            "space": "94"
+        }
+
+        type_id = type_ids.get(captcha_type)
+        if not type_id:
+            return {"code": -1, "msg": "未知的验证码类型"}
+
+        payload = {
+            "token": self.token,
+            "type": type_id,
+            "image": image_base64
+        }
+
+        # 文字点选类型可能需要传递提示文字
+        if captcha_type == "space" and tip_text:
+            payload["extra"] = tip_text
+
+        headers = {"Content-Type": "application/json"}
+
+        try:
+            response = requests.post(
+                self.api_url,
+                json=payload,
+                headers=headers,
+                timeout=30
+            )
+            return response.json()
+
+        except Exception as e:
+            print(f"API调用失败: {e}")
+            return {"code": -1, "msg": str(e)}
+
+    def parse_result(self, api_result: dict) -> list:
+        """
+        解析API返回结果
+        
+        Args:
+            api_result: API响应结果
+            
+        Returns:
+            list: 坐标列表
+        """
+        try:
+            if api_result.get("code") == 10000:
+                data = api_result.get("data", [])
+                if data:
+                    result_str = data[0].get("data", "")
+                    if result_str.startswith("[") and result_str.endswith("]"):
+                        return json.loads(result_str)
+                    else:
+                        try:
+                            return eval(result_str)
+                        except:
+                            print(f"无法解析返回数据: {result_str}")
+                            return []
+            else:
+                print(f"识别失败: {api_result.get('msg')}")
+                return []
+
+        except Exception as e:
+            print(f"解析结果失败: {e}")
+            return []
+
+    def click_coordinates(self, driver, coordinates: list):
+        """
+        执行点击操作
+        
+        Args:
+            driver: Selenium WebDriver实例
+            coordinates: 坐标列表
+        """
+        try:
+            img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
+
+            actions = ActionChains(driver)
+
+            for i, coord in enumerate(coordinates):
+                x, y = coord
+                actions.move_to_element_with_offset(img_element, x, y)
+                actions.click()
+
+                if i < len(coordinates) - 1:
+                    actions.pause(0.3)
+
+            actions.perform()
+            print(f"完成点击,共点击 {len(coordinates)} 次")
+
+            return True
+
+        except Exception as e:
+            print(f"点击操作失败: {e}")
+            return False
+
+    def solve(self, driver) -> bool:
+        """
+        完整的验证码解决流程
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            bool: 解决成功返回True
+        """
+        # 1. 检测验证码类型
+        captcha_type = self.detect_captcha_type(driver)
+
+        if captcha_type == "icon_point":
+            type_name = "顺序点击验证码(第一种)"
+            type_id = "317"
+        elif captcha_type == "space":
+            type_name = "文字特征点选验证码(第二种)"
+            type_id = "94"
+        else:
+            print("❌ 无法识别验证码类型或未检测到验证码")
+            return False
+
+        print("=" * 60)
+        print(f"✅ 检测到 {type_name}")
+        print(f"   云码接口ID: {type_id}")
+        print("=" * 60)
+
+        # 2. 提取验证码图片
+        image_base64 = self.extract_captcha_image(driver)
+        if not image_base64:
+            print("❌ 提取验证码图片失败")
+            return False
+
+        print("✅ 验证码图片提取成功")
+
+        # 3. 提取提示文字(仅第二种需要)
+        tip_text = ""
+        if captcha_type == "space":
+            tip_text = self.extract_tip_text(driver)
+
+        # 4. 调用云码API
+        print("🔄 正在调用云码API识别...")
+        api_result = self.call_api(image_base64, captcha_type, tip_text)
+
+        if not api_result or api_result.get("code") != 10000:
+            print(f"❌ API识别失败: {api_result}")
+            return False
+
+        print("✅ API识别成功")
+
+        # 5. 解析结果
+        coordinates = self.parse_result(api_result)
+        if not coordinates:
+            print("❌ 解析坐标失败")
+            return False
+
+        print(f"📍 获取到 {len(coordinates)} 个点击坐标: {coordinates}")
+
+        # 6. 执行点击
+        if self.click_coordinates(driver, coordinates):
+            print("✅ 点击完成")
+            return True
+        else:
+            print("❌ 点击失败")
+            return False
+
+
+def main():
+    """主函数示例"""
+    # 配置
+    TOKEN = "wVRyAIjI3hhJBicXndcs1ro9BN5UNMXIjW6SDWBFFyQ"  # 云码Token
+
+    # 创建识别器
+    solver = BuffCaptchaSolver(TOKEN)
+
+    # 使用示例
+    driver = webdriver.Chrome()
+    driver.get("https://buff.163.com")
+
+    try:
+        # 等待验证码出现(最多30秒)
+        WebDriverWait(driver, 30).until(
+            EC.presence_of_element_located((By.CLASS_NAME, "yidun"))
+        )
+
+        # 等待验证码完全加载
+        time.sleep(2)
+
+        # 自动识别并解决验证码
+        if solver.solve(driver):
+            print("\n🎉 验证码解决成功!")
+        else:
+            print("\n❌ 验证码解决失败")
+
+    except Exception as e:
+        print(f"错误: {e}")
+
+    finally:
+        input("\n按Enter键关闭浏览器...")
+        driver.quit()
+
+
+if __name__ == "__main__":
+    main()

+ 341 - 0
.history/云码验证码识别_综合版_20260204163449.py

@@ -0,0 +1,341 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+综合验证码识别器 - 自动识别并调用云码接口
+支持两种BUFF验证码类型:
+1. 顺序点击验证码(type=317)
+2. 文字特征点选验证码(type=94)
+"""
+
+import base64
+import json
+import time
+import requests
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.common.action_chains import ActionChains
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+
+
+class BuffCaptchaSolver:
+    """BUFF验证码综合识别器"""
+
+    def __init__(self, token: str):
+        """
+        初始化
+        
+        Args:
+            token: 云码平台API Token
+        """
+        self.token = token
+        self.api_url = "http://api.jfbym.com/api/YmServer/customApi"
+
+    def detect_captcha_type(self, driver) -> str:
+        """
+        检测验证码类型
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            str: 'icon_point' - 顺序点击验证码
+                 'space' - 文字特征点选验证码
+                 None - 未检测到验证码
+        """
+        try:
+            # 等待验证码容器出现
+            wait = WebDriverWait(driver, 10)
+            captcha_container = wait.until(
+                EC.presence_of_element_located((By.CLASS_NAME, "yidun"))
+            )
+
+            # 方法1:检查CSS类名(最可靠)
+            class_name = captcha_container.get_attribute("class")
+            if "yidun--icon_point" in class_name:
+                return "icon_point"
+            elif "yidun--space" in class_name:
+                return "space"
+
+            # 方法2:检查提示文字
+            try:
+                tips_element = driver.find_element(By.CSS_SELECTOR, ".yidun_tips__text")
+                tips_text = tips_element.text.strip()
+
+                if tips_text.startswith("请依次点击"):
+                    return "icon_point"
+                elif tips_text.startswith("请点击"):
+                    return "space"
+            except:
+                pass
+
+            return None
+
+        except Exception as e:
+            print(f"验证码检测失败: {e}")
+            return None
+
+    def extract_captcha_image(self, driver) -> str:
+        """
+        提取验证码图片并转为Base64
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            str: Base64编码的图片数据
+        """
+        try:
+            img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
+            img_src = img_element.get_attribute("src")
+            print(f"验证码图片地址: {img_src}")
+
+            if img_src.startswith("data:image"):
+                base64_data = img_src.split(",")[1]
+                return base64_data
+            elif img_src.startswith("http"):
+                response = requests.get(img_src)
+                if response.status_code == 200:
+                    return base64.b64encode(response.content).decode()
+
+            return None
+
+        except Exception as e:
+            print(f"提取验证码图片失败: {e}")
+            return None
+
+    def extract_tip_text(self, driver) -> str:
+        """
+        提取验证码提示文字
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            str: 提示文字
+        """
+        try:
+            tips_element = driver.find_element(By.CSS_SELECTOR, ".yidun_tips__text")
+            tip_text = tips_element.text.strip()
+            print(f"验证码提示文字: {tip_text}")
+            return tip_text
+        except:
+            return ""
+
+    def call_api(self, image_base64: str, captcha_type: str, tip_text: str = "") -> dict:
+        """
+        调用云码API
+        
+        Args:
+            image_base64: Base64编码的图片数据
+            captcha_type: 验证码类型
+            tip_text: 提示文字
+            
+        Returns:
+            dict: API响应结果
+        """
+        # 根据类型选择接口ID
+        type_ids = {
+            "icon_point": "317",
+            "space": "94"
+        }
+
+        type_id = type_ids.get(captcha_type)
+        if not type_id:
+            return {"code": -1, "msg": "未知的验证码类型"}
+
+        payload = {
+            "token": self.token,
+            "type": type_id,
+            "image": image_base64
+        }
+
+        # 文字点选类型可能需要传递提示文字
+        if captcha_type == "space" and tip_text:
+            payload["extra"] = tip_text
+
+        headers = {"Content-Type": "application/json"}
+
+        try:
+            response = requests.post(
+                self.api_url,
+                json=payload,
+                headers=headers,
+                timeout=30
+            )
+            return response.json()
+
+        except Exception as e:
+            print(f"API调用失败: {e}")
+            return {"code": -1, "msg": str(e)}
+
+    def parse_result(self, api_result: dict) -> list:
+        """
+        解析API返回结果
+        
+        Args:
+            api_result: API响应结果
+            
+        Returns:
+            list: 坐标列表
+        """
+        try:
+            if api_result.get("code") == 10000:
+                data = api_result.get("data", [])
+                if data:
+                    result_str = data[0].get("data", "")
+                    if result_str.startswith("[") and result_str.endswith("]"):
+                        return json.loads(result_str)
+                    else:
+                        try:
+                            return eval(result_str)
+                        except:
+                            print(f"无法解析返回数据: {result_str}")
+                            return []
+            else:
+                print(f"识别失败: {api_result.get('msg')}")
+                return []
+
+        except Exception as e:
+            print(f"解析结果失败: {e}")
+            return []
+
+    def click_coordinates(self, driver, coordinates: list):
+        """
+        执行点击操作
+        
+        Args:
+            driver: Selenium WebDriver实例
+            coordinates: 坐标列表
+        """
+        try:
+            img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
+
+            actions = ActionChains(driver)
+
+            for i, coord in enumerate(coordinates):
+                x, y = coord
+                actions.move_to_element_with_offset(img_element, x, y)
+                actions.click()
+
+                if i < len(coordinates) - 1:
+                    actions.pause(0.3)
+
+            actions.perform()
+            print(f"完成点击,共点击 {len(coordinates)} 次")
+
+            return True
+
+        except Exception as e:
+            print(f"点击操作失败: {e}")
+            return False
+
+    def solve(self, driver) -> bool:
+        """
+        完整的验证码解决流程
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            bool: 解决成功返回True
+        """
+        # 1. 检测验证码类型
+        captcha_type = self.detect_captcha_type(driver)
+
+        if captcha_type == "icon_point":
+            type_name = "顺序点击验证码(第一种)"
+            type_id = "317"
+        elif captcha_type == "space":
+            type_name = "文字特征点选验证码(第二种)"
+            type_id = "94"
+        else:
+            print("❌ 无法识别验证码类型或未检测到验证码")
+            return False
+
+        print("=" * 60)
+        print(f"✅ 检测到 {type_name}")
+        print(f"   云码接口ID: {type_id}")
+        print("=" * 60)
+
+        # 2. 提取验证码图片
+        image_base64 = self.extract_captcha_image(driver)
+        if not image_base64:
+            print("❌ 提取验证码图片失败")
+            return False
+
+        print("✅ 验证码图片提取成功")
+
+        # 3. 提取提示文字(仅第二种需要)
+        tip_text = ""
+        if captcha_type == "space":
+            tip_text = self.extract_tip_text(driver)
+
+        # 4. 调用云码API
+        print("🔄 正在调用云码API识别...")
+        api_result = self.call_api(image_base64, captcha_type, tip_text)
+
+        if not api_result or api_result.get("code") != 10000:
+            print(f"❌ API识别失败: {api_result}")
+            return False
+
+        print("✅ API识别成功")
+
+        # 5. 解析结果
+        coordinates = self.parse_result(api_result)
+        if not coordinates:
+            print("❌ 解析坐标失败")
+            return False
+
+        print(f"📍 获取到 {len(coordinates)} 个点击坐标: {coordinates}")
+
+        # 6. 执行点击
+        if self.click_coordinates(driver, coordinates):
+            print("✅ 点击完成")
+            return True
+        else:
+            print("❌ 点击失败")
+            return False
+
+
+def main():
+    """主函数示例"""
+    # 配置
+    TOKEN = "wVRyAIjI3hhJBicXndcs1ro9BN5UNMXIjW6SDWBFFyQ"  # 云码Token
+
+    # 创建识别器
+    solver = BuffCaptchaSolver(TOKEN)
+
+    # 使用示例
+    driver = webdriver.Chrome()
+    
+    # 访问指定URL
+    target_url = "https://buff.163.com/goods/900482?from=market#tab=selling"
+    print(f"正在访问: {target_url}")
+    driver.get(target_url)
+
+    try:
+        # 等待验证码出现(最多30秒)
+        WebDriverWait(driver, 30).until(
+            EC.presence_of_element_located((By.CLASS_NAME, "yidun"))
+        )
+
+        # 等待验证码完全加载
+        time.sleep(2)
+
+        # 自动识别并解决验证码
+        if solver.solve(driver):
+            print("\n🎉 验证码解决成功!")
+        else:
+            print("\n❌ 验证码解决失败")
+
+    except Exception as e:
+        print(f"错误: {e}")
+
+    finally:
+        input("\n按Enter键关闭浏览器...")
+        driver.quit()
+
+
+if __name__ == "__main__":
+    main()

+ 482 - 0
.history/云码验证码识别_综合版_简化版_20260204165631.py

@@ -0,0 +1,482 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+BUFF验证码自动识别器 - 使用Playwright(简化版)
+支持两种验证码类型:
+1. 顺序点击验证码(type=317)
+2. 文字特征点选验证码(type=94)
+
+使用方法:
+1. 安装依赖: pip install playwright && playwright install chromium
+2. 运行脚本: python3 云码验证码识别_综合版_简化版.py
+"""
+
+import base64
+import json
+import time
+import sys
+from playwright.sync_api import sync_playwright
+import requests
+
+
+def apply_stealth(page):
+    """
+    手动应用 stealth 脚本,防止被检测为自动化工具
+    """
+    page.add_init_script("""
+        Object.defineProperty(navigator, 'webdriver', {
+            get: () => undefined
+        });
+        Object.defineProperty(navigator, 'languages', {
+            get: () => ['zh-CN', 'zh', 'en-US', 'en']
+        });
+        Object.defineProperty(navigator, 'plugins', {
+            get: () => [1, 2, 3, 4, 5]
+        });
+        window.navigator.chrome = {
+            runtime: {},
+            loadTimes: function() {},
+            csi: function() {},
+            app: {}
+        };
+        Object.defineProperty(window, 'chrome', {
+            get: () => window.navigator.chrome
+        });
+        Object.defineProperty(navigator, 'platform', {
+            get: () => 'MacIntel'
+        });
+        Object.defineProperty(navigator, 'deviceMemory', {
+            get: () => 8
+        });
+        Object.defineProperty(navigator, 'hardwareConcurrency', {
+            get: () => 8
+        });
+    """)
+
+
+class BuffCaptchaSolver:
+    """BUFF验证码综合识别器"""
+
+    def __init__(self, token: str):
+        """
+        初始化
+
+        Args:
+            token: 云码平台API Token
+        """
+        self.token = token
+        self.api_url = "http://api.jfbym.com/api/YmServer/customApi"
+
+    def detect_captcha_type(self, page) -> str:
+        """
+        检测验证码类型
+
+        Args:
+            page: Playwright Page对象
+
+        Returns:
+            str: 'icon_point' - 顺序点击验证码
+                 'space' - 文字特征点选验证码
+                 None - 未检测到验证码
+        """
+        try:
+            # 等待验证码容器出现
+            page.wait_for_selector(".yidun", timeout=10000)
+
+            # 方法1:检查CSS类名(最可靠)
+            class_name = page.evaluate("""
+                () => {
+                    const el = document.querySelector('.yidun');
+                    return el ? el.className : '';
+                }
+            """)
+
+            print(f"验证码容器class: {class_name}")
+
+            if "yidun--icon_point" in class_name:
+                return "icon_point"
+            elif "yidun--space" in class_name:
+                return "space"
+
+            # 方法2:检查提示文字
+            try:
+                tips_text = page.evaluate("""
+                    () => {
+                        const el = document.querySelector('.yidun_tips__text');
+                        return el ? el.textContent.trim() : '';
+                    }
+                """)
+
+                print(f"验证码提示文字: {tips_text}")
+
+                if tips_text.startswith("请依次点击"):
+                    return "icon_point"
+                elif tips_text.startswith("请点击"):
+                    return "space"
+            except:
+                pass
+
+            return None
+
+        except Exception as e:
+            print(f"验证码检测失败: {e}")
+            return None
+
+    def extract_captcha_image(self, page) -> str:
+        """
+        提取验证码图片并转为Base64
+
+        Args:
+            page: Playwright Page对象
+
+        Returns:
+            str: Base64编码的图片数据
+        """
+        try:
+            # 获取图片src
+            img_src = page.evaluate("""
+                () => {
+                    const img = document.querySelector('.yidun_bg-img');
+                    return img ? img.src : null;
+                }
+            """)
+
+            if not img_src:
+                print("未找到验证码图片")
+                return None
+
+            print(f"验证码图片地址: {img_src[:100]}...")
+
+            if img_src.startswith("data:image"):
+                # 提取base64数据部分
+                base64_data = img_src.split(",")[1]
+                return base64_data
+            elif img_src.startswith("http"):
+                # 下载图片
+                response = requests.get(img_src)
+                if response.status_code == 200:
+                    return base64.b64encode(response.content).decode()
+
+            return None
+
+        except Exception as e:
+            print(f"提取验证码图片失败: {e}")
+            return None
+
+    def extract_tip_text(self, page) -> str:
+        """
+        提取验证码提示文字
+
+        Args:
+            page: Playwright Page对象
+
+        Returns:
+            str: 提示文字
+        """
+        try:
+            tip_text = page.evaluate("""
+                () => {
+                    const el = document.querySelector('.yidun_tips__text');
+                    return el ? el.textContent.trim() : '';
+                }
+            """)
+            print(f"验证码提示文字: {tip_text}")
+            return tip_text
+        except:
+            return ""
+
+    def call_api(self, image_base64: str, captcha_type: str, tip_text: str = "") -> dict:
+        """
+        调用云码API
+
+        Args:
+            image_base64: Base64编码的图片数据
+            captcha_type: 验证码类型
+            tip_text: 提示文字
+
+        Returns:
+            dict: API响应结果
+        """
+        # 根据类型选择接口ID
+        type_ids = {
+            "icon_point": "317",
+            "space": "94"
+        }
+
+        type_id = type_ids.get(captcha_type)
+        if not type_id:
+            return {"code": -1, "msg": "未知的验证码类型"}
+
+        payload = {
+            "token": self.token,
+            "type": type_id,
+            "image": image_base64
+        }
+
+        # 文字点选类型可能需要传递提示文字
+        if captcha_type == "space" and tip_text:
+            payload["extra"] = tip_text
+
+        headers = {"Content-Type": "application/json"}
+
+        try:
+            response = requests.post(
+                self.api_url,
+                json=payload,
+                headers=headers,
+                timeout=30
+            )
+            return response.json()
+
+        except Exception as e:
+            print(f"API调用失败: {e}")
+            return {"code": -1, "msg": str(e)}
+
+    def parse_result(self, api_result: dict) -> list:
+        """
+        解析API返回结果
+
+        Args:
+            api_result: API响应结果
+
+        Returns:
+            list: 坐标列表
+        """
+        try:
+            if api_result.get("code") == 10000:
+                data = api_result.get("data", [])
+                if data:
+                    result_str = data[0].get("data", "")
+                    print(f"云码返回坐标: {result_str}")
+
+                    if result_str.startswith("[") and result_str.endswith("]"):
+                        return json.loads(result_str)
+                    else:
+                        try:
+                            return eval(result_str)
+                        except:
+                            print(f"无法解析返回数据: {result_str}")
+                            return []
+            else:
+                print(f"识别失败: {api_result.get('msg')}")
+                return []
+
+        except Exception as e:
+            print(f"解析结果失败: {e}")
+            return []
+
+    def click_coordinates(self, page, coordinates: list):
+        """
+        执行点击操作
+
+        Args:
+            page: Playwright Page对象
+            coordinates: 坐标列表
+        """
+        try:
+            # 获取验证码图片元素
+            img_element = page.query_selector(".yidun_bg-img")
+            box = img_element.bounding_box()
+
+            if not box:
+                print("无法获取验证码图片位置")
+                return False
+
+            print(f"验证码图片位置: x={box['x']}, y={box['y']}, width={box['width']}, height={box['height']}")
+
+            # 执行点击
+            for i, coord in enumerate(coordinates):
+                x, y = coord
+
+                # 计算相对于图片的绝对坐标
+                abs_x = box["x"] + x
+                abs_y = box["y"] + y
+
+                # 移动并点击
+                page.mouse.click(abs_x, abs_y)
+                print(f"点击坐标 ({i+1}): ({abs_x}, {abs_y})")
+
+                # 每次点击后等待
+                if i < len(coordinates) - 1:
+                    time.sleep(0.5)
+
+            print(f"完成点击,共点击 {len(coordinates)} 次")
+            return True
+
+        except Exception as e:
+            print(f"点击操作失败: {e}")
+            return False
+
+    def solve(self, page) -> bool:
+        """
+        完整的验证码解决流程
+
+        Args:
+            page: Playwright Page对象
+
+        Returns:
+            bool: 解决成功返回True
+        """
+        # 1. 检测验证码类型
+        captcha_type = self.detect_captcha_type(page)
+
+        if captcha_type == "icon_point":
+            type_name = "顺序点击验证码(第一种)"
+            type_id = "317"
+        elif captcha_type == "space":
+            type_name = "文字特征点选验证码(第二种)"
+            type_id = "94"
+        else:
+            print("无法识别验证码类型或未检测到验证码")
+            return False
+
+        print("=" * 60)
+        print(f"检测到 {type_name}")
+        print(f"云码接口ID: {type_id}")
+        print("=" * 60)
+
+        # 2. 提取验证码图片
+        image_base64 = self.extract_captcha_image(page)
+        if not image_base64:
+            print("提取验证码图片失败")
+            return False
+
+        print("验证码图片提取成功")
+
+        # 3. 提取提示文字(仅第二种需要)
+        tip_text = ""
+        if captcha_type == "space":
+            tip_text = self.extract_tip_text(page)
+
+        # 4. 调用云码API
+        print("正在调用云码API识别...")
+        api_result = self.call_api(image_base64, captcha_type, tip_text)
+
+        if not api_result or api_result.get("code") != 10000:
+            print(f"API识别失败: {api_result}")
+            return False
+
+        print("API识别成功")
+
+        # 5. 解析结果
+        coordinates = self.parse_result(api_result)
+        if not coordinates:
+            print("解析坐标失败")
+            return False
+
+        print(f"获取到 {len(coordinates)} 个点击坐标")
+
+        # 6. 执行点击
+        if self.click_coordinates(page, coordinates):
+            print("点击完成")
+            return True
+        else:
+            print("点击失败")
+            return False
+
+
+def main():
+    """主函数"""
+    # 配置
+    TOKEN = "wVRyAIjI3hhJBicXndcs1ro9BN5UNMXIjW6SDWBFFyQ"  # 云码Token
+    TARGET_URL = "https://buff.163.com/goods/900482?from=market#tab=selling"
+
+    # 创建识别器
+    solver = BuffCaptchaSolver(TOKEN)
+
+    print("=" * 60)
+    print("BUFF验证码自动识别器(Playwright简化版)")
+    print("=" * 60)
+
+    with sync_playwright() as p:
+        # 启动浏览器
+        print("启动浏览器...")
+        browser = p.chromium.launch(
+            headless=False,  # False=显示浏览器窗口,方便调试
+            args=[
+                '--no-sandbox',
+                '--disable-setuid-sandbox',
+                '--disable-dev-shm-usage',
+                '--disable-gpu',
+                '--window-size=1920,1080',
+                '--start-maximized'
+            ]
+        )
+
+        # 创建上下文
+        context = browser.new_context(
+            viewport={'width': 1920, 'height': 1080},
+            user_agent='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
+        )
+
+        # 创建页面
+        page = context.new_page()
+
+        # 应用stealth防止被检测
+        apply_stealth(page)
+
+        try:
+            # 访问目标URL
+            print(f"正在访问: {TARGET_URL}")
+            page.goto(TARGET_URL)
+
+            # 等待页面加载
+            page.wait_for_load_state("networkidle")
+
+            print("页面加载完成")
+
+            # 设置session cookie防止验证码
+            print("设置session cookie...")
+            context.add_cookies([
+                {
+                    "name": "session",
+                    "value": "1-d0RH6dVVm6n4DY6USFPkE_3CmTlqadAV0EF2JAQ-AWpn2046511090",
+                    "domain": ".buff.163.com",
+                    "path": "/"
+                }
+            ])
+            print("session cookie设置成功")
+
+            # 刷新页面使cookie生效
+            page.reload()
+            page.wait_for_load_state("networkidle")
+            print("页面已刷新")
+
+            # 等待验证码出现(最多30秒)
+            print("等待验证码出现...")
+
+            # 尝试等待验证码
+            try:
+                page.wait_for_selector(".yidun", timeout=30000)
+                print("检测到验证码!")
+            except:
+                print("30秒内未检测到验证码")
+                print("可能的原因:")
+                print("1. 页面没有触发验证码")
+                print("2. 验证码已经被手动处理")
+                print("3. 页面结构已变化")
+                input("\n按Enter键关闭浏览器...")
+                browser.close()
+                return
+
+            # 等待验证码完全加载
+            time.sleep(2)
+
+            # 自动识别并解决验证码
+            if solver.solve(page):
+                print("\n验证码解决成功!")
+            else:
+                print("\n验证码解决失败")
+
+        except Exception as e:
+            print(f"错误: {e}")
+            import traceback
+            traceback.print_exc()
+
+        finally:
+            input("\n按Enter键关闭浏览器...")
+            browser.close()
+
+
+if __name__ == "__main__":
+    main()

+ 482 - 0
.history/云码验证码识别_综合版_简化版_20260204170044.py

@@ -0,0 +1,482 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+BUFF验证码自动识别器 - 使用Playwright(简化版)
+支持两种验证码类型:
+1. 顺序点击验证码(type=317)
+2. 文字特征点选验证码(type=94)
+
+使用方法:
+1. 安装依赖: pip install playwright && playwright install chromium
+2. 运行脚本: python3 云码验证码识别_综合版_简化版.py
+"""
+
+import base64
+import json
+import time
+import sys
+from playwright.sync_api import sync_playwright
+import requests
+
+
+def apply_stealth(page):
+    """
+    手动应用 stealth 脚本,防止被检测为自动化工具
+    """
+    page.add_init_script("""
+        Object.defineProperty(navigator, 'webdriver', {
+            get: () => undefined
+        });
+        Object.defineProperty(navigator, 'languages', {
+            get: () => ['zh-CN', 'zh', 'en-US', 'en']
+        });
+        Object.defineProperty(navigator, 'plugins', {
+            get: () => [1, 2, 3, 4, 5]
+        });
+        window.navigator.chrome = {
+            runtime: {},
+            loadTimes: function() {},
+            csi: function() {},
+            app: {}
+        };
+        Object.defineProperty(window, 'chrome', {
+            get: () => window.navigator.chrome
+        });
+        Object.defineProperty(navigator, 'platform', {
+            get: () => 'MacIntel'
+        });
+        Object.defineProperty(navigator, 'deviceMemory', {
+            get: () => 8
+        });
+        Object.defineProperty(navigator, 'hardwareConcurrency', {
+            get: () => 8
+        });
+    """)
+
+
+class BuffCaptchaSolver:
+    """BUFF验证码综合识别器"""
+
+    def __init__(self, token: str):
+        """
+        初始化
+
+        Args:
+            token: 云码平台API Token
+        """
+        self.token = token
+        self.api_url = "http://api.jfbym.com/api/YmServer/customApi"
+
+    def detect_captcha_type(self, page) -> str:
+        """
+        检测验证码类型
+
+        Args:
+            page: Playwright Page对象
+
+        Returns:
+            str: 'icon_point' - 顺序点击验证码
+                 'space' - 文字特征点选验证码
+                 None - 未检测到验证码
+        """
+        try:
+            # 等待验证码容器出现
+            page.wait_for_selector(".yidun", timeout=10000)
+
+            # 方法1:检查CSS类名(最可靠)
+            class_name = page.evaluate("""
+                () => {
+                    const el = document.querySelector('.yidun');
+                    return el ? el.className : '';
+                }
+            """)
+
+            print(f"验证码容器class: {class_name}")
+
+            if "yidun--icon_point" in class_name:
+                return "icon_point"
+            elif "yidun--space" in class_name:
+                return "space"
+
+            # 方法2:检查提示文字
+            try:
+                tips_text = page.evaluate("""
+                    () => {
+                        const el = document.querySelector('.yidun_tips__text');
+                        return el ? el.textContent.trim() : '';
+                    }
+                """)
+
+                print(f"验证码提示文字: {tips_text}")
+
+                if tips_text.startswith("请依次点击"):
+                    return "icon_point"
+                elif tips_text.startswith("请点击"):
+                    return "space"
+            except:
+                pass
+
+            return None
+
+        except Exception as e:
+            print(f"验证码检测失败: {e}")
+            return None
+
+    def extract_captcha_image(self, page) -> str:
+        """
+        提取验证码图片并转为Base64
+
+        Args:
+            page: Playwright Page对象
+
+        Returns:
+            str: Base64编码的图片数据
+        """
+        try:
+            # 获取图片src
+            img_src = page.evaluate("""
+                () => {
+                    const img = document.querySelector('.yidun_bg-img');
+                    return img ? img.src : null;
+                }
+            """)
+
+            if not img_src:
+                print("未找到验证码图片")
+                return None
+
+            print(f"验证码图片地址: {img_src[:100]}...")
+
+            if img_src.startswith("data:image"):
+                # 提取base64数据部分
+                base64_data = img_src.split(",")[1]
+                return base64_data
+            elif img_src.startswith("http"):
+                # 下载图片
+                response = requests.get(img_src)
+                if response.status_code == 200:
+                    return base64.b64encode(response.content).decode()
+
+            return None
+
+        except Exception as e:
+            print(f"提取验证码图片失败: {e}")
+            return None
+
+    def extract_tip_text(self, page) -> str:
+        """
+        提取验证码提示文字
+
+        Args:
+            page: Playwright Page对象
+
+        Returns:
+            str: 提示文字
+        """
+        try:
+            tip_text = page.evaluate("""
+                () => {
+                    const el = document.querySelector('.yidun_tips__text');
+                    return el ? el.textContent.trim() : '';
+                }
+            """)
+            print(f"验证码提示文字: {tip_text}")
+            return tip_text
+        except:
+            return ""
+
+    def call_api(self, image_base64: str, captcha_type: str, tip_text: str = "") -> dict:
+        """
+        调用云码API
+
+        Args:
+            image_base64: Base64编码的图片数据
+            captcha_type: 验证码类型
+            tip_text: 提示文字
+
+        Returns:
+            dict: API响应结果
+        """
+        # 根据类型选择接口ID
+        type_ids = {
+            "icon_point": "317",
+            "space": "94"
+        }
+
+        type_id = type_ids.get(captcha_type)
+        if not type_id:
+            return {"code": -1, "msg": "未知的验证码类型"}
+
+        payload = {
+            "token": self.token,
+            "type": type_id,
+            "image": image_base64
+        }
+
+        # 文字点选类型可能需要传递提示文字
+        if captcha_type == "space" and tip_text:
+            payload["extra"] = tip_text
+
+        headers = {"Content-Type": "application/json"}
+
+        try:
+            response = requests.post(
+                self.api_url,
+                json=payload,
+                headers=headers,
+                timeout=30
+            )
+            return response.json()
+
+        except Exception as e:
+            print(f"API调用失败: {e}")
+            return {"code": -1, "msg": str(e)}
+
+    def parse_result(self, api_result: dict) -> list:
+        """
+        解析API返回结果
+
+        Args:
+            api_result: API响应结果
+
+        Returns:
+            list: 坐标列表
+        """
+        try:
+            if api_result.get("code") == 10000:
+                data = api_result.get("data", [])
+                if data:
+                    result_str = data[0].get("data", "")
+                    print(f"云码返回坐标: {result_str}")
+
+                    if result_str.startswith("[") and result_str.endswith("]"):
+                        return json.loads(result_str)
+                    else:
+                        try:
+                            return eval(result_str)
+                        except:
+                            print(f"无法解析返回数据: {result_str}")
+                            return []
+            else:
+                print(f"识别失败: {api_result.get('msg')}")
+                return []
+
+        except Exception as e:
+            print(f"解析结果失败: {e}")
+            return []
+
+    def click_coordinates(self, page, coordinates: list):
+        """
+        执行点击操作
+
+        Args:
+            page: Playwright Page对象
+            coordinates: 坐标列表
+        """
+        try:
+            # 获取验证码图片元素
+            img_element = page.query_selector(".yidun_bg-img")
+            box = img_element.bounding_box()
+
+            if not box:
+                print("无法获取验证码图片位置")
+                return False
+
+            print(f"验证码图片位置: x={box['x']}, y={box['y']}, width={box['width']}, height={box['height']}")
+
+            # 执行点击
+            for i, coord in enumerate(coordinates):
+                x, y = coord
+
+                # 计算相对于图片的绝对坐标
+                abs_x = box["x"] + x
+                abs_y = box["y"] + y
+
+                # 移动并点击
+                page.mouse.click(abs_x, abs_y)
+                print(f"点击坐标 ({i+1}): ({abs_x}, {abs_y})")
+
+                # 每次点击后等待
+                if i < len(coordinates) - 1:
+                    time.sleep(0.5)
+
+            print(f"完成点击,共点击 {len(coordinates)} 次")
+            return True
+
+        except Exception as e:
+            print(f"点击操作失败: {e}")
+            return False
+
+    def solve(self, page) -> bool:
+        """
+        完整的验证码解决流程
+
+        Args:
+            page: Playwright Page对象
+
+        Returns:
+            bool: 解决成功返回True
+        """
+        # 1. 检测验证码类型
+        captcha_type = self.detect_captcha_type(page)
+
+        if captcha_type == "icon_point":
+            type_name = "顺序点击验证码(第一种)"
+            type_id = "317"
+        elif captcha_type == "space":
+            type_name = "文字特征点选验证码(第二种)"
+            type_id = "94"
+        else:
+            print("无法识别验证码类型或未检测到验证码")
+            return False
+
+        print("=" * 60)
+        print(f"检测到 {type_name}")
+        print(f"云码接口ID: {type_id}")
+        print("=" * 60)
+
+        # 2. 提取验证码图片
+        image_base64 = self.extract_captcha_image(page)
+        if not image_base64:
+            print("提取验证码图片失败")
+            return False
+
+        print("验证码图片提取成功")
+
+        # 3. 提取提示文字(仅第二种需要)
+        tip_text = ""
+        if captcha_type == "space":
+            tip_text = self.extract_tip_text(page)
+
+        # 4. 调用云码API
+        print("正在调用云码API识别...")
+        api_result = self.call_api(image_base64, captcha_type, tip_text)
+
+        if not api_result or api_result.get("code") != 10000:
+            print(f"API识别失败: {api_result}")
+            return False
+
+        print("API识别成功")
+
+        # 5. 解析结果
+        coordinates = self.parse_result(api_result)
+        if not coordinates:
+            print("解析坐标失败")
+            return False
+
+        print(f"获取到 {len(coordinates)} 个点击坐标")
+
+        # 6. 执行点击
+        if self.click_coordinates(page, coordinates):
+            print("点击完成")
+            return True
+        else:
+            print("点击失败")
+            return False
+
+
+def main():
+    """主函数"""
+    # 配置
+    TOKEN = "wVRyAIjI3hhJBicXndcs1ro9BN5UNMXIjW6SDWBFFyQ"  # 云码Token
+    TARGET_URL = "https://buff.163.com/goods/900482?from=market#tab=selling"
+
+    # 创建识别器
+    solver = BuffCaptchaSolver(TOKEN)
+
+    print("=" * 60)
+    print("BUFF验证码自动识别器(Playwright简化版)")
+    print("=" * 60)
+
+    with sync_playwright() as p:
+        # 启动浏览器
+        print("启动浏览器...")
+        browser = p.chromium.launch(
+            headless=False,  # False=显示浏览器窗口,方便调试
+            args=[
+                '--no-sandbox',
+                '--disable-setuid-sandbox',
+                '--disable-dev-shm-usage',
+                '--disable-gpu',
+                '--window-size=1920,1080',
+                '--start-maximized'
+            ]
+        )
+
+        # 创建上下文
+        context = browser.new_context(
+            viewport={'width': 1920, 'height': 1080},
+            user_agent='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
+        )
+
+        # 创建页面
+        page = context.new_page()
+
+        # 应用stealth防止被检测
+        apply_stealth(page)
+
+        try:
+            # 访问目标URL
+            print(f"正在访问: {TARGET_URL}")
+            page.goto(TARGET_URL)
+
+            # 等待页面加载
+            page.wait_for_load_state("networkidle")
+
+            print("页面加载完成")
+
+            # 设置session cookie防止验证码
+            print("设置session cookie...")
+            context.add_cookies([
+                {
+                    "name": "session",
+                    "value": "1-4AfdDg2Nxmk4zY5VbWUG73ZlpIb8VoCNiCdgaBMrzX4j2037343134",
+                    "domain": ".buff.163.com",
+                    "path": "/"
+                }
+            ])
+            print("session cookie设置成功")
+
+            # 刷新页面使cookie生效
+            page.reload()
+            page.wait_for_load_state("networkidle")
+            print("页面已刷新")
+
+            # 等待验证码出现(最多30秒)
+            print("等待验证码出现...")
+
+            # 尝试等待验证码
+            try:
+                page.wait_for_selector(".yidun", timeout=30000)
+                print("检测到验证码!")
+            except:
+                print("30秒内未检测到验证码")
+                print("可能的原因:")
+                print("1. 页面没有触发验证码")
+                print("2. 验证码已经被手动处理")
+                print("3. 页面结构已变化")
+                input("\n按Enter键关闭浏览器...")
+                browser.close()
+                return
+
+            # 等待验证码完全加载
+            time.sleep(2)
+
+            # 自动识别并解决验证码
+            if solver.solve(page):
+                print("\n验证码解决成功!")
+            else:
+                print("\n验证码解决失败")
+
+        except Exception as e:
+            print(f"错误: {e}")
+            import traceback
+            traceback.print_exc()
+
+        finally:
+            input("\n按Enter键关闭浏览器...")
+            browser.close()
+
+
+if __name__ == "__main__":
+    main()

+ 482 - 0
.history/云码验证码识别_综合版_简化版_20260204170143.py

@@ -0,0 +1,482 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+BUFF验证码自动识别器 - 使用Playwright(简化版)
+支持两种验证码类型:
+1. 顺序点击验证码(type=317)
+2. 文字特征点选验证码(type=94)
+
+使用方法:
+1. 安装依赖: pip install playwright && playwright install chromium
+2. 运行脚本: python3 云码验证码识别_综合版_简化版.py
+"""
+
+import base64
+import json
+import time
+import sys
+from playwright.sync_api import sync_playwright
+import requests
+
+
+def apply_stealth(page):
+    """
+    手动应用 stealth 脚本,防止被检测为自动化工具
+    """
+    page.add_init_script("""
+        Object.defineProperty(navigator, 'webdriver', {
+            get: () => undefined
+        });
+        Object.defineProperty(navigator, 'languages', {
+            get: () => ['zh-CN', 'zh', 'en-US', 'en']
+        });
+        Object.defineProperty(navigator, 'plugins', {
+            get: () => [1, 2, 3, 4, 5]
+        });
+        window.navigator.chrome = {
+            runtime: {},
+            loadTimes: function() {},
+            csi: function() {},
+            app: {}
+        };
+        Object.defineProperty(window, 'chrome', {
+            get: () => window.navigator.chrome
+        });
+        Object.defineProperty(navigator, 'platform', {
+            get: () => 'MacIntel'
+        });
+        Object.defineProperty(navigator, 'deviceMemory', {
+            get: () => 8
+        });
+        Object.defineProperty(navigator, 'hardwareConcurrency', {
+            get: () => 8
+        });
+    """)
+
+
+class BuffCaptchaSolver:
+    """BUFF验证码综合识别器"""
+
+    def __init__(self, token: str):
+        """
+        初始化
+
+        Args:
+            token: 云码平台API Token
+        """
+        self.token = token
+        self.api_url = "http://api.jfbym.com/api/YmServer/customApi"
+
+    def detect_captcha_type(self, page) -> str:
+        """
+        检测验证码类型
+
+        Args:
+            page: Playwright Page对象
+
+        Returns:
+            str: 'icon_point' - 顺序点击验证码
+                 'space' - 文字特征点选验证码
+                 None - 未检测到验证码
+        """
+        try:
+            # 等待验证码容器出现
+            page.wait_for_selector(".yidun", timeout=10000)
+
+            # 方法1:检查CSS类名(最可靠)
+            class_name = page.evaluate("""
+                () => {
+                    const el = document.querySelector('.yidun');
+                    return el ? el.className : '';
+                }
+            """)
+
+            print(f"验证码容器class: {class_name}")
+
+            if "yidun--icon_point" in class_name:
+                return "icon_point"
+            elif "yidun--space" in class_name:
+                return "space"
+
+            # 方法2:检查提示文字
+            try:
+                tips_text = page.evaluate("""
+                    () => {
+                        const el = document.querySelector('.yidun_tips__text');
+                        return el ? el.textContent.trim() : '';
+                    }
+                """)
+
+                print(f"验证码提示文字: {tips_text}")
+
+                if tips_text.startswith("请依次点击"):
+                    return "icon_point"
+                elif tips_text.startswith("请点击"):
+                    return "space"
+            except:
+                pass
+
+            return None
+
+        except Exception as e:
+            print(f"验证码检测失败: {e}")
+            return None
+
+    def extract_captcha_image(self, page) -> str:
+        """
+        提取验证码图片并转为Base64
+
+        Args:
+            page: Playwright Page对象
+
+        Returns:
+            str: Base64编码的图片数据
+        """
+        try:
+            # 获取图片src
+            img_src = page.evaluate("""
+                () => {
+                    const img = document.querySelector('.yidun_bg-img');
+                    return img ? img.src : null;
+                }
+            """)
+
+            if not img_src:
+                print("未找到验证码图片")
+                return None
+
+            print(f"验证码图片地址: {img_src[:100]}...")
+
+            if img_src.startswith("data:image"):
+                # 提取base64数据部分
+                base64_data = img_src.split(",")[1]
+                return base64_data
+            elif img_src.startswith("http"):
+                # 下载图片
+                response = requests.get(img_src)
+                if response.status_code == 200:
+                    return base64.b64encode(response.content).decode()
+
+            return None
+
+        except Exception as e:
+            print(f"提取验证码图片失败: {e}")
+            return None
+
+    def extract_tip_text(self, page) -> str:
+        """
+        提取验证码提示文字
+
+        Args:
+            page: Playwright Page对象
+
+        Returns:
+            str: 提示文字
+        """
+        try:
+            tip_text = page.evaluate("""
+                () => {
+                    const el = document.querySelector('.yidun_tips__text');
+                    return el ? el.textContent.trim() : '';
+                }
+            """)
+            print(f"验证码提示文字: {tip_text}")
+            return tip_text
+        except:
+            return ""
+
+    def call_api(self, image_base64: str, captcha_type: str, tip_text: str = "") -> dict:
+        """
+        调用云码API
+
+        Args:
+            image_base64: Base64编码的图片数据
+            captcha_type: 验证码类型
+            tip_text: 提示文字
+
+        Returns:
+            dict: API响应结果
+        """
+        # 根据类型选择接口ID
+        type_ids = {
+            "icon_point": "317",
+            "space": "30109"  # 文字特征点选验证码(第二种)- 根据云码文档
+        }
+
+        type_id = type_ids.get(captcha_type)
+        if not type_id:
+            return {"code": -1, "msg": "未知的验证码类型"}
+
+        payload = {
+            "token": self.token,
+            "type": type_id,
+            "image": image_base64
+        }
+
+        # 文字点选类型可能需要传递提示文字
+        if captcha_type == "space" and tip_text:
+            payload["extra"] = tip_text
+
+        headers = {"Content-Type": "application/json"}
+
+        try:
+            response = requests.post(
+                self.api_url,
+                json=payload,
+                headers=headers,
+                timeout=30
+            )
+            return response.json()
+
+        except Exception as e:
+            print(f"API调用失败: {e}")
+            return {"code": -1, "msg": str(e)}
+
+    def parse_result(self, api_result: dict) -> list:
+        """
+        解析API返回结果
+
+        Args:
+            api_result: API响应结果
+
+        Returns:
+            list: 坐标列表
+        """
+        try:
+            if api_result.get("code") == 10000:
+                data = api_result.get("data", [])
+                if data:
+                    result_str = data[0].get("data", "")
+                    print(f"云码返回坐标: {result_str}")
+
+                    if result_str.startswith("[") and result_str.endswith("]"):
+                        return json.loads(result_str)
+                    else:
+                        try:
+                            return eval(result_str)
+                        except:
+                            print(f"无法解析返回数据: {result_str}")
+                            return []
+            else:
+                print(f"识别失败: {api_result.get('msg')}")
+                return []
+
+        except Exception as e:
+            print(f"解析结果失败: {e}")
+            return []
+
+    def click_coordinates(self, page, coordinates: list):
+        """
+        执行点击操作
+
+        Args:
+            page: Playwright Page对象
+            coordinates: 坐标列表
+        """
+        try:
+            # 获取验证码图片元素
+            img_element = page.query_selector(".yidun_bg-img")
+            box = img_element.bounding_box()
+
+            if not box:
+                print("无法获取验证码图片位置")
+                return False
+
+            print(f"验证码图片位置: x={box['x']}, y={box['y']}, width={box['width']}, height={box['height']}")
+
+            # 执行点击
+            for i, coord in enumerate(coordinates):
+                x, y = coord
+
+                # 计算相对于图片的绝对坐标
+                abs_x = box["x"] + x
+                abs_y = box["y"] + y
+
+                # 移动并点击
+                page.mouse.click(abs_x, abs_y)
+                print(f"点击坐标 ({i+1}): ({abs_x}, {abs_y})")
+
+                # 每次点击后等待
+                if i < len(coordinates) - 1:
+                    time.sleep(0.5)
+
+            print(f"完成点击,共点击 {len(coordinates)} 次")
+            return True
+
+        except Exception as e:
+            print(f"点击操作失败: {e}")
+            return False
+
+    def solve(self, page) -> bool:
+        """
+        完整的验证码解决流程
+
+        Args:
+            page: Playwright Page对象
+
+        Returns:
+            bool: 解决成功返回True
+        """
+        # 1. 检测验证码类型
+        captcha_type = self.detect_captcha_type(page)
+
+        if captcha_type == "icon_point":
+            type_name = "顺序点击验证码(第一种)"
+            type_id = "317"
+        elif captcha_type == "space":
+            type_name = "文字特征点选验证码(第二种)"
+            type_id = "94"
+        else:
+            print("无法识别验证码类型或未检测到验证码")
+            return False
+
+        print("=" * 60)
+        print(f"检测到 {type_name}")
+        print(f"云码接口ID: {type_id}")
+        print("=" * 60)
+
+        # 2. 提取验证码图片
+        image_base64 = self.extract_captcha_image(page)
+        if not image_base64:
+            print("提取验证码图片失败")
+            return False
+
+        print("验证码图片提取成功")
+
+        # 3. 提取提示文字(仅第二种需要)
+        tip_text = ""
+        if captcha_type == "space":
+            tip_text = self.extract_tip_text(page)
+
+        # 4. 调用云码API
+        print("正在调用云码API识别...")
+        api_result = self.call_api(image_base64, captcha_type, tip_text)
+
+        if not api_result or api_result.get("code") != 10000:
+            print(f"API识别失败: {api_result}")
+            return False
+
+        print("API识别成功")
+
+        # 5. 解析结果
+        coordinates = self.parse_result(api_result)
+        if not coordinates:
+            print("解析坐标失败")
+            return False
+
+        print(f"获取到 {len(coordinates)} 个点击坐标")
+
+        # 6. 执行点击
+        if self.click_coordinates(page, coordinates):
+            print("点击完成")
+            return True
+        else:
+            print("点击失败")
+            return False
+
+
+def main():
+    """主函数"""
+    # 配置
+    TOKEN = "wVRyAIjI3hhJBicXndcs1ro9BN5UNMXIjW6SDWBFFyQ"  # 云码Token
+    TARGET_URL = "https://buff.163.com/goods/900482?from=market#tab=selling"
+
+    # 创建识别器
+    solver = BuffCaptchaSolver(TOKEN)
+
+    print("=" * 60)
+    print("BUFF验证码自动识别器(Playwright简化版)")
+    print("=" * 60)
+
+    with sync_playwright() as p:
+        # 启动浏览器
+        print("启动浏览器...")
+        browser = p.chromium.launch(
+            headless=False,  # False=显示浏览器窗口,方便调试
+            args=[
+                '--no-sandbox',
+                '--disable-setuid-sandbox',
+                '--disable-dev-shm-usage',
+                '--disable-gpu',
+                '--window-size=1920,1080',
+                '--start-maximized'
+            ]
+        )
+
+        # 创建上下文
+        context = browser.new_context(
+            viewport={'width': 1920, 'height': 1080},
+            user_agent='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
+        )
+
+        # 创建页面
+        page = context.new_page()
+
+        # 应用stealth防止被检测
+        apply_stealth(page)
+
+        try:
+            # 访问目标URL
+            print(f"正在访问: {TARGET_URL}")
+            page.goto(TARGET_URL)
+
+            # 等待页面加载
+            page.wait_for_load_state("networkidle")
+
+            print("页面加载完成")
+
+            # 设置session cookie防止验证码
+            print("设置session cookie...")
+            context.add_cookies([
+                {
+                    "name": "session",
+                    "value": "1-4AfdDg2Nxmk4zY5VbWUG73ZlpIb8VoCNiCdgaBMrzX4j2037343134",
+                    "domain": ".buff.163.com",
+                    "path": "/"
+                }
+            ])
+            print("session cookie设置成功")
+
+            # 刷新页面使cookie生效
+            page.reload()
+            page.wait_for_load_state("networkidle")
+            print("页面已刷新")
+
+            # 等待验证码出现(最多30秒)
+            print("等待验证码出现...")
+
+            # 尝试等待验证码
+            try:
+                page.wait_for_selector(".yidun", timeout=30000)
+                print("检测到验证码!")
+            except:
+                print("30秒内未检测到验证码")
+                print("可能的原因:")
+                print("1. 页面没有触发验证码")
+                print("2. 验证码已经被手动处理")
+                print("3. 页面结构已变化")
+                input("\n按Enter键关闭浏览器...")
+                browser.close()
+                return
+
+            # 等待验证码完全加载
+            time.sleep(2)
+
+            # 自动识别并解决验证码
+            if solver.solve(page):
+                print("\n验证码解决成功!")
+            else:
+                print("\n验证码解决失败")
+
+        except Exception as e:
+            print(f"错误: {e}")
+            import traceback
+            traceback.print_exc()
+
+        finally:
+            input("\n按Enter键关闭浏览器...")
+            browser.close()
+
+
+if __name__ == "__main__":
+    main()

+ 512 - 0
.history/云码验证码识别_综合版_简化版_20260204170903.py

@@ -0,0 +1,512 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+BUFF验证码自动识别器 - 使用Playwright(简化版)
+支持两种验证码类型:
+1. 顺序点击验证码(type=317)
+2. 文字特征点选验证码(type=94)
+
+使用方法:
+1. 安装依赖: pip install playwright && playwright install chromium
+2. 运行脚本: python3 云码验证码识别_综合版_简化版.py
+"""
+
+import base64
+import json
+import time
+import sys
+from playwright.sync_api import sync_playwright
+import requests
+
+
+def apply_stealth(page):
+    """
+    手动应用 stealth 脚本,防止被检测为自动化工具
+    """
+    page.add_init_script("""
+        Object.defineProperty(navigator, 'webdriver', {
+            get: () => undefined
+        });
+        Object.defineProperty(navigator, 'languages', {
+            get: () => ['zh-CN', 'zh', 'en-US', 'en']
+        });
+        Object.defineProperty(navigator, 'plugins', {
+            get: () => [1, 2, 3, 4, 5]
+        });
+        window.navigator.chrome = {
+            runtime: {},
+            loadTimes: function() {},
+            csi: function() {},
+            app: {}
+        };
+        Object.defineProperty(window, 'chrome', {
+            get: () => window.navigator.chrome
+        });
+        Object.defineProperty(navigator, 'platform', {
+            get: () => 'MacIntel'
+        });
+        Object.defineProperty(navigator, 'deviceMemory', {
+            get: () => 8
+        });
+        Object.defineProperty(navigator, 'hardwareConcurrency', {
+            get: () => 8
+        });
+    """)
+
+
+class BuffCaptchaSolver:
+    """BUFF验证码综合识别器"""
+
+    def __init__(self, token: str):
+        """
+        初始化
+
+        Args:
+            token: 云码平台API Token
+        """
+        self.token = token
+        self.api_url = "http://api.jfbym.com/api/YmServer/customApi"
+
+    def detect_captcha_type(self, page) -> str:
+        """
+        检测验证码类型
+
+        Args:
+            page: Playwright Page对象
+
+        Returns:
+            str: 'icon_point' - 顺序点击验证码
+                 'space' - 文字特征点选验证码
+                 None - 未检测到验证码
+        """
+        try:
+            # 等待验证码容器出现
+            page.wait_for_selector(".yidun", timeout=10000)
+
+            # 方法1:检查CSS类名(最可靠)
+            class_name = page.evaluate("""
+                () => {
+                    const el = document.querySelector('.yidun');
+                    return el ? el.className : '';
+                }
+            """)
+
+            print(f"验证码容器class: {class_name}")
+
+            if "yidun--icon_point" in class_name:
+                return "icon_point"
+            elif "yidun--space" in class_name:
+                return "space"
+
+            # 方法2:检查提示文字
+            try:
+                tips_text = page.evaluate("""
+                    () => {
+                        const el = document.querySelector('.yidun_tips__text');
+                        return el ? el.textContent.trim() : '';
+                    }
+                """)
+
+                print(f"验证码提示文字: {tips_text}")
+
+                if tips_text.startswith("请依次点击"):
+                    return "icon_point"
+                elif tips_text.startswith("请点击"):
+                    return "space"
+            except:
+                pass
+
+            return None
+
+        except Exception as e:
+            print(f"验证码检测失败: {e}")
+            return None
+
+    def extract_captcha_image(self, page) -> str:
+        """
+        提取验证码图片并转为Base64
+
+        Args:
+            page: Playwright Page对象
+
+        Returns:
+            str: Base64编码的图片数据
+        """
+        try:
+            # 获取图片src
+            img_src = page.evaluate("""
+                () => {
+                    const img = document.querySelector('.yidun_bg-img');
+                    return img ? img.src : null;
+                }
+            """)
+
+            if not img_src:
+                print("未找到验证码图片")
+                return None
+
+            print(f"验证码图片地址: {img_src[:100]}...")
+
+            if img_src.startswith("data:image"):
+                # 提取base64数据部分
+                base64_data = img_src.split(",")[1]
+                return base64_data
+            elif img_src.startswith("http"):
+                # 下载图片
+                response = requests.get(img_src)
+                if response.status_code == 200:
+                    return base64.b64encode(response.content).decode()
+
+            return None
+
+        except Exception as e:
+            print(f"提取验证码图片失败: {e}")
+            return None
+
+    def extract_tip_text(self, page) -> str:
+        """
+        提取验证码提示文字
+
+        Args:
+            page: Playwright Page对象
+
+        Returns:
+            str: 提示文字
+        """
+        try:
+            tip_text = page.evaluate("""
+                () => {
+                    const el = document.querySelector('.yidun_tips__text');
+                    return el ? el.textContent.trim() : '';
+                }
+            """)
+            print(f"验证码提示文字: {tip_text}")
+            return tip_text
+        except:
+            return ""
+
+    def call_api(self, image_base64: str, captcha_type: str, tip_text: str = "") -> dict:
+        """
+        调用云码API
+
+        Args:
+            image_base64: Base64编码的图片数据
+            captcha_type: 验证码类型
+            tip_text: 提示文字
+
+        Returns:
+            dict: API响应结果
+        """
+        # 根据类型选择接口ID
+        type_ids = {
+            "icon_point": "317",
+            "space": "30109"  # 文字特征点选验证码(第二种)- 根据云码文档
+        }
+
+        type_id = type_ids.get(captcha_type)
+        if not type_id:
+            return {"code": -1, "msg": "未知的验证码类型"}
+
+        payload = {
+            "token": self.token,
+            "type": type_id,
+            "image": image_base64
+        }
+
+        # 文字点选类型可能需要传递提示文字
+        if captcha_type == "space" and tip_text:
+            payload["extra"] = tip_text
+
+        headers = {"Content-Type": "application/json"}
+
+        try:
+            response = requests.post(
+                self.api_url,
+                json=payload,
+                headers=headers,
+                timeout=30
+            )
+            return response.json()
+
+        except Exception as e:
+            print(f"API调用失败: {e}")
+            return {"code": -1, "msg": str(e)}
+
+    def parse_result(self, api_result: dict) -> list:
+        """
+        解析API返回结果
+
+        Args:
+            api_result: API响应结果
+
+        Returns:
+            list: 坐标列表
+        """
+        try:
+            print(f"API完整返回: {api_result}")
+
+            if api_result.get("code") == 10000:
+                data = api_result.get("data", {})
+                print(f"data字段: {data}, 类型: {type(data)}")
+
+                # 新的返回格式:data 是字典
+                if isinstance(data, dict):
+                    inner_code = data.get("code", 0)
+                    result_str = data.get("data", "")
+                    print(f"inner_code: {inner_code}, result_str: {result_str}")
+
+                    if inner_code == 0 and result_str:
+                        # 格式1: "156,61" 逗号分隔的坐标
+                        if isinstance(result_str, str) and "," in result_str:
+                            parts = result_str.split(",")
+                            if len(parts) == 2:
+                                try:
+                                    x, y = int(parts[0].strip()), int(parts[1].strip())
+                                    print(f"解析到坐标: [{x}, {y}]")
+                                    return [[x, y]]
+                                except ValueError:
+                                    pass
+
+                        # 格式2: "[x, y]" JSON数组格式
+                        if isinstance(result_str, str) and result_str.startswith("["):
+                            return json.loads(result_str)
+
+                # 旧的返回格式:data 是列表
+                elif isinstance(data, list) and data:
+                    result_str = data[0].get("data", "")
+                    print(f"result_str: {result_str}")
+
+                    if result_str.startswith("[") and result_str.endswith("]"):
+                        return json.loads(result_str)
+                    else:
+                        try:
+                            return eval(result_str)
+                        except:
+                            print(f"无法解析返回数据: {result_str}")
+                            return []
+
+            else:
+                print(f"识别失败: {api_result.get('msg')}")
+                return []
+
+        except Exception as e:
+            print(f"解析结果失败: {e}")
+            import traceback
+            traceback.print_exc()
+            return []
+
+    def click_coordinates(self, page, coordinates: list):
+        """
+        执行点击操作
+
+        Args:
+            page: Playwright Page对象
+            coordinates: 坐标列表
+        """
+        try:
+            # 获取验证码图片元素
+            img_element = page.query_selector(".yidun_bg-img")
+            box = img_element.bounding_box()
+
+            if not box:
+                print("无法获取验证码图片位置")
+                return False
+
+            print(f"验证码图片位置: x={box['x']}, y={box['y']}, width={box['width']}, height={box['height']}")
+
+            # 执行点击
+            for i, coord in enumerate(coordinates):
+                x, y = coord
+
+                # 计算相对于图片的绝对坐标
+                abs_x = box["x"] + x
+                abs_y = box["y"] + y
+
+                # 移动并点击
+                page.mouse.click(abs_x, abs_y)
+                print(f"点击坐标 ({i+1}): ({abs_x}, {abs_y})")
+
+                # 每次点击后等待
+                if i < len(coordinates) - 1:
+                    time.sleep(0.5)
+
+            print(f"完成点击,共点击 {len(coordinates)} 次")
+            return True
+
+        except Exception as e:
+            print(f"点击操作失败: {e}")
+            return False
+
+    def solve(self, page) -> bool:
+        """
+        完整的验证码解决流程
+
+        Args:
+            page: Playwright Page对象
+
+        Returns:
+            bool: 解决成功返回True
+        """
+        # 1. 检测验证码类型
+        captcha_type = self.detect_captcha_type(page)
+
+        if captcha_type == "icon_point":
+            type_name = "顺序点击验证码(第一种)"
+            type_id = "317"
+        elif captcha_type == "space":
+            type_name = "文字特征点选验证码(第二种)"
+            type_id = "94"
+        else:
+            print("无法识别验证码类型或未检测到验证码")
+            return False
+
+        print("=" * 60)
+        print(f"检测到 {type_name}")
+        print(f"云码接口ID: {type_id}")
+        print("=" * 60)
+
+        # 2. 提取验证码图片
+        image_base64 = self.extract_captcha_image(page)
+        if not image_base64:
+            print("提取验证码图片失败")
+            return False
+
+        print("验证码图片提取成功")
+
+        # 3. 提取提示文字(仅第二种需要)
+        tip_text = ""
+        if captcha_type == "space":
+            tip_text = self.extract_tip_text(page)
+
+        # 4. 调用云码API
+        print("正在调用云码API识别...")
+        api_result = self.call_api(image_base64, captcha_type, tip_text)
+
+        if not api_result or api_result.get("code") != 10000:
+            print(f"API识别失败: {api_result}")
+            return False
+
+        print("API识别成功")
+
+        # 5. 解析结果
+        coordinates = self.parse_result(api_result)
+        if not coordinates:
+            print("解析坐标失败")
+            return False
+
+        print(f"获取到 {len(coordinates)} 个点击坐标")
+
+        # 6. 执行点击
+        if self.click_coordinates(page, coordinates):
+            print("点击完成")
+            return True
+        else:
+            print("点击失败")
+            return False
+
+
+def main():
+    """主函数"""
+    # 配置
+    TOKEN = "wVRyAIjI3hhJBicXndcs1ro9BN5UNMXIjW6SDWBFFyQ"  # 云码Token
+    TARGET_URL = "https://buff.163.com/goods/900482?from=market#tab=selling"
+
+    # 创建识别器
+    solver = BuffCaptchaSolver(TOKEN)
+
+    print("=" * 60)
+    print("BUFF验证码自动识别器(Playwright简化版)")
+    print("=" * 60)
+
+    with sync_playwright() as p:
+        # 启动浏览器
+        print("启动浏览器...")
+        browser = p.chromium.launch(
+            headless=False,  # False=显示浏览器窗口,方便调试
+            args=[
+                '--no-sandbox',
+                '--disable-setuid-sandbox',
+                '--disable-dev-shm-usage',
+                '--disable-gpu',
+                '--window-size=1920,1080',
+                '--start-maximized'
+            ]
+        )
+
+        # 创建上下文
+        context = browser.new_context(
+            viewport={'width': 1920, 'height': 1080},
+            user_agent='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
+        )
+
+        # 创建页面
+        page = context.new_page()
+
+        # 应用stealth防止被检测
+        apply_stealth(page)
+
+        try:
+            # 访问目标URL
+            print(f"正在访问: {TARGET_URL}")
+            page.goto(TARGET_URL)
+
+            # 等待页面加载
+            page.wait_for_load_state("networkidle")
+
+            print("页面加载完成")
+
+            # 设置session cookie防止验证码
+            print("设置session cookie...")
+            context.add_cookies([
+                {
+                    "name": "session",
+                    "value": "1-29ene-taFfBv6_EK2btogRrVsqsQsuuJooUGnxBDUwLI2043282888",
+                    "domain": ".buff.163.com",
+                    "path": "/"
+                }
+            ])
+            print("session cookie设置成功")
+
+            # 刷新页面使cookie生效
+            page.reload()
+            page.wait_for_load_state("networkidle")
+            print("页面已刷新")
+
+            # 等待验证码出现(最多30秒)
+            print("等待验证码出现...")
+
+            # 尝试等待验证码
+            try:
+                page.wait_for_selector(".yidun", timeout=30000)
+                print("检测到验证码!")
+            except:
+                print("30秒内未检测到验证码")
+                print("可能的原因:")
+                print("1. 页面没有触发验证码")
+                print("2. 验证码已经被手动处理")
+                print("3. 页面结构已变化")
+                input("\n按Enter键关闭浏览器...")
+                browser.close()
+                return
+
+            # 等待验证码完全加载
+            time.sleep(2)
+
+            # 自动识别并解决验证码
+            if solver.solve(page):
+                print("\n验证码解决成功!")
+            else:
+                print("\n验证码解决失败")
+
+        except Exception as e:
+            print(f"错误: {e}")
+            import traceback
+            traceback.print_exc()
+
+        finally:
+            input("\n按Enter键关闭浏览器...")
+            browser.close()
+
+
+if __name__ == "__main__":
+    main()

BIN
SendMsg/.DS_Store


BIN
buff/.DS_Store


BIN
buff/BUFF/.DS_Store


ファイルの差分が大きいため隠しています
+ 0 - 0
buff平台两种验证码/第一种验证码01.html


ファイルの差分が大きいため隠しています
+ 0 - 0
buff平台两种验证码/第一种验证码02.html


ファイルの差分が大きいため隠しています
+ 0 - 0
buff平台两种验证码/第一种验证码03.html


+ 57 - 0
buff平台两种验证码/第一种验证码云码接口说明.md

@@ -0,0 +1,57 @@
+价格: 0.01元/次
+本接口只支持3小时内报错!
+类型: 通用图标点选类型
+
+说明:
+
+1.本接口是通用图标点选接口,只能传一张图(截图或者拼接图),图上需要有如何点击的信息;
+
+2.本接口有个必填参数direction,如果待识别图上告知如何点击的信息在图片上部,参数值为top,在下则值为bottom;
+
+3.图上最好不要有其他的无关干扰,只需要附上如何点击的顺序图标即可。
+
+4.click_num参数是可选参数,指的是,需要返回的坐标个数。
+
+只支持HTTP中的Post方法,同时Content-Type请设置为application/json或者application/x-www-form-urlencoded
+
+跨平台http接口,请仔细查看各类型的http接口,仅支持post传参
+
+http api 接口 文档
+
+1.1.请求地址
+
+http://api.jfbym.com/api/YmServer/customApi
+
+1.2.参数列表
+
+参数名称	传值类型	说明	是否必须
+image	string	需要识别图片的base64字符串	是
+direction	string	bottom/top(提示信息相对位置,)	是
+click_num	string	需要返回坐标数,非必填	是
+token	string	用户中心Token	是
+type	string	30332	是
+ 
+
+2.1.反参
+ 
+
+参数名称	传值类型	说明
+code	int	状态值
+msg	string	请求说明
+data	array	打码数据
+--code	int	打码服务状态
+--data	string	识别结果
+--time	string	打码服务时长
+2.2.code列表
+code	说明
+10000	识别成功
+10001	参数错误
+10002	余额不足
+10003	无此访问权限(未获取到有效token)
+10004	无此验证类型(type参数值不对)
+10005	网络拥塞
+10006	数据包过载
+10007	服务繁忙(接口模型无法处理图片参数)
+10008	网络错误,请稍后重试
+10009	结果准备中,请稍后再试
+10010	请求结束

ファイルの差分が大きいため隠しています
+ 0 - 0
buff平台两种验证码/第二种验证码01.html


ファイルの差分が大きいため隠しています
+ 0 - 0
buff平台两种验证码/第二种验证码02.html


ファイルの差分が大きいため隠しています
+ 0 - 0
buff平台两种验证码/第二种验证码03.html


+ 50 - 0
buff平台两种验证码/第二种验证码云码接口说明.md

@@ -0,0 +1,50 @@
+价格: 0.012元/次
+本接口只支持3小时内报错!
+注意:
+
+类型: 定制-空间推理点选1(xd,原图,extra="请点击xxx")
+
+说明:带有定制前缀的,是搜集该类图片训练的机器学习模型;请用户检查自己的打码图是否和图例类似,  测试无误后可使用.
+
+1.如遇到服务繁忙,大概率是图片base64的问题,请看常见问题里的:关于base64格式图片的问题;如果确认图片base64无问题,请联系工作人员解决.
+
+2.如遇到无权限访问,用户中心秘钥重置下,再填到代码里的token试试;如果还是不行,请联系工作人员解决.
+
+只支持HTTP中的Post方法,同时Content-Type请设置为application/json或者application/x-www-form-urlencoded
+
+跨平台http接口,请仔细查看各类型的http接口,仅支持post传参
+
+http api 接口 文档
+
+1.1.请求地址
+
+http://api.jfbym.com/api/YmServer/customApi
+
+1.2.参数列表
+
+参数名称	传值类型	说明	是否必须
+image	string	需要识别图片的base64字符串	是
+extra	string	描述内容	是
+token	string	用户中心Token	是
+type	string	30109	是
+2.1.反参
+参数名称	传值类型	说明
+code	int	状态值
+msg	string	请求说明
+data	array	打码数据
+--code	int	打码服务状态
+--data	string	数英计算:答案; 滑块:像素距离; 旋转:角度; 坐标类型:按顺序坐标...
+--time	string	打码服务时长
+2.2.code列表
+code	说明
+10000	识别成功
+10001	参数错误
+10002	余额不足
+10003	无此访问权限
+10004	无此验证类型
+10005	网络拥塞
+10006	数据包过载
+10007	服务繁忙
+10008	网络错误,请稍后重试
+10009	结果准备中,请稍后再试
+10010	请求结束

+ 304 - 0
云码验证码识别_第一种验证码.py

@@ -0,0 +1,304 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+云码验证码识别 - 第一种验证码(顺序点击图标)
+接口文档: https://www.jfbym.com/test/317.html
+"""
+
+import base64
+import json
+import time
+import requests
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.common.action_chains import ActionChains
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+
+
+class YunmaIconPointCaptcha:
+    """云码顺序点击验证码识别器"""
+
+    def __init__(self, token: str):
+        """
+        初始化
+        
+        Args:
+            token: 云码平台API Token
+        """
+        self.token = token
+        self.api_url = "http://api.jfbym.com/api/YmServer/customApi"
+        self.type_id = "317"  # 顺序点击验证码接口ID
+
+    def identify_type(self, driver) -> bool:
+        """
+        识别是否为顺序点击验证码
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            bool: 是顺序点击验证码返回True
+        """
+        try:
+            # 查找验证码容器
+            captcha_container = driver.find_element(By.CLASS_NAME, "yidun")
+            class_name = captcha_container.get_attribute("class")
+            
+            # 检查是否包含icon_point类名
+            if "yidun--icon_point" in class_name:
+                return True
+            
+            # 备选:检查提示文字
+            try:
+                tips_element = driver.find_element(By.CSS_SELECTOR, ".yidun_tips__text")
+                tips_text = tips_element.text.strip()
+                if tips_text.startswith("请依次点击"):
+                    return True
+            except:
+                pass
+            
+            return False
+            
+        except Exception as e:
+            print(f"识别验证码类型失败: {e}")
+            return False
+
+    def extract_captcha_image(self, driver) -> str:
+        """
+        提取验证码图片并转为Base64
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            str: Base64编码的图片数据
+        """
+        try:
+            # 查找验证码背景图片
+            img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
+            
+            # 获取图片src
+            img_src = img_element.get_attribute("src")
+            print(f"验证码图片地址: {img_src}")
+            
+            # 如果是base64格式
+            if img_src.startswith("data:image"):
+                # 提取base64数据部分
+                base64_data = img_src.split(",")[1]
+                return base64_data
+            
+            # 如果是URL,下载图片
+            elif img_src.startswith("http"):
+                response = requests.get(img_src)
+                if response.status_code == 200:
+                    # 转为base64
+                    return base64.b64encode(response.content).decode()
+            
+            return None
+            
+        except Exception as e:
+            print(f"提取验证码图片失败: {e}")
+            return None
+
+    def call_api(self, image_base64: str) -> dict:
+        """
+        调用云码API识别验证码
+        
+        Args:
+            image_base64: Base64编码的图片数据
+            
+        Returns:
+            dict: API响应结果
+        """
+        payload = {
+            "token": self.token,
+            "type": self.type_id,
+            "image": image_base64
+        }
+        
+        headers = {
+            "Content-Type": "application/json"
+        }
+        
+        try:
+            response = requests.post(
+                self.api_url, 
+                json=payload, 
+                headers=headers,
+                timeout=30
+            )
+            
+            return response.json()
+            
+        except Exception as e:
+            print(f"API调用失败: {e}")
+            return {"code": -1, "msg": str(e)}
+
+    def parse_result(self, api_result: dict) -> list:
+        """
+        解析API返回结果
+        
+        Args:
+            api_result: API响应结果
+            
+        Returns:
+            list: 坐标列表 [[x1, y1], [x2, y2], ...]
+        """
+        try:
+            if api_result.get("code") == 10000:
+                # 识别成功
+                data = api_result.get("data", [])
+                if data:
+                    # 解析返回的坐标数据
+                    result_str = data[0].get("data", "")
+                    
+                    # 尝试解析为坐标列表
+                    if result_str.startswith("[[") and result_str.endswith("]]"):
+                        return json.loads(result_str)
+                    else:
+                        # 可能是其他格式,尝试eval
+                        try:
+                            return eval(result_str)
+                        except:
+                            print(f"无法解析返回数据: {result_str}")
+                            return []
+                
+            else:
+                print(f"识别失败: {api_result.get('msg')}")
+                return []
+                
+        except Exception as e:
+            print(f"解析结果失败: {e}")
+            return []
+
+    def click_coordinates(self, driver, coordinates: list):
+        """
+        执行点击操作
+        
+        Args:
+            driver: Selenium WebDriver实例
+            coordinates: 坐标列表
+        """
+        try:
+            # 查找验证码图片元素
+            img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
+            location = img_element.location
+            size = img_element.size
+            
+            # 创建Actions链
+            actions = ActionChains(driver)
+            
+            for i, coord in enumerate(coordinates):
+                x, y = coord
+                
+                # 移动到指定位置并点击
+                actions.move_to_element_with_offset(img_element, x, y)
+                actions.click()
+                
+                # 每次点击后等待一下
+                if i < len(coordinates) - 1:
+                    actions.pause(0.3)
+            
+            actions.perform()
+            print(f"完成点击,共点击 {len(coordinates)} 次")
+            
+            return True
+            
+        except Exception as e:
+            print(f"点击操作失败: {e}")
+            return False
+
+    def solve(self, driver) -> bool:
+        """
+        完整的验证码解决流程
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            bool: 解决成功返回True
+        """
+        print("=" * 50)
+        print("开始处理顺序点击验证码(第一种)")
+        print("=" * 50)
+        
+        # 1. 识别验证码类型
+        if not self.identify_type(driver):
+            print("❌ 不是顺序点击验证码")
+            return False
+        
+        print("✅ 确认为顺序点击验证码")
+        
+        # 2. 提取验证码图片
+        image_base64 = self.extract_captcha_image(driver)
+        if not image_base64:
+            print("❌ 提取验证码图片失败")
+            return False
+        
+        print("✅ 验证码图片提取成功")
+        
+        # 3. 调用云码API
+        print("🔄 正在调用云码API识别...")
+        api_result = self.call_api(image_base64)
+        
+        if not api_result or api_result.get("code") != 10000:
+            print(f"❌ API识别失败: {api_result}")
+            return False
+        
+        print("✅ API识别成功")
+        
+        # 4. 解析结果
+        coordinates = self.parse_result(api_result)
+        if not coordinates:
+            print("❌ 解析坐标失败")
+            return False
+        
+        print(f"📍 获取到 {len(coordinates)} 个点击坐标: {coordinates}")
+        
+        # 5. 执行点击
+        if self.click_coordinates(driver, coordinates):
+            print("✅ 点击完成")
+            return True
+        else:
+            print("❌ 点击失败")
+            return False
+
+
+def main():
+    """主函数示例"""
+    # 配置
+    TOKEN = "wVRyAIjI3hhJBicXndcs1ro9BN5UNMXIjW6SDWBFFyQ"  # 云码Token
+    
+    # 创建识别器
+    solver = YunmaIconPointCaptcha(TOKEN)
+    
+    # 使用示例
+    driver = webdriver.Chrome()
+    driver.get("https://buff.163.com")
+    
+    try:
+        # 等待验证码出现
+        WebDriverWait(driver, 10).until(
+            EC.presence_of_element_located((By.CLASS_NAME, "yidun"))
+        )
+        
+        # 等待验证码完全加载
+        time.sleep(2)
+        
+        # 解决验证码
+        if solver.solve(driver):
+            print("🎉 验证码解决成功!")
+        else:
+            print("❌ 验证码解决失败")
+            
+    except Exception as e:
+        print(f"错误: {e}")
+        
+    finally:
+        input("按Enter键关闭浏览器...")
+        driver.quit()
+
+
+if __name__ == "__main__":
+    main()

+ 329 - 0
云码验证码识别_第二种验证码.py

@@ -0,0 +1,329 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+云码验证码识别 - 第二种验证码(文字特征点选)
+接口文档: https://www.jfbym.com/test/94.html
+"""
+
+import base64
+import json
+import time
+import requests
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.common.action_chains import ActionChains
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+
+
+class YunmaSpaceCaptcha:
+    """云码文字特征点选验证码识别器"""
+
+    def __init__(self, token: str):
+        """
+        初始化
+        
+        Args:
+            token: 云码平台API Token
+        """
+        self.token = token
+        self.api_url = "http://api.jfbym.com/api/YmServer/customApi"
+        self.type_id = "94"  # 文字特征点选验证码接口ID
+
+    def identify_type(self, driver) -> bool:
+        """
+        识别是否为文字特征点选验证码
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            bool: 是文字特征点选验证码返回True
+        """
+        try:
+            # 查找验证码容器
+            captcha_container = driver.find_element(By.CLASS_NAME, "yidun")
+            class_name = captcha_container.get_attribute("class")
+            
+            # 检查是否包含space类名
+            if "yidun--space" in class_name:
+                return True
+            
+            # 备选:检查提示文字(以"请点击"开头但不包含"依次")
+            try:
+                tips_element = driver.find_element(By.CSS_SELECTOR, ".yidun_tips__text")
+                tips_text = tips_element.text.strip()
+                if tips_text.startswith("请点击") and "依次" not in tips_text:
+                    return True
+            except:
+                pass
+            
+            return False
+            
+        except Exception as e:
+            print(f"识别验证码类型失败: {e}")
+            return False
+
+    def extract_captcha_image(self, driver) -> str:
+        """
+        提取验证码图片并转为Base64
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            str: Base64编码的图片数据
+        """
+        try:
+            # 查找验证码背景图片
+            img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
+            
+            # 获取图片src
+            img_src = img_element.get_attribute("src")
+            print(f"验证码图片地址: {img_src}")
+            
+            # 如果是base64格式
+            if img_src.startswith("data:image"):
+                # 提取base64数据部分
+                base64_data = img_src.split(",")[1]
+                return base64_data
+            
+            # 如果是URL,下载图片
+            elif img_src.startswith("http"):
+                response = requests.get(img_src)
+                if response.status_code == 200:
+                    # 转为base64
+                    return base64.b64encode(response.content).decode()
+            
+            return None
+            
+        except Exception as e:
+            print(f"提取验证码图片失败: {e}")
+            return None
+
+    def extract_tip_text(self, driver) -> str:
+        """
+        提取验证码提示文字
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            str: 提示文字,如"请点击黄色小写w"
+        """
+        try:
+            tips_element = driver.find_element(By.CSS_SELECTOR, ".yidun_tips__text")
+            tip_text = tips_element.text.strip()
+            print(f"验证码提示文字: {tip_text}")
+            return tip_text
+        except Exception as e:
+            print(f"提取提示文字失败: {e}")
+            return ""
+
+    def call_api(self, image_base64: str, tip_text: str = "") -> dict:
+        """
+        调用云码API识别验证码
+        
+        Args:
+            image_base64: Base64编码的图片数据
+            tip_text: 提示文字
+            
+        Returns:
+            dict: API响应结果
+        """
+        payload = {
+            "token": self.token,
+            "type": self.type_id,
+            "image": image_base64
+        }
+        
+        # 如果需要传递提示文字,根据接口要求添加
+        if tip_text:
+            # 根据云码接口文档,可能需要添加extra字段
+            payload["extra"] = tip_text
+        
+        headers = {
+            "Content-Type": "application/json"
+        }
+        
+        try:
+            response = requests.post(
+                self.api_url, 
+                json=payload, 
+                headers=headers,
+                timeout=30
+            )
+            
+            return response.json()
+            
+        except Exception as e:
+            print(f"API调用失败: {e}")
+            return {"code": -1, "msg": str(e)}
+
+    def parse_result(self, api_result: dict) -> list:
+        """
+        解析API返回结果
+        
+        Args:
+            api_result: API响应结果
+            
+        Returns:
+            list: 坐标列表 [[x, y]] 或 [[x1, y1], [x2, y2], ...]
+        """
+        try:
+            if api_result.get("code") == 10000:
+                # 识别成功
+                data = api_result.get("data", [])
+                if data:
+                    # 解析返回的坐标数据
+                    result_str = data[0].get("data", "")
+                    
+                    # 尝试解析为坐标列表
+                    if result_str.startswith("[") and result_str.endswith("]"):
+                        return json.loads(result_str)
+                    else:
+                        # 可能是其他格式,尝试eval
+                        try:
+                            return eval(result_str)
+                        except:
+                            print(f"无法解析返回数据: {result_str}")
+                            return []
+                
+            else:
+                print(f"识别失败: {api_result.get('msg')}")
+                return []
+                
+        except Exception as e:
+            print(f"解析结果失败: {e}")
+            return []
+
+    def click_coordinates(self, driver, coordinates: list):
+        """
+        执行点击操作
+        
+        Args:
+            driver: Selenium WebDriver实例
+            coordinates: 坐标列表
+        """
+        try:
+            # 查找验证码图片元素
+            img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
+            location = img_element.location
+            size = img_element.size
+            
+            # 创建Actions链
+            actions = ActionChains(driver)
+            
+            for coord in coordinates:
+                x, y = coord
+                
+                # 移动到指定位置并点击
+                actions.move_to_element_with_offset(img_element, x, y)
+                actions.click()
+                actions.pause(0.3)  # 点击间隔
+            
+            actions.perform()
+            print(f"完成点击,共点击 {len(coordinates)} 次")
+            
+            return True
+            
+        except Exception as e:
+            print(f"点击操作失败: {e}")
+            return False
+
+    def solve(self, driver) -> bool:
+        """
+        完整的验证码解决流程
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            bool: 解决成功返回True
+        """
+        print("=" * 50)
+        print("开始处理文字特征点选验证码(第二种)")
+        print("=" * 50)
+        
+        # 1. 识别验证码类型
+        if not self.identify_type(driver):
+            print("❌ 不是文字特征点选验证码")
+            return False
+        
+        print("✅ 确认为文字特征点选验证码")
+        
+        # 2. 提取提示文字
+        tip_text = self.extract_tip_text(driver)
+        
+        # 3. 提取验证码图片
+        image_base64 = self.extract_captcha_image(driver)
+        if not image_base64:
+            print("❌ 提取验证码图片失败")
+            return False
+        
+        print("✅ 验证码图片提取成功")
+        
+        # 4. 调用云码API
+        print("🔄 正在调用云码API识别...")
+        api_result = self.call_api(image_base64, tip_text)
+        
+        if not api_result or api_result.get("code") != 10000:
+            print(f"❌ API识别失败: {api_result}")
+            return False
+        
+        print("✅ API识别成功")
+        
+        # 5. 解析结果
+        coordinates = self.parse_result(api_result)
+        if not coordinates:
+            print("❌ 解析坐标失败")
+            return False
+        
+        print(f"📍 获取到 {len(coordinates)} 个点击坐标: {coordinates}")
+        
+        # 6. 执行点击
+        if self.click_coordinates(driver, coordinates):
+            print("✅ 点击完成")
+            return True
+        else:
+            print("❌ 点击失败")
+            return False
+
+
+def main():
+    """主函数示例"""
+    # 配置
+    TOKEN = "wVRyAIjI3hhJBicXndcs1ro9BN5UNMXIjW6SDWBFFyQ"  # 云码Token
+    
+    # 创建识别器
+    solver = YunmaSpaceCaptcha(TOKEN)
+    
+    # 使用示例
+    driver = webdriver.Chrome()
+    driver.get("https://buff.163.com")
+    
+    try:
+        # 等待验证码出现
+        WebDriverWait(driver, 10).until(
+            EC.presence_of_element_located((By.CLASS_NAME, "yidun"))
+        )
+        
+        # 等待验证码完全加载
+        time.sleep(2)
+        
+        # 解决验证码
+        if solver.solve(driver):
+            print("🎉 验证码解决成功!")
+        else:
+            print("❌ 验证码解决失败")
+            
+    except Exception as e:
+        print(f"错误: {e}")
+        
+    finally:
+        input("按Enter键关闭浏览器...")
+        driver.quit()
+
+
+if __name__ == "__main__":
+    main()

+ 341 - 0
云码验证码识别_综合版.py

@@ -0,0 +1,341 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+综合验证码识别器 - 自动识别并调用云码接口
+支持两种BUFF验证码类型:
+1. 顺序点击验证码(type=317)
+2. 文字特征点选验证码(type=94)
+"""
+
+import base64
+import json
+import time
+import requests
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.common.action_chains import ActionChains
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+
+
+class BuffCaptchaSolver:
+    """BUFF验证码综合识别器"""
+
+    def __init__(self, token: str):
+        """
+        初始化
+        
+        Args:
+            token: 云码平台API Token
+        """
+        self.token = token
+        self.api_url = "http://api.jfbym.com/api/YmServer/customApi"
+
+    def detect_captcha_type(self, driver) -> str:
+        """
+        检测验证码类型
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            str: 'icon_point' - 顺序点击验证码
+                 'space' - 文字特征点选验证码
+                 None - 未检测到验证码
+        """
+        try:
+            # 等待验证码容器出现
+            wait = WebDriverWait(driver, 10)
+            captcha_container = wait.until(
+                EC.presence_of_element_located((By.CLASS_NAME, "yidun"))
+            )
+
+            # 方法1:检查CSS类名(最可靠)
+            class_name = captcha_container.get_attribute("class")
+            if "yidun--icon_point" in class_name:
+                return "icon_point"
+            elif "yidun--space" in class_name:
+                return "space"
+
+            # 方法2:检查提示文字
+            try:
+                tips_element = driver.find_element(By.CSS_SELECTOR, ".yidun_tips__text")
+                tips_text = tips_element.text.strip()
+
+                if tips_text.startswith("请依次点击"):
+                    return "icon_point"
+                elif tips_text.startswith("请点击"):
+                    return "space"
+            except:
+                pass
+
+            return None
+
+        except Exception as e:
+            print(f"验证码检测失败: {e}")
+            return None
+
+    def extract_captcha_image(self, driver) -> str:
+        """
+        提取验证码图片并转为Base64
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            str: Base64编码的图片数据
+        """
+        try:
+            img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
+            img_src = img_element.get_attribute("src")
+            print(f"验证码图片地址: {img_src}")
+
+            if img_src.startswith("data:image"):
+                base64_data = img_src.split(",")[1]
+                return base64_data
+            elif img_src.startswith("http"):
+                response = requests.get(img_src)
+                if response.status_code == 200:
+                    return base64.b64encode(response.content).decode()
+
+            return None
+
+        except Exception as e:
+            print(f"提取验证码图片失败: {e}")
+            return None
+
+    def extract_tip_text(self, driver) -> str:
+        """
+        提取验证码提示文字
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            str: 提示文字
+        """
+        try:
+            tips_element = driver.find_element(By.CSS_SELECTOR, ".yidun_tips__text")
+            tip_text = tips_element.text.strip()
+            print(f"验证码提示文字: {tip_text}")
+            return tip_text
+        except:
+            return ""
+
+    def call_api(self, image_base64: str, captcha_type: str, tip_text: str = "") -> dict:
+        """
+        调用云码API
+        
+        Args:
+            image_base64: Base64编码的图片数据
+            captcha_type: 验证码类型
+            tip_text: 提示文字
+            
+        Returns:
+            dict: API响应结果
+        """
+        # 根据类型选择接口ID
+        type_ids = {
+            "icon_point": "317",
+            "space": "94"
+        }
+
+        type_id = type_ids.get(captcha_type)
+        if not type_id:
+            return {"code": -1, "msg": "未知的验证码类型"}
+
+        payload = {
+            "token": self.token,
+            "type": type_id,
+            "image": image_base64
+        }
+
+        # 文字点选类型可能需要传递提示文字
+        if captcha_type == "space" and tip_text:
+            payload["extra"] = tip_text
+
+        headers = {"Content-Type": "application/json"}
+
+        try:
+            response = requests.post(
+                self.api_url,
+                json=payload,
+                headers=headers,
+                timeout=30
+            )
+            return response.json()
+
+        except Exception as e:
+            print(f"API调用失败: {e}")
+            return {"code": -1, "msg": str(e)}
+
+    def parse_result(self, api_result: dict) -> list:
+        """
+        解析API返回结果
+        
+        Args:
+            api_result: API响应结果
+            
+        Returns:
+            list: 坐标列表
+        """
+        try:
+            if api_result.get("code") == 10000:
+                data = api_result.get("data", [])
+                if data:
+                    result_str = data[0].get("data", "")
+                    if result_str.startswith("[") and result_str.endswith("]"):
+                        return json.loads(result_str)
+                    else:
+                        try:
+                            return eval(result_str)
+                        except:
+                            print(f"无法解析返回数据: {result_str}")
+                            return []
+            else:
+                print(f"识别失败: {api_result.get('msg')}")
+                return []
+
+        except Exception as e:
+            print(f"解析结果失败: {e}")
+            return []
+
+    def click_coordinates(self, driver, coordinates: list):
+        """
+        执行点击操作
+        
+        Args:
+            driver: Selenium WebDriver实例
+            coordinates: 坐标列表
+        """
+        try:
+            img_element = driver.find_element(By.CSS_SELECTOR, ".yidun_bg-img")
+
+            actions = ActionChains(driver)
+
+            for i, coord in enumerate(coordinates):
+                x, y = coord
+                actions.move_to_element_with_offset(img_element, x, y)
+                actions.click()
+
+                if i < len(coordinates) - 1:
+                    actions.pause(0.3)
+
+            actions.perform()
+            print(f"完成点击,共点击 {len(coordinates)} 次")
+
+            return True
+
+        except Exception as e:
+            print(f"点击操作失败: {e}")
+            return False
+
+    def solve(self, driver) -> bool:
+        """
+        完整的验证码解决流程
+        
+        Args:
+            driver: Selenium WebDriver实例
+            
+        Returns:
+            bool: 解决成功返回True
+        """
+        # 1. 检测验证码类型
+        captcha_type = self.detect_captcha_type(driver)
+
+        if captcha_type == "icon_point":
+            type_name = "顺序点击验证码(第一种)"
+            type_id = "317"
+        elif captcha_type == "space":
+            type_name = "文字特征点选验证码(第二种)"
+            type_id = "94"
+        else:
+            print("❌ 无法识别验证码类型或未检测到验证码")
+            return False
+
+        print("=" * 60)
+        print(f"✅ 检测到 {type_name}")
+        print(f"   云码接口ID: {type_id}")
+        print("=" * 60)
+
+        # 2. 提取验证码图片
+        image_base64 = self.extract_captcha_image(driver)
+        if not image_base64:
+            print("❌ 提取验证码图片失败")
+            return False
+
+        print("✅ 验证码图片提取成功")
+
+        # 3. 提取提示文字(仅第二种需要)
+        tip_text = ""
+        if captcha_type == "space":
+            tip_text = self.extract_tip_text(driver)
+
+        # 4. 调用云码API
+        print("🔄 正在调用云码API识别...")
+        api_result = self.call_api(image_base64, captcha_type, tip_text)
+
+        if not api_result or api_result.get("code") != 10000:
+            print(f"❌ API识别失败: {api_result}")
+            return False
+
+        print("✅ API识别成功")
+
+        # 5. 解析结果
+        coordinates = self.parse_result(api_result)
+        if not coordinates:
+            print("❌ 解析坐标失败")
+            return False
+
+        print(f"📍 获取到 {len(coordinates)} 个点击坐标: {coordinates}")
+
+        # 6. 执行点击
+        if self.click_coordinates(driver, coordinates):
+            print("✅ 点击完成")
+            return True
+        else:
+            print("❌ 点击失败")
+            return False
+
+
+def main():
+    """主函数示例"""
+    # 配置
+    TOKEN = "wVRyAIjI3hhJBicXndcs1ro9BN5UNMXIjW6SDWBFFyQ"  # 云码Token
+
+    # 创建识别器
+    solver = BuffCaptchaSolver(TOKEN)
+
+    # 使用示例
+    driver = webdriver.Chrome()
+    
+    # 访问指定URL
+    target_url = "https://buff.163.com/goods/900482?from=market#tab=selling"
+    print(f"正在访问: {target_url}")
+    driver.get(target_url)
+
+    try:
+        # 等待验证码出现(最多30秒)
+        WebDriverWait(driver, 30).until(
+            EC.presence_of_element_located((By.CLASS_NAME, "yidun"))
+        )
+
+        # 等待验证码完全加载
+        time.sleep(2)
+
+        # 自动识别并解决验证码
+        if solver.solve(driver):
+            print("\n🎉 验证码解决成功!")
+        else:
+            print("\n❌ 验证码解决失败")
+
+    except Exception as e:
+        print(f"错误: {e}")
+
+    finally:
+        input("\n按Enter键关闭浏览器...")
+        driver.quit()
+
+
+if __name__ == "__main__":
+    main()

+ 419 - 0
云码验证码识别_综合版_Playwright.py

@@ -0,0 +1,419 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+BUFF验证码自动识别器 - 使用Playwright
+支持两种验证码类型:
+1. 顺序点击验证码(type=317)
+2. 文字特征点选验证码(type=94)
+
+使用方法:
+1. 安装依赖: pip install playwright && playwright install chromium
+2. 运行脚本: python 云码验证码识别_综合版.py
+"""
+
+import base64
+import json
+import time
+import sys
+from playwright.sync_api import sync_playwright
+from playwright_stealth import stealth
+import requests
+
+
+class BuffCaptchaSolver:
+    """BUFF验证码综合识别器"""
+
+    def __init__(self, token: str):
+        """
+        初始化
+        
+        Args:
+            token: 云码平台API Token
+        """
+        self.token = token
+        self.api_url = "http://api.jfbym.com/api/YmServer/customApi"
+
+    def detect_captcha_type(self, page) -> str:
+        """
+        检测验证码类型
+        
+        Args:
+            page: Playwright Page对象
+            
+        Returns:
+            str: 'icon_point' - 顺序点击验证码
+                 'space' - 文字特征点选验证码
+                 None - 未检测到验证码
+        """
+        try:
+            # 等待验证码容器出现
+            page.wait_for_selector(".yidun", timeout=10000)
+
+            # 方法1:检查CSS类名(最可靠)
+            class_name = page.evaluate("""
+                () => {
+                    const el = document.querySelector('.yidun');
+                    return el ? el.className : '';
+                }
+            """)
+            
+            if "yidun--icon_point" in class_name:
+                return "icon_point"
+            elif "yidun--space" in class_name:
+                return "space"
+
+            # 方法2:检查提示文字
+            try:
+                tips_text = page.evaluate("""
+                    () => {
+                        const el = document.querySelector('.yidun_tips__text');
+                        return el ? el.textContent.trim() : '';
+                    }
+                """)
+                
+                if tips_text.startswith("请依次点击"):
+                    return "icon_point"
+                elif tips_text.startswith("请点击"):
+                    return "space"
+            except:
+                pass
+
+            return None
+
+        except Exception as e:
+            print(f"验证码检测失败: {e}")
+            return None
+
+    def extract_captcha_image(self, page) -> str:
+        """
+        提取验证码图片并转为Base64
+        
+        Args:
+            page: Playwright Page对象
+            
+        Returns:
+            str: Base64编码的图片数据
+        """
+        try:
+            # 获取图片src
+            img_src = page.evaluate("""
+                () => {
+                    const img = document.querySelector('.yidun_bg-img');
+                    return img ? img.src : null;
+                }
+            """)
+            
+            if not img_src:
+                print("❌ 未找到验证码图片")
+                return None
+            
+            print(f"验证码图片地址: {img_src}")
+
+            if img_src.startswith("data:image"):
+                # 提取base64数据部分
+                base64_data = img_src.split(",")[1]
+                return base64_data
+            elif img_src.startswith("http"):
+                # 下载图片
+                response = requests.get(img_src)
+                if response.status_code == 200:
+                    return base64.b64encode(response.content).decode()
+
+            return None
+
+        except Exception as e:
+            print(f"提取验证码图片失败: {e}")
+            return None
+
+    def extract_tip_text(self, page) -> str:
+        """
+        提取验证码提示文字
+        
+        Args:
+            page: Playwright Page对象
+            
+        Returns:
+            str: 提示文字
+        """
+        try:
+            tip_text = page.evaluate("""
+                () => {
+                    const el = document.querySelector('.yidun_tips__text');
+                    return el ? el.textContent.trim() : '';
+                }
+            """)
+            print(f"验证码提示文字: {tip_text}")
+            return tip_text
+        except:
+            return ""
+
+    def call_api(self, image_base64: str, captcha_type: str, tip_text: str = "") -> dict:
+        """
+        调用云码API
+        
+        Args:
+            image_base64: Base64编码的图片数据
+            captcha_type: 验证码类型
+            tip_text: 提示文字
+            
+        Returns:
+            dict: API响应结果
+        """
+        # 根据类型选择接口ID
+        type_ids = {
+            "icon_point": "317",
+            "space": "94"
+        }
+
+        type_id = type_ids.get(captcha_type)
+        if not type_id:
+            return {"code": -1, "msg": "未知的验证码类型"}
+
+        payload = {
+            "token": self.token,
+            "type": type_id,
+            "image": image_base64
+        }
+
+        # 文字点选类型可能需要传递提示文字
+        if captcha_type == "space" and tip_text:
+            payload["extra"] = tip_text
+
+        headers = {"Content-Type": "application/json"}
+
+        try:
+            response = requests.post(
+                self.api_url,
+                json=payload,
+                headers=headers,
+                timeout=30
+            )
+            return response.json()
+
+        except Exception as e:
+            print(f"API调用失败: {e}")
+            return {"code": -1, "msg": str(e)}
+
+    def parse_result(self, api_result: dict) -> list:
+        """
+        解析API返回结果
+        
+        Args:
+            api_result: API响应结果
+            
+        Returns:
+            list: 坐标列表
+        """
+        try:
+            if api_result.get("code") == 10000:
+                data = api_result.get("data", [])
+                if data:
+                    result_str = data[0].get("data", "")
+                    print(f"云码返回坐标: {result_str}")
+                    
+                    if result_str.startswith("[") and result_str.endswith("]"):
+                        return json.loads(result_str)
+                    else:
+                        try:
+                            return eval(result_str)
+                        except:
+                            print(f"无法解析返回数据: {result_str}")
+                            return []
+            else:
+                print(f"识别失败: {api_result.get('msg')}")
+                return []
+
+        except Exception as e:
+            print(f"解析结果失败: {e}")
+            return []
+
+    def click_coordinates(self, page, coordinates: list):
+        """
+        执行点击操作
+        
+        Args:
+            page: Playwright Page对象
+            coordinates: 坐标列表
+        """
+        try:
+            # 获取验证码图片元素
+            img_element = page.query_selector(".yidun_bg-img")
+            box = img_element.bounding_box()
+            
+            if not box:
+                print("❌ 无法获取验证码图片位置")
+                return False
+
+            # 执行点击
+            for i, coord in enumerate(coordinates):
+                x, y = coord
+                
+                # 计算相对于图片的绝对坐标
+                abs_x = box["x"] + x
+                abs_y = box["y"] + y
+                
+                # 移动并点击
+                page.mouse.click(abs_x, abs_y)
+                print(f"点击坐标: ({abs_x}, {abs_y})")
+                
+                # 每次点击后等待
+                if i < len(coordinates) - 1:
+                    time.sleep(0.3)
+
+            print(f"✅ 完成点击,共点击 {len(coordinates)} 次")
+            return True
+
+        except Exception as e:
+            print(f"点击操作失败: {e}")
+            return False
+
+    def solve(self, page) -> bool:
+        """
+        完整的验证码解决流程
+        
+        Args:
+            page: Playwright Page对象
+            
+        Returns:
+            bool: 解决成功返回True
+        """
+        # 1. 检测验证码类型
+        captcha_type = self.detect_captcha_type(page)
+
+        if captcha_type == "icon_point":
+            type_name = "顺序点击验证码(第一种)"
+            type_id = "317"
+        elif captcha_type == "space":
+            type_name = "文字特征点选验证码(第二种)"
+            type_id = "94"
+        else:
+            print("❌ 无法识别验证码类型或未检测到验证码")
+            return False
+
+        print("=" * 60)
+        print(f"✅ 检测到 {type_name}")
+        print(f"   云码接口ID: {type_id}")
+        print("=" * 60)
+
+        # 2. 提取验证码图片
+        image_base64 = self.extract_captcha_image(page)
+        if not image_base64:
+            print("❌ 提取验证码图片失败")
+            return False
+
+        print("✅ 验证码图片提取成功")
+
+        # 3. 提取提示文字(仅第二种需要)
+        tip_text = ""
+        if captcha_type == "space":
+            tip_text = self.extract_tip_text(page)
+
+        # 4. 调用云码API
+        print("🔄 正在调用云码API识别...")
+        api_result = self.call_api(image_base64, captcha_type, tip_text)
+
+        if not api_result or api_result.get("code") != 10000:
+            print(f"❌ API识别失败: {api_result}")
+            return False
+
+        print("✅ API识别成功")
+
+        # 5. 解析结果
+        coordinates = self.parse_result(api_result)
+        if not coordinates:
+            print("❌ 解析坐标失败")
+            return False
+
+        print(f"📍 获取到 {len(coordinates)} 个点击坐标")
+
+        # 6. 执行点击
+        if self.click_coordinates(page, coordinates):
+            print("✅ 点击完成")
+            return True
+        else:
+            print("❌ 点击失败")
+            return False
+
+
+def main():
+    """主函数"""
+    # 配置
+    TOKEN = "wVRyAIjI3hhJBicXndcs1ro9BN5UNMXIjW6SDWBFFyQ"  # 云码Token
+    TARGET_URL = "https://buff.163.com/goods/900482?from=market#tab=selling"
+
+    # 创建识别器
+    solver = BuffCaptchaSolver(TOKEN)
+
+    with sync_playwright() as p:
+        # 启动浏览器(无头模式=False,可以看到浏览器窗口)
+        print("🚀 启动浏览器...")
+        browser = p.chromium.launch(
+            headless=False,  # False=显示浏览器窗口
+            args=[
+                '--no-sandbox',
+                '--disable-setuid-sandbox',
+                '--disable-dev-shm-usage',
+                '--disable-gpu',
+                '--window-size=1920,1080'
+            ]
+        )
+        
+        # 创建上下文
+        context = browser.new_context(
+            viewport={'width': 1920, 'height': 1080},
+            user_agent='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
+        )
+        
+        # 创建页面
+        page = context.new_page()
+        
+        # 应用stealth防止被检测
+        stealth(page)
+        
+        try:
+            # 访问目标URL
+            print(f"📍 正在访问: {TARGET_URL}")
+            page.goto(TARGET_URL)
+            
+            # 等待页面加载
+            page.wait_for_load_state("networkidle")
+            
+            print("✅ 页面加载完成")
+            
+            # 等待验证码出现(最多30秒)
+            print("🔍 等待验证码出现...")
+            
+            # 尝试等待验证码
+            try:
+                page.wait_for_selector(".yidun", timeout=30000)
+                print("✅ 检测到验证码!")
+            except:
+                print("❌ 30秒内未检测到验证码")
+                input("\n按Enter键关闭浏览器...")
+                browser.close()
+                return
+            
+            # 等待验证码完全加载
+            time.sleep(2)
+            
+            # 自动识别并解决验证码
+            if solver.solve(page):
+                print("\n🎉 验证码解决成功!")
+            else:
+                print("\n❌ 验证码解决失败")
+
+        except Exception as e:
+            print(f"错误: {e}")
+            import traceback
+            traceback.print_exc()
+
+        finally:
+            input("\n按Enter键关闭浏览器...")
+            browser.close()
+
+
+if __name__ == "__main__":
+    print("=" * 60)
+    print("BUFF验证码自动识别器(Playwright版本)")
+    print("=" * 60)
+    main()

+ 556 - 0
云码验证码识别_综合版_简化版.py

@@ -0,0 +1,556 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+BUFF验证码自动识别器 - 使用Playwright(简化版)
+支持两种验证码类型:
+1. 顺序点击验证码(type=317)
+2. 文字特征点选验证码(type=94)
+
+使用方法:
+1. 安装依赖: pip install playwright && playwright install chromium
+2. 运行脚本: python3 云码验证码识别_综合版_简化版.py
+"""
+
+import base64
+import json
+import time
+import sys
+from playwright.sync_api import sync_playwright
+import requests
+
+
+def apply_stealth(page):
+    """
+    手动应用 stealth 脚本,防止被检测为自动化工具
+    """
+    page.add_init_script("""
+        Object.defineProperty(navigator, 'webdriver', {
+            get: () => undefined
+        });
+        Object.defineProperty(navigator, 'languages', {
+            get: () => ['zh-CN', 'zh', 'en-US', 'en']
+        });
+        Object.defineProperty(navigator, 'plugins', {
+            get: () => [1, 2, 3, 4, 5]
+        });
+        window.navigator.chrome = {
+            runtime: {},
+            loadTimes: function() {},
+            csi: function() {},
+            app: {}
+        };
+        Object.defineProperty(window, 'chrome', {
+            get: () => window.navigator.chrome
+        });
+        Object.defineProperty(navigator, 'platform', {
+            get: () => 'MacIntel'
+        });
+        Object.defineProperty(navigator, 'deviceMemory', {
+            get: () => 8
+        });
+        Object.defineProperty(navigator, 'hardwareConcurrency', {
+            get: () => 8
+        });
+    """)
+
+
+class BuffCaptchaSolver:
+    """BUFF验证码综合识别器"""
+
+    def __init__(self, token: str):
+        """
+        初始化
+
+        Args:
+            token: 云码平台API Token
+        """
+        self.token = token
+        self.api_url = "http://api.jfbym.com/api/YmServer/customApi"
+
+    def detect_captcha_type(self, page) -> str:
+        """
+        检测验证码类型
+
+        Args:
+            page: Playwright Page对象
+
+        Returns:
+            str: 'icon_point' - 顺序点击验证码
+                 'space' - 文字特征点选验证码
+                 None - 未检测到验证码
+        """
+        try:
+            # 等待验证码容器出现
+            page.wait_for_selector(".yidun", timeout=10000)
+
+            # 方法1:检查CSS类名(最可靠)
+            class_name = page.evaluate("""
+                () => {
+                    const el = document.querySelector('.yidun');
+                    return el ? el.className : '';
+                }
+            """)
+
+            print(f"验证码容器class: {class_name}")
+
+            if "yidun--icon_point" in class_name:
+                return "icon_point"
+            elif "yidun--space" in class_name:
+                return "space"
+
+            # 方法2:检查提示文字
+            try:
+                tips_text = page.evaluate("""
+                    () => {
+                        const el = document.querySelector('.yidun_tips__text');
+                        return el ? el.textContent.trim() : '';
+                    }
+                """)
+
+                print(f"验证码提示文字: {tips_text}")
+
+                if tips_text.startswith("请依次点击"):
+                    return "icon_point"
+                elif tips_text.startswith("请点击"):
+                    return "space"
+            except:
+                pass
+
+            return None
+
+        except Exception as e:
+            print(f"验证码检测失败: {e}")
+            return None
+
+    def extract_captcha_image(self, page) -> str:
+        """
+        提取验证码图片并转为Base64
+
+        Args:
+            page: Playwright Page对象
+
+        Returns:
+            str: Base64编码的图片数据
+        """
+        try:
+            # 获取图片src
+            img_src = page.evaluate("""
+                () => {
+                    const img = document.querySelector('.yidun_bg-img');
+                    return img ? img.src : null;
+                }
+            """)
+
+            if not img_src:
+                print("未找到验证码图片")
+                return None
+
+            print(f"验证码图片地址: {img_src[:100]}...")
+
+            if img_src.startswith("data:image"):
+                # 提取base64数据部分
+                base64_data = img_src.split(",")[1]
+                return base64_data
+            elif img_src.startswith("http"):
+                # 下载图片
+                response = requests.get(img_src)
+                if response.status_code == 200:
+                    return base64.b64encode(response.content).decode()
+
+            return None
+
+        except Exception as e:
+            print(f"提取验证码图片失败: {e}")
+            return None
+
+    def extract_tip_text(self, page) -> str:
+        """
+        提取验证码提示文字
+
+        Args:
+            page: Playwright Page对象
+
+        Returns:
+            str: 提示文字
+        """
+        try:
+            tip_text = page.evaluate("""
+                () => {
+                    const el = document.querySelector('.yidun_tips__text');
+                    return el ? el.textContent.trim() : '';
+                }
+            """)
+            print(f"验证码提示文字: {tip_text}")
+            return tip_text
+        except:
+            return ""
+
+    def call_api(self, image_base64: str, captcha_type: str, tip_text: str = "") -> dict:
+        """
+        调用云码API
+
+        Args:
+            image_base64: Base64编码的图片数据
+            captcha_type: 验证码类型
+            tip_text: 提示文字
+
+        Returns:
+            dict: API响应结果
+        """
+        # 根据类型选择接口ID
+        type_ids = {
+            "icon_point": "30332",  # 顺序点击验证码(第一种)- 根据云码文档
+            "space": "30109"  # 文字特征点选验证码(第二种)- 根据云码文档
+        }
+
+        type_id = type_ids.get(captcha_type)
+        if not type_id:
+            return {"code": -1, "msg": "未知的验证码类型"}
+
+        payload = {
+            "token": self.token,
+            "type": type_id,
+            "image": image_base64
+        }
+
+        # 第一种验证码(通用图标点选)- 需要 direction 参数
+        if captcha_type == "icon_point":
+            # 根据提示文字判断提示信息位置
+            # "按顺序点击下图中的" - 通常在图片下方显示提示
+            if tip_text:
+                if "上方" in tip_text or "上面" in tip_text:
+                    payload["direction"] = "top"
+                else:
+                    payload["direction"] = "bottom"
+            else:
+                payload["direction"] = "bottom"  # 默认
+            payload["click_num"] = "4"  # 默认4个坐标
+
+        # 第二种验证码(文字特征点选)- 需要 extra 参数
+        elif captcha_type == "space" and tip_text:
+            payload["extra"] = tip_text
+
+        headers = {"Content-Type": "application/json"}
+
+        try:
+            response = requests.post(
+                self.api_url,
+                json=payload,
+                headers=headers,
+                timeout=30
+            )
+            return response.json()
+
+        except Exception as e:
+            print(f"API调用失败: {e}")
+            return {"code": -1, "msg": str(e)}
+
+    def parse_result(self, api_result: dict) -> list:
+        """
+        解析API返回结果
+
+        Args:
+            api_result: API响应结果
+
+        Returns:
+            list: 坐标列表 [[x1, y1], [x2, y2], ...]
+        """
+        try:
+            print(f"API完整返回: {api_result}")
+
+            if api_result.get("code") == 10000:
+                data = api_result.get("data", {})
+                print(f"data字段: {data}, 类型: {type(data)}")
+
+                # 新的返回格式:data 是字典
+                if isinstance(data, dict):
+                    inner_code = data.get("code", 0)
+                    result_str = data.get("data", "")
+                    print(f"inner_code: {inner_code}, result_str: {result_str}")
+
+                    if inner_code == 0 and result_str:
+                        # 格式1: JSON数组 "[x1,y1,x2,y2,x3,y3,x4,y4]"
+                        if isinstance(result_str, str) and result_str.startswith("["):
+                            try:
+                                coords = json.loads(result_str)
+                                # 转换为一维数组为二维坐标数组
+                                if isinstance(coords, list) and len(coords) >= 2:
+                                    if all(isinstance(c, (int, float)) for c in coords):
+                                        # 一维数组 [x1,y1,x2,y2,...] 转为 [[x1,y1],[x2,y2],...]
+                                        result = []
+                                        for i in range(0, len(coords) - 1, 2):
+                                            if i + 1 < len(coords):
+                                                result.append([int(coords[i]), int(coords[i + 1])])
+                                        if result:
+                                            print(f"解析到 {len(result)} 个坐标: {result}")
+                                            return result
+                            except json.JSONDecodeError:
+                                pass
+
+                        # 格式2: 逗号分隔 "x1,y1,x2,y2,x3,y3,x4,y4"
+                        if isinstance(result_str, str) and "," in result_str:
+                            parts = result_str.replace(" ", "").split(",")
+                            if len(parts) >= 2:
+                                try:
+                                    coords = [int(p) for p in parts if p.strip().isdigit() or (p.strip().lstrip('-').isdigit())]
+                                    if len(coords) >= 2:
+                                        result = []
+                                        for i in range(0, len(coords) - 1, 2):
+                                            if i + 1 < len(coords):
+                                                result.append([coords[i], coords[i + 1]])
+                                        if result:
+                                            print(f"解析到 {len(result)} 个坐标: {result}")
+                                            return result
+                                except ValueError:
+                                    pass
+
+                        # 格式3: 单个坐标 "156,61"
+                        if isinstance(result_str, str) and result_str.count(",") == 1:
+                            parts = result_str.split(",")
+                            if len(parts) == 2:
+                                try:
+                                    x, y = int(parts[0].strip()), int(parts[1].strip())
+                                    print(f"解析到单个坐标: [{x}, {y}]")
+                                    return [[x, y]]
+                                except ValueError:
+                                    pass
+
+                # 旧的返回格式:data 是列表
+                elif isinstance(data, list) and data:
+                    result_str = data[0].get("data", "")
+                    print(f"result_str: {result_str}")
+
+                    if result_str.startswith("[") and result_str.endswith("]"):
+                        return json.loads(result_str)
+                    else:
+                        try:
+                            return eval(result_str)
+                        except:
+                            print(f"无法解析返回数据: {result_str}")
+                            return []
+
+            else:
+                print(f"识别失败: {api_result.get('msg')}")
+                return []
+
+        except Exception as e:
+            print(f"解析结果失败: {e}")
+            import traceback
+            traceback.print_exc()
+            return []
+
+    def click_coordinates(self, page, coordinates: list):
+        """
+        执行点击操作
+
+        Args:
+            page: Playwright Page对象
+            coordinates: 坐标列表
+        """
+        try:
+            # 获取验证码图片元素
+            img_element = page.query_selector(".yidun_bg-img")
+            box = img_element.bounding_box()
+
+            if not box:
+                print("无法获取验证码图片位置")
+                return False
+
+            print(f"验证码图片位置: x={box['x']}, y={box['y']}, width={box['width']}, height={box['height']}")
+
+            # 执行点击
+            for i, coord in enumerate(coordinates):
+                x, y = coord
+
+                # 计算相对于图片的绝对坐标
+                abs_x = box["x"] + x
+                abs_y = box["y"] + y
+
+                # 移动并点击
+                page.mouse.click(abs_x, abs_y)
+                print(f"点击坐标 ({i+1}): ({abs_x}, {abs_y})")
+
+                # 每次点击后等待
+                if i < len(coordinates) - 1:
+                    time.sleep(0.5)
+
+            print(f"完成点击,共点击 {len(coordinates)} 次")
+            return True
+
+        except Exception as e:
+            print(f"点击操作失败: {e}")
+            return False
+
+    def solve(self, page) -> bool:
+        """
+        完整的验证码解决流程
+
+        Args:
+            page: Playwright Page对象
+
+        Returns:
+            bool: 解决成功返回True
+        """
+        # 1. 检测验证码类型
+        captcha_type = self.detect_captcha_type(page)
+
+        if captcha_type == "icon_point":
+            type_name = "顺序点击验证码(第一种)"
+            type_id = "30332"  # 根据云码文档
+        elif captcha_type == "space":
+            type_name = "文字特征点选验证码(第二种)"
+            type_id = "30109"  # 根据云码文档
+        else:
+            print("无法识别验证码类型或未检测到验证码")
+            return False
+
+        print("=" * 60)
+        print(f"检测到 {type_name}")
+        print(f"云码接口ID: {type_id}")
+        print("=" * 60)
+
+        # 2. 提取验证码图片
+        image_base64 = self.extract_captcha_image(page)
+        if not image_base64:
+            print("提取验证码图片失败")
+            return False
+
+        print("验证码图片提取成功")
+
+        # 3. 提取提示文字(仅第二种需要)
+        tip_text = ""
+        if captcha_type == "space":
+            tip_text = self.extract_tip_text(page)
+
+        # 4. 调用云码API
+        print("正在调用云码API识别...")
+        api_result = self.call_api(image_base64, captcha_type, tip_text)
+
+        if not api_result or api_result.get("code") != 10000:
+            print(f"API识别失败: {api_result}")
+            return False
+
+        print("API识别成功")
+
+        # 5. 解析结果
+        coordinates = self.parse_result(api_result)
+        if not coordinates:
+            print("解析坐标失败")
+            return False
+
+        print(f"获取到 {len(coordinates)} 个点击坐标")
+
+        # 6. 执行点击
+        if self.click_coordinates(page, coordinates):
+            print("点击完成")
+            return True
+        else:
+            print("点击失败")
+            return False
+
+
+def main():
+    """主函数"""
+    # 配置
+    TOKEN = "wVRyAIjI3hhJBicXndcs1ro9BN5UNMXIjW6SDWBFFyQ"  # 云码Token
+    TARGET_URL = "https://buff.163.com/goods/900482?from=market#tab=selling"
+
+    # 创建识别器
+    solver = BuffCaptchaSolver(TOKEN)
+
+    print("=" * 60)
+    print("BUFF验证码自动识别器(Playwright简化版)")
+    print("=" * 60)
+
+    with sync_playwright() as p:
+        # 启动浏览器(无痕模式)
+        print("启动浏览器(无痕模式)...")
+
+        # 使用 launch_persistent_context 启动真正的无痕模式
+        context = p.chromium.launch_persistent_context(
+            user_data_dir="",  # 空目录 = 临时无痕模式
+            headless=False,  # False=显示浏览器窗口,方便调试
+            viewport={'width': 1920, 'height': 1080},
+            user_agent='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
+            locale='zh-CN',
+            args=[
+                '--no-sandbox',
+                '--disable-setuid-sandbox',
+                '--disable-dev-shm-usage',
+                '--disable-gpu',
+                '--window-size=1920,1080',
+                '--start-maximized'
+            ]
+        )
+
+        # 获取页面
+        page = context.pages[0] if context.pages else context.new_page()
+
+        # 应用stealth防止被检测
+        apply_stealth(page)
+
+        try:
+            # 访问目标URL
+            print(f"正在访问: {TARGET_URL}")
+            page.goto(TARGET_URL)
+
+            # 等待页面加载
+            page.wait_for_load_state("networkidle")
+
+            print("页面加载完成")
+
+            # 设置session cookie防止验证码
+            print("设置session cookie...")
+            context.add_cookies([
+                {
+                    "name": "session",
+                    "value": "1-29ene-taFfBv6_EK2btogRrVsqsQsuuJooUGnxBDUwLI2043282888",
+                    "domain": ".buff.163.com",
+                    "path": "/"
+                }
+            ])
+            print("session cookie设置成功")
+
+            # 刷新页面使cookie生效
+            page.reload()
+            page.wait_for_load_state("networkidle")
+            print("页面已刷新")
+
+            # 等待验证码出现(最多30秒)
+            print("等待验证码出现...")
+
+            # 尝试等待验证码
+            try:
+                page.wait_for_selector(".yidun", timeout=30000)
+                print("检测到验证码!")
+            except:
+                print("30秒内未检测到验证码")
+                print("可能的原因:")
+                print("1. 页面没有触发验证码")
+                print("2. 验证码已经被手动处理")
+                print("3. 页面结构已变化")
+                input("\n按Enter键关闭浏览器...")
+                context.close()
+                return
+
+            # 等待验证码完全加载
+            time.sleep(2)
+
+            # 自动识别并解决验证码
+            if solver.solve(page):
+                print("\n验证码解决成功!")
+            else:
+                print("\n验证码解决失败")
+
+        except Exception as e:
+            print(f"错误: {e}")
+            import traceback
+            traceback.print_exc()
+
+        finally:
+            input("\n按Enter键关闭浏览器...")
+            browser.close()
+
+
+if __name__ == "__main__":
+    main()

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません