123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- import random
- import re
- import cv2
- import requests
- import cv2 as cv
- import numpy as np
- from PIL import Image
- from playwright.sync_api import sync_playwright, Page
- from util.playwright_util import is_element_present
- class CaptchaIdent:
- """
- 处理灰豚登录的滑块验证码
- """
- def __init__(self, page):
- self.page = page
- self.frame = self.page.frames[1]
- def start(self):
- for i in range(10):
- self.page.wait_for_timeout(1000)
- start_x, start_y = self.get_slide_block_img_and_start()
- self.get_slide_bg_img()
- distance1, distance2 = self.get_slide_distance(start_x, start_y)
- slide_result = self.move_to_notch(distance1, distance2)
- if not slide_result:
- self.refresh_captcha()
- else:
- return
- raise Exception("滑块验证失败")
- def get_slide_bg_img(self):
- """截取滑动验证码背景图片"""
- if self.frame is not None:
- bg_ele = self.frame.query_selector('.tc-bg-img')
- bg_style = bg_ele.evaluate(
- "element => window.getComputedStyle(element).getPropertyValue('background-image')")
- bg_img = re.search(r'url\("([^"]+)"\)', bg_style).group(1)
- r = requests.get(bg_img)
- with open("./slide_bg.png", "wb") as f:
- f.write(r.content)
-
- img = Image.open("./slide_bg.png")
- bg_img = Image.open("./bg.png")
- ratio = img.width / bg_img.width
- img = img.resize(size=(bg_img.width, int(img.height / ratio)))
- img.save("./slide_bg.png")
- def get_slide_block_img_and_start(self):
- """获取滑块图片以及初始x坐标"""
- print("正在获取滑块图片")
-
- self.page.wait_for_timeout(2000)
- slideBg = self.frame.query_selector('#slideBg')
- slideBg.screenshot(path="bg.png")
-
- captcha_frame = self.frame
-
-
- for i in range(3):
- slide_block_ele = captcha_frame.locator(".tc-fg-item").nth(i)
- slide_block_style = slide_block_ele.get_attribute("style")
-
- if "url" not in slide_block_style:
- continue
-
- top_value = re.search(r'top: (.+)px;', slide_block_style).groups()[0]
- if float(top_value) > 150:
- continue
-
- slide_block_x = float(re.search(r'left: (.+)px; top: ', slide_block_style).groups()[0])
- slide_block_y = float(top_value)
-
- slide_block_rect = slide_block_ele.bounding_box()
- bg = Image.open("bg.png")
-
- offset = 5
- slide_block_img = bg.crop((slide_block_x + offset, slide_block_y + offset,
- slide_block_x + slide_block_rect["width"] - offset,
- slide_block_y + slide_block_rect["height"] - offset))
- slide_block_img.save("slide_block.png")
- return slide_block_x + offset, slide_block_y + offset
- def get_slide_distance(self, start_x, slide_bg_y):
- """获取滑动距离"""
- print("正在获取滑动距离")
-
- slide_bg_img = cv.imread("./slide_bg.png")
- slide_block_img = cv.imread("./slide_block.png")
- block_height = slide_block_img.shape[0]
-
- slide_bg_img = slide_bg_img[int(slide_bg_y) - 5: int(slide_bg_y + block_height) + 5, :]
- slide_bg_img = self.set_contrast_brightness(slide_bg_img, 1, 100)
- cv.imwrite("./slide_bg_handled.png", slide_bg_img)
- slide_block_img = self.set_contrast_brightness(slide_block_img, 0.4, 0)
- cv.imwrite("./slide_block_handled.png", slide_block_img)
- result = cv.matchTemplate(slide_block_img, slide_bg_img, cv.TM_CCOEFF_NORMED)
- minVal, maxVal, minLoc, maxLoc = cv.minMaxLoc(result)
- th, tw = slide_block_img.shape[:2]
- tl = maxLoc
- br = (tl[0] + tw, tl[1] + th)
- cv2.rectangle(slide_bg_img, tl, br, (0, 0, 255), 2)
- cv2.rectangle(slide_bg_img, minLoc, (minLoc[0] + tw, minLoc[1] + th), (0, 255, 255), 2)
- cv2.imwrite("./match_result.png", slide_bg_img)
-
- distance1 = minLoc[0] - start_x
- distance2 = maxLoc[0] - start_x
- return distance1, distance2
- def get_slide_distance2(self, start_x):
- """获取滑动距离"""
- print("正在获取滑动距离")
-
- slide_bg_img = cv2.imread("./slide_bg.png")
- slide_bg_img = cv2.Canny(slide_bg_img, 100, 200)
- cv2.imwrite("./slide_bg_handle1.png", slide_bg_img)
- slide_bg_img = cv2.cvtColor(slide_bg_img, cv2.COLOR_GRAY2RGB)
- cv2.imwrite("./slide_bg_handle2.png", slide_bg_img)
- slide_block_img = cv.imread("./slide_block.png")
- slide_block_img = cv2.Canny(slide_block_img, 100, 200)
- cv.imwrite("./slide_block_handled1.png", slide_block_img)
- slide_block_img = cv2.cvtColor(slide_block_img, cv2.COLOR_GRAY2RGB)
- cv2.imwrite("./slide_block_handled2.png", slide_block_img)
- result = cv.matchTemplate(slide_block_img, slide_bg_img, cv.TM_CCOEFF_NORMED)
- minVal, maxVal, minLoc, maxLoc = cv.minMaxLoc(result)
- th, tw = slide_block_img.shape[:2]
- tl = maxLoc
- br = (tl[0] + tw, tl[1] + th)
- cv2.rectangle(slide_bg_img, tl, br, (0, 0, 255), 2)
- cv2.imwrite("./match_result.png", slide_bg_img)
-
- return maxLoc[0] - start_x, minLoc[0] - start_x
- @staticmethod
- def set_contrast_brightness(frame, contrast_value, brightness_value):
- if not contrast_value:
- contrast_value = 0.0
- if not brightness_value:
- brightness_value = 0
- blank = np.zeros(frame.shape, frame.dtype)
- frame = cv.addWeighted(frame, contrast_value, blank, 1 - contrast_value, brightness_value)
- return frame
- @staticmethod
- def get_tracks(distance):
- """获取移动轨迹"""
- tracks = []
- current = 0
- mid = distance * 3/4
- t = 0.5
- v = 1
- while current < distance:
- if current < mid:
- a = random.randint(5, 10)
- else:
- a = random.randint(-5, -3)
- v0 = v
- v = v0 + a * t
- move = v0 * t + 1 / 2 * a * t * t
- current += move
- tracks.append(round(current))
- return tracks
- def move_to_notch(self, distance1, distance2):
- """移动滑轨按钮到缺口处"""
-
- captcha_iframe = self.frame
- for i in range(2):
-
- slider_btn_rect = captcha_iframe.get_by_alt_text("slider").bounding_box()
- self.page.mouse.move(slider_btn_rect['x'], slider_btn_rect['y'])
- self.page.mouse.down()
- distance = [distance1, distance2][i]
- if distance <= 0:
- continue
- print(f"正在进行第{i + 1}次滑动,滑动距离{distance}")
- tracks = self.get_tracks(distance)
- for x in tracks:
- self.page.mouse.move(slider_btn_rect['x'] + x, random.randint(-5, 5) + slider_btn_rect['y'])
- self.page.mouse.move(slider_btn_rect['x'] + tracks[-1] + 5, random.randint(-5, 5) + slider_btn_rect['y'])
- self.page.mouse.move(slider_btn_rect['x'] + tracks[-1] - 5, random.randint(-5, 5) + slider_btn_rect['y'])
- self.page.mouse.up()
-
- self.page.wait_for_timeout(2000)
-
- if not is_element_present(self.page, '.ant-modal-body'):
- print("滑动验证通过")
- return True
- return False
- def refresh_captcha(self):
- """刷新验证码"""
-
- print("刷新验证码")
- self.frame.query_selector('.tc-action-icon').click()
- self.page.wait_for_timeout(2500)
- self.frame = self.page.frames[1]
|