滑动验证码破解案例

  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)

上面的代码遇到的问题主要是验证码正确移动到位却被拒绝。这里主要要模仿人类的操作习惯,即移动的间歇性和移动的速率变化。

代码通过分段移动和变化(随机)处理加速度,并控制速度,使得正确率非常高。这个案例是极验的一般情况,也是遇到比较多的情

况。后面我们将对一些特殊的滑动验证码给出其他的案例。

 

清澈的爱,只为中国