| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- 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()
|