mirror of
https://github.com/zhao-zg/jd-login.git
synced 2026-01-12 05:10:42 +08:00
commit
7d5e578b3f
202
login.py
202
login.py
@ -156,7 +156,7 @@ async def loginPhone(chromium_path, workList, uid, headless):
|
||||
|
||||
elif await page.xpath('//*[@id="captcha_modal"]'):
|
||||
logger.info("进入安全验证分支")
|
||||
if await page.xpath('//*[@id="small_img"]'):
|
||||
if await page.xpath('//*[@id="slot_img"]'):
|
||||
logger.info("进入过滑块分支")
|
||||
|
||||
workList[uid].status = "pending"
|
||||
@ -354,7 +354,7 @@ async def loginPassword(chromium_path, workList, uid, headless):
|
||||
|
||||
elif await page.xpath('//*[@id="captcha_modal"]'):
|
||||
logger.info("进入安全验证分支")
|
||||
if await page.xpath('//*[@id="small_img"]'):
|
||||
if await page.xpath('//*[@id="slot_img"]'):
|
||||
logger.info("进入过滑块分支")
|
||||
|
||||
workList[uid].status = "pending"
|
||||
@ -610,7 +610,7 @@ async def sendSMSDirectly(page):
|
||||
|
||||
try:
|
||||
while True:
|
||||
if await page.xpath('//*[@id="small_img"]'):
|
||||
if await page.xpath('//*[@id="slot_img"]'):
|
||||
await verification(page)
|
||||
|
||||
elif await page.xpath('//*[@id="captcha_modal"]/div/div[3]/button'):
|
||||
@ -652,7 +652,7 @@ async def sendSMS(page):
|
||||
|
||||
try:
|
||||
while True:
|
||||
if await page.xpath('//*[@id="small_img"]'):
|
||||
if await page.xpath('//*[@id="slot_img"]'):
|
||||
await verification(page)
|
||||
|
||||
elif await page.xpath('//*[@id="captcha_modal"]/div/div[3]/button'):
|
||||
@ -743,96 +743,140 @@ async def typeSMScode(page, workList, uid):
|
||||
|
||||
|
||||
async def verification(page):
|
||||
logger.info("开始过滑块")
|
||||
logger.info("开始过滑块验证")
|
||||
|
||||
try:
|
||||
# 1. 获取滑块和背景图
|
||||
await page.waitForSelector("#main_img", timeout=5000)
|
||||
logger.info("已定位滑块背景图元素")
|
||||
|
||||
image_src = await page.Jeval("#main_img", 'el => el.getAttribute("src")')
|
||||
logger.info(f"背景图SRC: {image_src[:30]}...")
|
||||
|
||||
request.urlretrieve(image_src, "image.png")
|
||||
logger.info("背景图下载完成")
|
||||
|
||||
# 2. 获取模板图
|
||||
template_src = await page.Jeval("#slot_img", 'el => el.getAttribute("src")')
|
||||
logger.info(f"滑块模板SRC: {template_src[:30]}...")
|
||||
|
||||
request.urlretrieve(template_src, "template.png")
|
||||
logger.info("滑块模板下载完成")
|
||||
|
||||
# 3. 获取实际显示尺寸
|
||||
bg_width = await page.evaluate('() => document.getElementById("main_img").clientWidth')
|
||||
bg_height = await page.evaluate('() => document.getElementById("main_img").clientHeight')
|
||||
logger.info(f"背景图显示尺寸: {bg_width}x{bg_height}")
|
||||
|
||||
slot_width = await page.evaluate('() => document.getElementById("slot_img").clientWidth')
|
||||
slot_height = await page.evaluate('() => document.getElementById("slot_img").clientHeight')
|
||||
logger.info(f"滑块显示尺寸: {slot_width}x{slot_height}")
|
||||
|
||||
# 4. 调整图片到显示尺寸
|
||||
for path, size in [("image.png", (bg_width, bg_height)),
|
||||
("template.png", (slot_width, slot_height))]:
|
||||
img = Image.open(path)
|
||||
img = img.resize(size)
|
||||
img.save(path)
|
||||
logger.info("图片尺寸调整完成")
|
||||
|
||||
# 5. 计算滑块距离
|
||||
logger.info("开始计算滑动距离...")
|
||||
|
||||
async def get_distance():
|
||||
img = cv2.imread("image.png", 0)
|
||||
template = cv2.imread("template.png", 0)
|
||||
|
||||
# 增强边缘检测
|
||||
img = cv2.GaussianBlur(img, (5, 5), 0)
|
||||
template = cv2.GaussianBlur(template, (5, 5), 0)
|
||||
bg_edge = cv2.Canny(img, 100, 200)
|
||||
cut_edge = cv2.Canny(template, 100, 200)
|
||||
img = cv2.cvtColor(bg_edge, cv2.COLOR_GRAY2RGB)
|
||||
template = cv2.cvtColor(cut_edge, cv2.COLOR_GRAY2RGB)
|
||||
res = cv2.matchTemplate(
|
||||
img, template, cv2.TM_CCOEFF_NORMED
|
||||
)
|
||||
value = cv2.minMaxLoc(res)[3][0]
|
||||
distance = (
|
||||
value + 10
|
||||
)
|
||||
return distance
|
||||
|
||||
await page.waitForSelector("#cpc_img")
|
||||
image_src = await page.Jeval(
|
||||
"#cpc_img", 'el => el.getAttribute("src")'
|
||||
)
|
||||
request.urlretrieve(image_src, "image.png")
|
||||
width = await page.evaluate(
|
||||
'() => { return document.getElementById("cpc_img").clientWidth; }'
|
||||
)
|
||||
height = await page.evaluate(
|
||||
'() => { return document.getElementById("cpc_img").clientHeight; }'
|
||||
)
|
||||
image = Image.open("image.png")
|
||||
resized_image = image.resize((width, height))
|
||||
resized_image.save("image.png")
|
||||
template_src = await page.Jeval(
|
||||
"#small_img", 'el => el.getAttribute("src")'
|
||||
)
|
||||
request.urlretrieve(template_src, "template.png")
|
||||
width = await page.evaluate(
|
||||
'() => { return document.getElementById("small_img").clientWidth; }'
|
||||
)
|
||||
height = await page.evaluate(
|
||||
'() => { return document.getElementById("small_img").clientHeight; }'
|
||||
)
|
||||
image = Image.open("template.png")
|
||||
resized_image = image.resize((width, height))
|
||||
resized_image.save("template.png")
|
||||
await page.waitFor(100)
|
||||
el = await page.querySelector(
|
||||
bg_edge = cv2.Canny(img, 50, 150)
|
||||
cut_edge = cv2.Canny(template, 50, 150)
|
||||
|
||||
# 可视化边缘检测结果(调试用)
|
||||
cv2.imwrite("debug_bg_edge.png", bg_edge)
|
||||
cv2.imwrite("debug_slot_edge.png", cut_edge)
|
||||
logger.info("边缘检测结果已保存")
|
||||
|
||||
# 模板匹配
|
||||
res = cv2.matchTemplate(bg_edge, cut_edge, cv2.TM_CCOEFF_NORMED)
|
||||
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
|
||||
logger.info(f"匹配结果: 最大相似度={max_val:.2f}, 位置={max_loc}")
|
||||
|
||||
# 计算滑块中心点到缺口中心的距离
|
||||
slot_center_x = max_loc[0] + slot_width // 2
|
||||
distance = slot_center_x - 10 # 减去初始偏移
|
||||
|
||||
logger.info(f"计算滑动距离: {distance}px")
|
||||
|
||||
# 6. 执行滑块拖动
|
||||
logger.info("开始模拟拖动滑块...")
|
||||
|
||||
slider = await page.querySelector(
|
||||
"#captcha_modal > div > div.captcha_footer > div > div.sp-msg"
|
||||
)
|
||||
if not el:
|
||||
el = await page.querySelector(
|
||||
) or await page.querySelector(
|
||||
"#captcha_modal > div > div.captcha_footer > div > img"
|
||||
)
|
||||
box = await el.boundingBox()
|
||||
distance = await get_distance()
|
||||
await page.mouse.move(box["x"] + 10, box["y"] + 10)
|
||||
|
||||
if not slider:
|
||||
logger.error("未找到滑块元素!")
|
||||
return
|
||||
|
||||
box = await slider.boundingBox()
|
||||
start_x = box["x"] + 10
|
||||
start_y = box["y"] + 10
|
||||
|
||||
# 移动到滑块位置
|
||||
await page.mouse.move(start_x, start_y)
|
||||
await page.mouse.down()
|
||||
await asyncio.sleep(0.2)
|
||||
|
||||
# 分段拖动模拟人类操作
|
||||
steps = 30
|
||||
start_x = box["x"] # 直接使用box的x作为起点
|
||||
start_y = box["y"] # Y轴固定不变
|
||||
end_x = start_x + distance
|
||||
drag_log = []
|
||||
current_x = start_x
|
||||
|
||||
for i in range(steps):
|
||||
t = i / steps
|
||||
|
||||
if t < 0.7:
|
||||
# 加速阶段:贝塞尔控制点偏向终点
|
||||
phase_ratio = t / 0.7
|
||||
x_ratio = 3 * (phase_ratio**2) - 2 * (phase_ratio**3) # 三阶缓动
|
||||
current_x = start_x + distance * 0.8 * x_ratio
|
||||
noise = random.uniform(-2, 2) # 允许较大初始抖动
|
||||
# 变速拖动:先快后慢
|
||||
if i < steps * 0.7:
|
||||
step_size = distance * 0.8 / (steps * 0.7)
|
||||
else:
|
||||
# 减速阶段:精准收敛至终点
|
||||
phase_ratio = (t - 0.7) / 0.3
|
||||
x_ratio = 0.8 + 0.2 * (1-(1-phase_ratio)**2) # 线性缓入
|
||||
current_x = start_x + distance * x_ratio
|
||||
noise = random.uniform(-1, 1)
|
||||
step_size = distance * 0.2 / (steps * 0.3)
|
||||
|
||||
# 应用噪声(距离越大抖动幅度越大)
|
||||
current_x += noise
|
||||
# 添加随机抖动
|
||||
current_x += step_size + random.uniform(-1, 1)
|
||||
|
||||
# 移动鼠标(Y轴始终不变)
|
||||
await page.mouse.move(current_x, start_y, steps=1)
|
||||
# 记录拖动轨迹(调试用)
|
||||
drag_log.append((current_x, start_y))
|
||||
|
||||
await page.waitFor(random.randint(150,400))
|
||||
await page.mouse.move(current_x, start_y)
|
||||
await asyncio.sleep(random.uniform(0.03, 0.08))
|
||||
|
||||
# 最终微调
|
||||
for _ in range(3):
|
||||
current_x += random.uniform(-1, 1)
|
||||
await page.mouse.move(current_x, start_y)
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
# 释放鼠标
|
||||
await page.mouse.up()
|
||||
logger.info("过滑块结束")
|
||||
logger.info(f"滑块拖动完成,轨迹点: {len(drag_log)}个")
|
||||
|
||||
# 保存轨迹图像(调试用)
|
||||
debug_img = cv2.imread("image.png")
|
||||
for x, y in drag_log:
|
||||
cv2.circle(debug_img, (int(x - start_x), 50), 2, (0, 0, 255), -1)
|
||||
cv2.imwrite("debug_drag_path.png", debug_img)
|
||||
logger.info("拖动轨迹已保存至 debug_drag_path.png")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"滑块验证失败: {str(e)}")
|
||||
logger.exception(e)
|
||||
|
||||
# 保存错误截图
|
||||
await page.screenshot({'path': 'slider_error.png'})
|
||||
logger.error("错误截图已保存至 slider_error.png")
|
||||
raise
|
||||
|
||||
|
||||
async def verification_shape(page):
|
||||
@ -947,20 +991,20 @@ async def verification_shape(page):
|
||||
i -= 1
|
||||
await page.waitForSelector("div.captcha_footer img")
|
||||
image_src = await page.Jeval(
|
||||
"#cpc_img", 'el => el.getAttribute("src")'
|
||||
"#main_img", 'el => el.getAttribute("src")'
|
||||
)
|
||||
request.urlretrieve(image_src, "shape_image.png")
|
||||
width = await page.evaluate(
|
||||
'() => { return document.getElementById("cpc_img").clientWidth; }'
|
||||
'() => { return document.getElementById("main_img").clientWidth; }'
|
||||
)
|
||||
height = await page.evaluate(
|
||||
'() => { return document.getElementById("cpc_img").clientHeight; }'
|
||||
'() => { return document.getElementById("main_img").clientHeight; }'
|
||||
)
|
||||
image = Image.open("shape_image.png")
|
||||
resized_image = image.resize((width, height))
|
||||
resized_image.save("shape_image.png")
|
||||
|
||||
b_image = await page.querySelector("#cpc_img")
|
||||
b_image = await page.querySelector("#main_img")
|
||||
b_image_box = await b_image.boundingBox()
|
||||
image_top_left_x = b_image_box["x"]
|
||||
image_top_left_y = b_image_box["y"]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user