获取验证码图片
识别缺口位置
生成滑块拖动路径
模拟实现滑块拼合
1 import time 2 from io import BytesIO 3 from PIL import Image 4 from selenium import webdriver 5 from selenium.webdriver import ActionChains 6 from selenium.webdriver.common.by import By 7 from selenium.webdriver.support.ui import WebDriverWait 8 from selenium.webdriver.support import expected_conditions as EC 9 10 EMAIL = '1764662628@qq.com' 11 PASSWORD = '***' 12 BORDER = 6 13 INIT_LEFT = 60 14 15 16 class CrackGeetest(): 17 def __init__(self): 18 self.url = 'https://account.geetest.com/login' 19 self.browser = webdriver.Chrome() 20 self.wait = WebDriverWait(self.browser, 20) 21 self.email = EMAIL 22 self.password = PASSWORD 23 24 def __del__(self): 25 self.browser.close() 26 27 def get_geetest_button(self): 28 """ 29 获取初始验证按钮 30 :return: 31 """ 32 button = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_radar_tip'))) 33 return button 34 35 def get_position(self): 36 """ 37 获取验证码位置 38 :return: 验证码位置元组 39 """ 40 img = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_canvas_img'))) 41 time.sleep(2) 42 location = img.location 43 size = img.size 44 top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size[ 45 'width'] 46 return (top, bottom, left, right) 47 48 def get_screenshot(self): 49 """ 50 获取网页截图 51 :return: 截图对象 52 """ 53 screenshot = self.browser.get_screenshot_as_png() 54 screenshot = Image.open(BytesIO(screenshot)) 55 return screenshot 56 57 def get_slider(self): 58 """ 59 获取滑块 60 :return: 滑块对象 61 """ 62 slider = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_slider_button'))) 63 return slider 64 65 def get_geetest_image(self, name='captcha.png'): 66 """ 67 获取验证码图片 68 :return: 图片对象 69 """ 70 top, bottom, left, right = self.get_position() 71 print('验证码位置', top, bottom, left, right) 72 screenshot = self.get_screenshot() 73 captcha = screenshot.crop((left, top, right, bottom)) 74 captcha.save(name) 75 return captcha 76 77 def open(self): 78 """ 79 打开网页输入用户名密码 80 :return: None 81 """ 82 self.browser.get(self.url) 83 email = self.wait.until(EC.presence_of_element_located((By.ID, 'email'))) 84 password = self.wait.until(EC.presence_of_element_located((By.ID, 'password'))) 85 email.send_keys(self.email) 86 password.send_keys(self.password) 87 88 def get_gap(self, image1, image2): 89 """ 90 获取缺口偏移量 91 :param image1: 不带缺口图片 92 :param image2: 带缺口图片 93 :return: 94 """ 95 left = 60 96 for i in range(left, image1.size[0]): 97 for j in range(image1.size[1]): 98 if not self.is_pixel_equal(image1, image2, i, j): 99 left = i100 return left101 return left102 103 def is_pixel_equal(self, image1, image2, x, y):104 """105 判断两个像素是否相同106 :param image1: 图片1107 :param image2: 图片2108 :param x: 位置x109 :param y: 位置y110 :return: 像素是否相同111 """112 # 取两个图片的像素点113 pixel1 = image1.load()[x, y]114 pixel2 = image2.load()[x, y]115 threshold = 60116 if abs(pixel1[0] - pixel2[0]) < threshold and abs(pixel1[1] - pixel2[1]) < threshold and abs(117 pixel1[2] - pixel2[2]) < threshold:118 return True119 else:120 return False121 122 def get_track(self, distance):123 """124 根据偏移量获取移动轨迹125 :param distance: 偏移量126 :return: 移动轨迹127 """128 # 移动轨迹129 track = []130 # 当前位移131 current = 0132 # 减速阈值133 mid = distance * 4 / 5134 # 计算间隔135 t = 0.2136 # 初速度137 v = 0138 139 while current < distance:140 if current < mid:141 # 加速度为正2142 a = 2143 else:144 # 加速度为负3145 a = -3146 # 初速度v0147 v0 = v148 # 当前速度v = v0 + at149 v = v0 + a * t150 # 移动距离x = v0t + 1/2 * a * t^2151 move = v0 * t + 1 / 2 * a * t * t152 # 当前位移153 current += move154 # 加入轨迹155 track.append(round(move))156 return track157 158 def move_to_gap(self, slider, track):159 """160 拖动滑块到缺口处161 :param slider: 滑块162 :param track: 轨迹163 :return:164 """165 ActionChains(self.browser).click_and_hold(slider).perform()166 for x in track:167 ActionChains(self.browser).move_by_offset(xoffset=x, yoffset=0).perform()168 time.sleep(0.5)169 ActionChains(self.browser).release().perform()170 171 def login(self):172 """173 登录174 :return: None175 """176 submit = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'login-btn')))177 submit.click()178 time.sleep(10)179 print('登录成功')180 181 def crack(self):182 # 输入用户名密码183 self.open()184 # 点击验证按钮185 button = self.get_geetest_button()186 button.click()187 # 获取验证码图片188 image1 = self.get_geetest_image('captcha1.png')189 # 点按呼出缺口190 slider = self.get_slider()191 slider.click()192 # 获取带缺口的验证码图片193 image2 = self.get_geetest_image('captcha2.png')194 # 获取缺口位置195 gap = self.get_gap(image1, image2)196 print('缺口位置', gap)197 # 减去缺口位移198 gap -= BORDER199 # 获取移动轨迹200 track = self.get_track(gap)201 print('滑动轨迹', track)202 # 拖动滑块203 self.move_to_gap(slider, track)204 205 success = self.wait.until(206 EC.text_to_be_present_in_element((By.CLASS_NAME, 'geetest_success_radar_tip_content'), '验证成功'))207 print(success)208 209 # 失败后重试210 if not success:211 self.crack()212 else:213 self.login()214 215 216 if __name__ == '__main__':217 crack = CrackGeetest()218 crack.crack()
估计是高分屏的原因,截全图下来的时候我用画图软件看了图形验证码的像素位置,刚好是给的位置参数乘以2,所以保存下来的2张验证码的图还要压缩一下分辨率,加入下面语句就可以做对比匹配了。
1 captcha = screenshot.crop((2*left, 2*top, 2*right, 2*bottom))2 size = 258,1593 captcha.thumbnail(size)
修改参数
1 import time 2 from io import BytesIO 3 from PIL import Image 4 from selenium import webdriver 5 from selenium.webdriver import ActionChains 6 from selenium.webdriver.common.by import By 7 from selenium.webdriver.support.ui import WebDriverWait 8 from selenium.webdriver.support import expected_conditions as EC 9 10 EMAIL = '1764662628@qq.com' 11 PASSWORD = '***' 12 BORDER = 6 13 INIT_LEFT = 60 14 15 16 class CrackGeetest(): 17 def __init__(self): 18 self.url = 'https://account.geetest.com/login' 19 self.browser = webdriver.Chrome() 20 self.wait = WebDriverWait(self.browser, 20) 21 self.email = EMAIL 22 self.password = PASSWORD 23 24 def __del__(self): 25 self.browser.close() 26 27 def get_geetest_button(self): 28 """ 29 获取初始验证按钮 30 :return: 31 """ 32 button = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_radar_tip'))) 33 return button 34 35 def get_position(self): 36 """ 37 获取验证码位置 38 :return: 验证码位置元组 39 """ 40 img = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_canvas_img'))) 41 time.sleep(2) 42 location = img.location 43 size = img.size 44 top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size[ 45 'width'] 46 return (top, bottom, left, right) 47 48 def get_screenshot(self): 49 """ 50 获取网页截图 51 :return: 截图对象 52 """ 53 screenshot = self.browser.get_screenshot_as_png() 54 screenshot = Image.open(BytesIO(screenshot)) 55 return screenshot 56 57 def get_slider(self): 58 """ 59 获取滑块 60 :return: 滑块对象 61 """ 62 slider = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'geetest_slider_button'))) 63 return slider 64 65 def get_geetest_image(self, name='captcha.png'): 66 """ 67 获取验证码图片 68 :return: 图片对象 69 """ 70 top, bottom, left, right = self.get_position() 71 print('验证码位置', top, bottom, left, right) 72 screenshot = self.get_screenshot() 73 #captcha = screenshot.crop((left, top, right, bottom)) 74 captcha = screenshot.crop((2*left, 2*top, 2*right, 2*bottom)) 75 size = 258,159 76 captcha.thumbnail(size) 77 captcha.save(name) 78 return captcha 79 80 def open(self): 81 """ 82 打开网页输入用户名密码 83 :return: None 84 """ 85 self.browser.get(self.url) 86 email = self.wait.until(EC.presence_of_element_located((By.ID, 'email'))) 87 password = self.wait.until(EC.presence_of_element_located((By.ID, 'password'))) 88 email.send_keys(self.email) 89 password.send_keys(self.password) 90 91 def get_gap(self, image1, image2): 92 """ 93 获取缺口偏移量 94 :param image1: 不带缺口图片 95 :param image2: 带缺口图片 96 :return: 97 """ 98 left = 60 99 for i in range(left, image1.size[0]):100 for j in range(image1.size[1]):101 if not self.is_pixel_equal(image1, image2, i, j):102 left = i103 return left104 return left105 106 def is_pixel_equal(self, image1, image2, x, y):107 """108 判断两个像素是否相同109 :param image1: 图片1110 :param image2: 图片2111 :param x: 位置x112 :param y: 位置y113 :return: 像素是否相同114 """115 # 取两个图片的像素点116 pixel1 = image1.load()[x, y]117 pixel2 = image2.load()[x, y]118 threshold = 60119 if abs(pixel1[0] - pixel2[0]) < threshold and abs(pixel1[1] - pixel2[1]) < threshold and abs(120 pixel1[2] - pixel2[2]) < threshold:121 return True122 else:123 return False124 125 def get_track(self, distance):126 """127 根据偏移量获取移动轨迹128 :param distance: 偏移量129 :return: 移动轨迹130 """131 # 移动轨迹132 track = []133 # 当前位移134 current = 0135 # 减速阈值136 mid = distance * 4 / 5137 print("距离")138 print(distance)139 print(mid)140 # 计算间隔141 t = 0.1142 # 初速度143 v = 0144 145 while current < distance:146 if current < mid:147 # 加速度为正2148 a = 2149 else:150 # 加速度为负3151 a = -3152 # 初速度v0153 v0 = v154 print("速度")155 print(v)156 # 当前速度v = v0 + at157 v = v0 + a * t158 # 移动距离x = v0t + 1/2 * a * t^2159 move = v0 * t + 1 / 2 * a * t * t160 print("移动距离")161 print(move)162 # 当前位移163 current += move164 print("当前位移")165 print(current)166 # 加入轨迹167 track.append(round(move))168 return track169 170 def move_to_gap(self, slider, track):171 """172 拖动滑块到缺口处173 :param slider: 滑块174 :param track: 轨迹175 :return:176 """177 ActionChains(self.browser).click_and_hold(slider).perform()178 for x in track:179 ActionChains(self.browser).move_by_offset(xoffset=x, yoffset=0).perform()180 time.sleep(0.5)181 ActionChains(self.browser).release().perform()182 183 def login(self):184 """185 登录186 :return: None187 """188 submit = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'login-btn')))189 submit.click()190 time.sleep(10)191 print('登录成功')192 193 def crack(self):194 # 输入用户名密码195 self.open()196 # 点击验证按钮197 button = self.get_geetest_button()198 button.click()199 # 获取验证码图片200 image1 = self.get_geetest_image('captcha1.png')201 # 点按呼出缺口202 slider = self.get_slider()203 slider.click()204 # 获取带缺口的验证码图片205 image2 = self.get_geetest_image('captcha2.png')206 # 获取缺口位置207 gap = self.get_gap(image1, image2)208 print('缺口位置', gap)209 # 减去缺口位移210 gap -= BORDER211 # 获取移动轨迹212 track = self.get_track(gap)213 print('滑动轨迹', track)214 # 拖动滑块215 self.move_to_gap(slider, track)216 217 success = self.wait.until(218 EC.text_to_be_present_in_element((By.CLASS_NAME, 'geetest_success_radar_tip_content'), '验证成功'))219 print(success)220 221 # 失败后重试222 if not success:223 self.crack()224 else:225 self.login()226 227 228 if __name__ == '__main__':229 crack = CrackGeetest()230 crack.crack()
结果输出:
1 wljdeMacBook-Pro:Desktop wlj$ python3 CrackGeetest.py 2 验证码位置 172 331 528 786 3 验证码位置 172 331 528 786 4 缺口位置 94 5 距离 6 88 7 70.4 8 速度 9 0 10 移动距离 11 0.010000000000000002 12 当前位移 13 0.010000000000000002 14 速度 15 0.2 16 移动距离 17 0.030000000000000006 18 当前位移 19 0.04000000000000001 20 速度 21 0.4 22 移动距离 23 0.05000000000000001 24 当前位移 25 0.09000000000000002 26 速度 27 0.6000000000000001 28 移动距离 29 0.07 30 当前位移 31 0.16000000000000003 32 速度 33 0.8 34 移动距离 35 0.09000000000000002 36 当前位移 37 0.25000000000000006 38 速度 39 1.0 40 移动距离 41 0.11000000000000001 42 当前位移 43 0.3600000000000001 44 速度 45 1.2 46 移动距离 47 0.13 48 当前位移 49 0.4900000000000001 50 速度 51 1.4 52 移动距离 53 0.15 54 当前位移 55 0.6400000000000001 56 速度 57 1.5999999999999999 58 移动距离 59 0.17 60 当前位移 61 0.8100000000000002 62 速度 63 1.7999999999999998 64 移动距离 65 0.19 66 当前位移 67 1.0000000000000002 68 速度 69 1.9999999999999998 70 移动距离 71 0.21 72 当前位移 73 1.2100000000000002 74 速度 75 2.1999999999999997 76 移动距离 77 0.22999999999999998 78 当前位移 79 1.4400000000000002 80 速度 81 2.4 82 移动距离 83 0.25 84 当前位移 85 1.6900000000000002 86 速度 87 2.6 88 移动距离 89 0.27 90 当前位移 91 1.9600000000000002 92 速度 93 2.8000000000000003 94 移动距离 95 0.29000000000000004 96 当前位移 97 2.25 98 速度 99 3.0000000000000004100 移动距离101 0.31000000000000005102 当前位移103 2.56104 速度105 3.2000000000000006106 移动距离107 0.33000000000000007108 当前位移109 2.89110 速度111 3.400000000000001112 移动距离113 0.3500000000000001114 当前位移115 3.24116 速度117 3.600000000000001118 移动距离119 0.3700000000000001120 当前位移121 3.6100000000000003122 速度123 3.800000000000001124 移动距离125 0.3900000000000001126 当前位移127 4.0128 速度129 4.000000000000001130 移动距离131 0.41000000000000014132 当前位移133 4.41134 速度135 4.200000000000001136 移动距离137 0.43000000000000016138 当前位移139 4.84140 速度141 4.400000000000001142 移动距离143 0.4500000000000002144 当前位移145 5.29146 速度147 4.600000000000001148 移动距离149 0.4700000000000002150 当前位移151 5.76152 速度153 4.800000000000002154 移动距离155 0.4900000000000002156 当前位移157 6.25158 速度159 5.000000000000002160 移动距离161 0.5100000000000002162 当前位移163 6.76164 速度165 5.200000000000002166 移动距离167 0.5300000000000002168 当前位移169 7.29170 速度171 5.400000000000002172 移动距离173 0.5500000000000003174 当前位移175 7.84176 速度177 5.600000000000002178 移动距离179 0.5700000000000003180 当前位移181 8.41182 速度183 5.8000000000000025184 移动距离185 0.5900000000000003186 当前位移187 9.0188 速度189 6.000000000000003190 移动距离191 0.6100000000000003192 当前位移193 9.61194 速度195 6.200000000000003196 移动距离197 0.6300000000000003198 当前位移199 10.24200 速度201 6.400000000000003202 移动距离203 0.6500000000000004204 当前位移205 10.89206 速度207 6.600000000000003208 移动距离209 0.6700000000000004210 当前位移211 11.56212 速度213 6.800000000000003214 移动距离215 0.6900000000000004216 当前位移217 12.25218 速度219 7.0000000000000036220 移动距离221 0.7100000000000004222 当前位移223 12.96224 速度225 7.200000000000004226 移动距离227 0.7300000000000004228 当前位移229 13.690000000000001230 速度231 7.400000000000004232 移动距离233 0.7500000000000004234 当前位移235 14.440000000000001236 速度237 7.600000000000004238 移动距离239 0.7700000000000005240 当前位移241 15.21242 速度243 7.800000000000004244 移动距离245 0.7900000000000005246 当前位移247 16.0248 速度249 8.000000000000004250 移动距离251 0.8100000000000004252 当前位移253 16.81254 速度255 8.200000000000003256 移动距离257 0.8300000000000003258 当前位移259 17.64260 速度261 8.400000000000002262 移动距离263 0.8500000000000003264 当前位移265 18.490000000000002266 速度267 8.600000000000001268 移动距离269 0.8700000000000002270 当前位移271 19.360000000000003272 速度273 8.8274 移动距离275 0.8900000000000001276 当前位移277 20.250000000000004278 速度279 9.0280 移动距离281 0.91282 当前位移283 21.160000000000004284 速度285 9.2286 移动距离287 0.9299999999999999288 当前位移289 22.090000000000003290 速度291 9.399999999999999292 移动距离293 0.95294 当前位移295 23.040000000000003296 速度297 9.599999999999998298 移动距离299 0.9699999999999999300 当前位移301 24.01302 速度303 9.799999999999997304 移动距离305 0.9899999999999998306 当前位移307 25.0308 速度309 9.999999999999996310 移动距离311 1.0099999999999996312 当前位移313 26.009999999999998314 速度315 10.199999999999996316 移动距离317 1.0299999999999996318 当前位移319 27.04320 速度321 10.399999999999995322 移动距离323 1.0499999999999996324 当前位移325 28.09326 速度327 10.599999999999994328 移动距离329 1.0699999999999994330 当前位移331 29.16332 速度333 10.799999999999994334 移动距离335 1.0899999999999994336 当前位移337 30.25338 速度339 10.999999999999993340 移动距离341 1.1099999999999994342 当前位移343 31.36344 速度345 11.199999999999992346 移动距离347 1.1299999999999992348 当前位移349 32.49350 速度351 11.399999999999991352 移动距离353 1.1499999999999992354 当前位移355 33.64356 速度357 11.59999999999999358 移动距离359 1.169999999999999360 当前位移361 34.81362 速度363 11.79999999999999364 移动距离365 1.189999999999999366 当前位移367 36.0368 速度369 11.99999999999999370 移动距离371 1.209999999999999372 当前位移373 37.21374 速度375 12.199999999999989376 移动距离377 1.2299999999999989378 当前位移379 38.44380 速度381 12.399999999999988382 移动距离383 1.249999999999999384 当前位移385 39.69386 速度387 12.599999999999987388 移动距离389 1.269999999999999390 当前位移391 40.959999999999994392 速度393 12.799999999999986394 移动距离395 1.2899999999999987396 当前位移397 42.24999999999999398 速度399 12.999999999999986400 移动距离401 1.3099999999999987402 当前位移403 43.55999999999999404 速度405 13.199999999999985406 移动距离407 1.3299999999999985408 当前位移409 44.889999999999986410 速度411 13.399999999999984412 移动距离413 1.3499999999999985414 当前位移415 46.23999999999999416 速度417 13.599999999999984418 移动距离419 1.3699999999999986420 当前位移421 47.609999999999985422 速度423 13.799999999999983424 移动距离425 1.3899999999999983426 当前位移427 48.999999999999986428 速度429 13.999999999999982430 移动距离431 1.4099999999999984432 当前位移433 50.40999999999998434 速度435 14.199999999999982436 移动距离437 1.4299999999999982438 当前位移439 51.83999999999998440 速度441 14.39999999999998442 移动距离443 1.4499999999999982444 当前位移445 53.28999999999998446 速度447 14.59999999999998448 移动距离449 1.4699999999999982450 当前位移451 54.75999999999998452 速度453 14.79999999999998454 移动距离455 1.489999999999998456 当前位移457 56.24999999999997458 速度459 14.999999999999979460 移动距离461 1.509999999999998462 当前位移463 57.75999999999997464 速度465 15.199999999999978466 移动距离467 1.5299999999999978468 当前位移469 59.28999999999997470 速度471 15.399999999999977472 移动距离473 1.5499999999999978474 当前位移475 60.83999999999997476 速度477 15.599999999999977478 移动距离479 1.5699999999999978480 当前位移481 62.40999999999997482 速度483 15.799999999999976484 移动距离485 1.5899999999999976486 当前位移487 63.999999999999964488 速度489 15.999999999999975490 移动距离491 1.6099999999999977492 当前位移493 65.60999999999996494 速度495 16.199999999999974496 移动距离497 1.6299999999999975498 当前位移499 67.23999999999995500 速度501 16.399999999999974502 移动距离503 1.6499999999999975504 当前位移505 68.88999999999994506 速度507 16.599999999999973508 移动距离509 1.6699999999999975510 当前位移511 70.55999999999995512 速度513 16.799999999999972514 移动距离515 1.6649999999999974516 当前位移517 72.22499999999994518 速度519 16.49999999999997520 移动距离521 1.6349999999999973522 当前位移523 73.85999999999993524 速度525 16.19999999999997526 移动距离527 1.6049999999999973528 当前位移529 75.46499999999993530 速度531 15.89999999999997532 移动距离533 1.5749999999999973534 当前位移535 77.03999999999994536 速度537 15.59999999999997538 移动距离539 1.544999999999997540 当前位移541 78.58499999999994542 速度543 15.299999999999969544 移动距离545 1.514999999999997546 当前位移547 80.09999999999994548 速度549 14.999999999999968550 移动距离551 1.484999999999997552 当前位移553 81.58499999999994554 速度555 14.699999999999967556 移动距离557 1.454999999999997558 当前位移559 83.03999999999994560 速度561 14.399999999999967562 移动距离563 1.424999999999997564 当前位移565 84.46499999999993566 速度567 14.099999999999966568 移动距离569 1.3949999999999967570 当前位移571 85.85999999999993572 速度573 13.799999999999965574 移动距离575 1.3649999999999967576 当前位移577 87.22499999999992578 速度579 13.499999999999964580 移动距离581 1.3349999999999966582 当前位移583 88.55999999999992584 滑动轨迹 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1]585 True586 登录成功587 wljdeMacBook-Pro:Desktop wlj$