滑动验证码破解案例
1 # !/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 import time 4 import random 5 import re 6 import requests 7 from selenium import webdriver 8 from selenium.webdriver.common.by import By 9 from selenium.webdriver.support.wait import WebDriverWait # 等待元素的加载 10 from selenium.webdriver.support import expected_conditions as EC 11 from selenium.webdriver.common.action_chains import ActionChains # 实现拖拽 12 from PIL import Image 13 from io import BytesIO 14 15 16 class Slipping(object): 17 """ 18 19 """ 20 def __init__(self, url): 21 self.url = url 22 self.headers = { 23 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36', 24 } 25 26 @staticmethod 27 def judge(img_1, img_2, i, j): 28 pixel1 = img_1.getpixel((i, j)) 29 pixel2 = img_2.getpixel((i, j)) 30 for i in range(3): 31 if abs(pixel1[i] - pixel2[i]) >= 50: 32 return True 33 return False 34 35 def get_abscissa(self, img_1, img_2): 36 for i in range(0, 260): 37 for j in range(0, 116): 38 if self.judge(img_1, img_2, i, j): 39 return i 40 41 @staticmethod 42 def recompose(file, location_list): 43 """ 44 将图片整理成一张正常的图片 45 :param file: 无序图像文件 46 :param location_list:偏移位置记录列表 47 :return: 返回一个正常的验证码图像 48 """ 49 im = Image.open(file) 50 im.save('code.jpg') 51 size = im.size 52 new_im = Image.new('RGB', size) 53 location_lower = [] 54 location_upper = [] 55 for location in location_list: 56 if location['y'] == -58: 57 location_upper.append(im.crop((abs(location['x']), 58, abs(location['x'])+10, 116))) 58 if location['y'] == 0: 59 location_lower.append(im.crop((abs(location['x']), 0, abs(location['x'])+10, 58))) 60 61 x_offset = 0 62 for i in location_upper: 63 new_im.paste(i, (x_offset, 0)) # 把小图片放到 新的空白图片上 64 x_offset += i.size[0] 65 66 x_offset = 0 67 for i in location_lower: 68 new_im.paste(i, (x_offset, 58)) 69 x_offset += i.size[0] 70 new_im.show() 71 return new_im 72 73 def get_img(self, driver, div_path): 74 """ 75 获取网页的两张原始图片 76 :param driver: 浏览器实例如:webdriver.Chrome() 77 :param div_path: 下载连接的xpath 78 :return: 下载的图片对象 79 """ 80 div_class = driver.find_elements_by_xpath(div_path) 81 location_list = [] 82 for this in div_class: 83 location = {} 84 result = re.findall('background-image: url\("(.*?)"\); background-position: (.*?)px (.*?)px;', this.get_attribute('style')) 85 location['x'] = int(result[0][1]) 86 location['y'] = int(result[0][2]) 87 url_img = result[0][0] 88 location_list.append(location) 89 90 image_url = url_img.format('webp', 'jpg') 91 92 while True: 93 try: 94 response = requests.get(image_url, headers=self.headers).content 95 time.sleep(1) 96 except Exception: 97 continue 98 else: 99 break 100 image_file = BytesIO(response) # 是一张无序的图片 101 image = self.recompose(image_file, location_list) 102 return image 103 104 @staticmethod 105 def keep_move(dis): 106 # 当前位移 107 current = 12 108 # 什么时候减速 109 mid = (dis * 2) / 5 110 # 计算间隔 111 t = 0.23 112 # # 初速度 113 v = 0 114 print(dis) 115 116 track = [0, 1, 2, 4, 5] 117 while current < dis + 5: 118 if current < dis - 10: 119 a = 5*random.random() 120 elif v > 6: 121 a = -2.4*random.randint(2, 4) 122 else: 123 a = -5 124 # 初速度 125 v0 = v 126 # 当前速度 127 v = v0 + a * t 128 # 移动距离 129 move = v0 * t + 0.5 * a * t * t 130 current += move 131 track.append(round(move)) 132 133 x = dis + 5 - sum(track) 134 track.append(round(x/2)) 135 print(sum(track)) 136 # while current < dis + 5: 137 # if current < max(30, (mid / 4)) and v < 4: 138 # a = 15.6 # 加速度 139 # elif max(40, (mid / 4)) < current < 2 * (mid / 5) or v > 4: 140 # a = random.randint(4, 6)*random.random() 141 # else: 142 # if dis > 200 or v > 5: 143 # a = -9 144 # elif dis > 140 or v > 4: 145 # a = -7 146 # elif dis < 80: 147 # a = -6 148 # else: 149 # a = -5 150 # # 初速度 151 # v0 = v 152 # # 当前速度 153 # v = v0 + a * t 154 # # 移动距离 155 # move = v0 * t + 0.5 * a * t * t 156 # current += move 157 # if round(move) < -2: 158 # track.append(abs(round(move))) 159 # else: 160 # track.append(round(move)) 161 # x = dis + 5 - sum(track) 162 # track.append(round(x/2)) 163 # print(sum(track)) 164 return track 165 166 def main(self): 167 driver = webdriver.Chrome() 168 driver.get(self.url) 169 # 等待页面拖动按钮加载 170 wait = WebDriverWait(driver, 50, 0.5).until(EC.element_to_be_clickable((By.CLASS_NAME, 'gt_slider_knob '))) 171 image_1 = self.get_img(driver, '//div[@class="gt_cut_bg gt_show"]/div') 172 image_2 = self.get_img(driver, '//div[@class="gt_cut_fullbg gt_show"]/div') 173 # 获取缺口的横坐标 174 dis = self.get_abscissa(image_1, image_2) 175 track_list = self.keep_move(dis) 176 print(track_list) 177 print('第一步,点击滑动按钮') 178 ActionChains(driver).click_and_hold(on_element=wait).perform() # 179 time.sleep(1) 180 print('第二步,拖动元素') 181 track_1 = track_list[:9] 182 print(track_1) 183 for track in track_1: 184 ActionChains(driver).move_by_offset(xoffset=track, yoffset=0).perform() 185 ActionChains(driver).move_by_offset(xoffset=-1, yoffset=0).perform() 186 time.sleep(0.18432) 187 track_2 = track_list[9::] 188 print(track_2) 189 for track in track_2: 190 ActionChains(driver).move_by_offset(xoffset=track, yoffset=0).perform() 191 ActionChains(driver).move_by_offset(xoffset=-1, yoffset=0).perform() 192 time.sleep(0.21432) 193 track_list = [-1, -1, -2, -2, -2, -2, -1] 194 for track in track_list: 195 ActionChains(driver).move_by_offset(xoffset=track, yoffset=0).perform() 196 ActionChains(driver).move_by_offset(xoffset=-1, yoffset=0).perform() 197 time.sleep(1) 198 print('第三步,释放鼠标') 199 ActionChains(driver).release(on_element=wait).perform() 200 time.sleep(10) 201 driver.close() 202 203 204 if __name__ == '__main__': 205 try: 206 slide = Slipping('http://www.cnbaowen.net/api/geetest/') 207 slide.main() 208 except Exception as e: 209 print(e)
上面的代码遇到的问题主要是验证码正确移动到位却被拒绝。这里主要要模仿人类的操作习惯,即移动的间歇性和移动的速率变化。
代码通过分段移动和变化(随机)处理加速度,并控制速度,使得正确率非常高。这个案例是极验的一般情况,也是遇到比较多的情
况。后面我们将对一些特殊的滑动验证码给出其他的案例。