import base64 import random import time from urllib.parse import quote import requests from DrissionPage import ChromiumPage, ChromiumOptions from PIL import Image token = "zPzmt1mG1ouCU6GTzsZN2Lmm8pdZypapPcLJTBRETco" proxyUrl = "117.26.226.31:40030" proxies = { "http": "http://" + proxyUrl, "https": "http://" + proxyUrl, } chrome_path = r"C:\Program Files\Google\Chrome\Application\chrome.exe" class CaptchaDeal(object): def __init__(self): self.page = self.init_chromepage() def init_chromepage(self): # 初始化ChromiumPage co = ChromiumOptions().auto_port().set_browser_path(chrome_path) co.headless(False) # 可视化模式便于调试 # co.set_proxy("http://" + proxyUrl) page = ChromiumPage(co) return page def get_page(self): """解析验证码界面""" url = "https://cfe.m.jd.com/privatedomain/risk_handler/03101900/?returnurl=https%3A%2F%2Fsearch.jd.com%2FSearch%3Fkeyword%3D%25E5%25A4%25A9%25E5%25A3%25AB%25E5%258A%259B%25E5%2585%25BB%25E8%25A1%2580%25E6%25B8%2585%25E8%2584%2591%25E9%25A2%2597%25E7%25B2%25924g%26enc%3Dutf-8%26wq%3D%25E5%25A4%25A9%25E5%25A3%25AB%25E5%258A%259B%25E5%2585%25BB%25E8%25A1%2580%25E6%25B8%2585%25E8%2584%2591%25E9%25A2%2597%25E7%25B2%25924g&rqhost=https%3A%2F%2Fapi.m.jd.com&rpid=rp-112038530-10428-1774874729116&evtype=2&evapi=color_pc_search_searchWare&source=1&forceCurrentView=1&evsid=AASWz-Ccn-fmDu0QbI5tPvKhPINYZNJcz6vQ-TCMSXxv5jRuoOMT4SCV_hpjQ0UyoEjA5OF-" self.page.get(url) self.page.wait.doc_loaded() time.sleep(3) self.page.ele("xpath://div[@class='verifyBtn']").click() self.page.wait.doc_loaded() time.sleep(4) print("为滑块验证码") capt_ele = self.page.ele('xpath://*[@id="captcha_modal"]', timeout=2) capt_ele.get_screenshot('./element_screenshot.png') distance = self.verify(2) print(f"滑块距离:{distance}") slider_element = self.page.ele( "xpath://img[@class='move-img']") self.simulate_slider_drag(slider_element, float(distance)) # 滑块验证处理 time.sleep(5) print(self.page.url, self.page.title) def verify(self, type_num): """调用云码平台服务""" with open('element_screenshot.png', 'rb') as f: b = base64.b64encode(f.read()).decode() url = "http://api.jfbym.com/api/YmServer/customApi" if type_num == 1: # 坐标类型 data = { "token": token, "type": "30332", "direction": "top", "click_num": 3, "image": b, } else: # 滑块类型 data = { "token": token, "type": "22222", "image": b, } _headers = { "Content-Type": "application/json" } response = requests.request("POST", url, headers=_headers, json=data).json() print(response) return response["data"]["data"] def generate_human_track(self, distance): """ 生成人类拖动的轨迹 :param distance: 需要拖动的距离(像素) :return: 轨迹点列表,每个点包含(x偏移, y偏移, 延迟时间) """ tracks = [] current = 0 mid = distance * 0.7 # 70%处开始减速 t = 0.2 v = 0 move_points = [] # 第一阶段:加速 while current < mid: a = random.uniform(2, 4) v0 = v v = v0 + a * t move = v0 * t + 0.5 * a * t * t current += move move_points.append(move) # 第二阶段:减速 while current < distance: a = -random.uniform(0.5, 1.5) v0 = v v = v0 + a * t if v < 0.5: # 防止速度过小 v = 0.5 move = v0 * t + 0.5 * a * t * t current += move move_points.append(move) # 添加随机性并生成最终轨迹 total_points = len(move_points) for i, move in enumerate(move_points): x_offset = move # 添加垂直抖动(模拟手抖) if i % random.randint(2, 4) == 0: y_offset = random.randint(-2, 2) else: y_offset = 0 # 时间间隔(模拟人类反应) if i < total_points * 0.3: # 开始阶段较快 duration = random.uniform(0.01, 0.03) elif i > total_points * 0.7: # 结束阶段较慢 duration = random.uniform(0.03, 0.08) else: # 中间阶段 duration = random.uniform(0.02, 0.05) # 随机添加微小停顿 if random.random() < 0.05: duration += random.uniform(0.05, 0.1) tracks.append((x_offset, y_offset, duration)) # 最终微调:到达终点后轻微回拉 if random.random() < 0.7: tracks.append((-random.randint(1, 3), 0, 0.05)) return tracks def simulate_slider_drag(self, slider_element, target_distance): """ 模拟人类拖动滑块 """ # 移动到滑块并按住 self.page.actions.move_to(slider_element).hold() # 生成轨迹 tracks = self.generate_human_track(target_distance) # 按轨迹拖动 for track in tracks: offset_x, offset_y, duration = track self.page.actions.move(offset_x, offset_y, duration=duration / 1000) # 释放鼠标 self.page.actions.release() def run(self): self.get_page() if __name__ == '__main__': captcga_deal = CaptchaDeal() captcga_deal.run()