python易盾滑动验证码
上selenium 比较好上手的一种验证码, cv2模板匹配方法找缺口图在背景图中的位置, 计算要移动的距离, 移动缺口图 ,要注意的是移动轨迹模拟人移动的加速和减速
from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains import time from image_match import distance from image_match import get_tracks from image_match import getSlideInstance class yiDundriver(object): def __init__(self, url, prt='', time2wait=10): ex_path = 'C:\Program Files\Google\Chrome\Application\chromedriver.exe' chrome_options = Options() # chrome_options.add_argument("--proxy-server=http://%s" % prt) chrome_options.add_argument('--disable-gpu') #谷歌文档提到需要加上这个属性来规避bug chrome_options.add_argument('disable-infobars') self.browser = webdriver.Chrome(executable_path= ex_path, chrome_options=chrome_options) self.browser.set_window_size(500,800) self.browser.implicitly_wait(10) self.browser.get(url) self.wait = WebDriverWait(self.browser, time2wait) def __clickVerifyBtn(self): verify_btn = self.wait.until(EC.element_to_be_clickable((By.ID, "btnCertificationpone"))) verify_btn.click() def __slideVerifyCode(self): slider = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'yidun_slider'))) ActionChains(self.browser).click_and_hold(slider).perform() slider_loc_x = slider.location["x"] img = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, "yidun_bg-img"))) icon = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, "yidun_jigsaw"))) pic_width = img.size['width'] icon_width = icon.size['width'] img_tags = self.browser.find_elements_by_tag_name("img") img_url = img_tags[0].get_attribute("src") icon_url = img_tags[1].get_attribute("src") match_x = distance(img_url, icon_url, pic_width) if match_x == -1: raise Exception() slider_instance = getSlideInstance(pic_width, icon_width, match_x) tracks = get_tracks(slider_instance) for track in tracks: ActionChains(self.browser).move_by_offset(xoffset=track, yoffset=0).perform() else: ActionChains(self.browser).move_by_offset(xoffset=3, yoffset=0).perform() ActionChains(self.browser).move_by_offset(xoffset=-3, yoffset=0).perform() time.sleep(0.5) ActionChains(self.browser).release().perform() time.sleep(3) cur_loc_x = slider.location["x"] if cur_loc_x > slider_loc_x: print("success") return True else: return False def verifySlideCode(self,attempt_times=10): #尝试attempt_times次滑动验证,返回是否验证通过 self.wait.until(EC.text_to_be_present_in_element((By.CLASS_NAME,"yidun_tips__text"), r"向右拖动滑块填充拼图")) for attempt in range(attempt_times): try: if self.__slideVerifyCode(): return True except Exception as e: print(e) ActionChains(self.browser).release().perform() refresh = self.wait.until(EC.visibility_of_element_located((By.CLASS_NAME, "yidun_refresh"))) refresh.click() time.sleep(0.6) return False if __name__ == '__main__': drv = yiDundriver('http://dun.163.com/trial/jigsaw-wap') drv.verifySlideCode()
image_match.py
import cv2 import numpy as np import urllib.request as request import time def mathc_img(img_gray, template, value): #图标和原图的匹配位置,即为图标要移动的距离 w, h = template.shape[::-1] res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED) threshold = value loc = np.where(res >= threshold) result_size = len(loc[1]) if result_size > 0: middle = round(result_size/2) ''' #show match result guess_points = zip(*loc[::-1]) for pt in guess_points: cv2.rectangle(img_gray, pt, (pt[0] + w, pt[1] + h), (7, 249, 151), 1) cv2.imshow('Detected', img_gray) cv2.waitKey(0) cv2.destroyAllWindows() ''' return loc[1][middle] else: return -1 def cropHeight(icon): mid = round(icon.shape[1] / 2) c = icon[:, mid, 2] no0 = np.where(c != 0) first, last = no0[0][0], no0[0][-1] return first, last def loadImg(url): resp = request.urlopen(url) image = np.asarray(bytearray(resp.read()), dtype="uint8") image = cv2.imdecode(image, cv2.IMREAD_COLOR) return image def cropImage(img,top_y,bottom_y): img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) crop_img = img_gray[top_y:bottom_y,:] return crop_img def showImg(img,name): cv2.imshow(name, img) cv2.waitKey(0) cv2.destroyAllWindows() def distance(img_url,icon_url,display_width): value = 0.45 img_rgb = loadImg(img_url) tmp_rgb = loadImg(icon_url) crop_height = cropHeight(tmp_rgb) pic = cropImage(img_rgb,*crop_height) icon = cropImage(tmp_rgb,*crop_height) src_width = img_rgb.shape[1] guess_px = mathc_img(pic, icon, value) if guess_px is not -1: return round(guess_px * display_width / src_width) else: return -1 # copy demo def get_tracks(distance): ''''' 拿到移动轨迹,模仿人的滑动行为,先匀加速后匀减速 匀变速运动基本公式: ①v=v0+at ②s=v0t+½at² ③v²-v0²=2as :param distance: 需要移动的距离 :return: 存放每0.3秒移动的距离 ''' # 初速度 v = 0 # 单位时间为0.2s来统计轨迹,轨迹即0.2内的位移 t = 0.3 # 位移/轨迹列表,列表内的一个元素代表0.2s的位移 tracks = [] # 当前的位移 current = 0 # 到达mid值开始减速 mid = distance * 4 / 5 while current < distance: if current < mid: # 加速度越小,单位时间的位移越小,模拟的轨迹就越多越详细 a = 2 else: a = -3 # 初速度 v0 = v # 0.2秒时间内的位移 s = v0 * t + 0.5 * a * (t ** 2) # 当前的位置 current += s # 添加到轨迹列表 tracks.append(round(s)) # 速度已经达到v,该速度作为下次的初速度 v = v0 + a * t return tracks def getSlideInstance(img_w,icon_w,match_x): #考虑到滑块和图标的速度不总是1:1,获取滑块实际滑动的距离 slider_width = 40 iconMslider = icon_w - slider_width first_l = round(iconMslider / 2) mid_l = img_w - first_l #end_l = img_w - first_l - mid_l #eliminate 1px error if match_x <= first_l: return match_x * 2 elif match_x <= first_l + mid_l: return match_x + first_l else: return 2 * match_x - mid_l