前言
上篇文章记录了2种分割验证码的方法,此外还有一种叫做”滴水算法”(Drop Fall Algorithm)的方法,但本人智商原因看这个算法看的云里雾里的,所以今天记录滑动验证码的处理吧。网上据说有大神已经破解了滑动验证码的算法,可以不使用selenium来破解,但本人能力不足还是使用笨方法吧。
基础原理很简单,首先点击验证码按钮后的图片是滑动后的完整结果,点击一下滑块后会出现拼图,对这2个分别截图后比较像素值来找出滑动距离,并结合selenium来实现拖拽效果。
至于selenium怎么安装就不说了,滑动验证码的一个难点就是要模拟人的拖拽行为,移动快了不行,慢了也不行。
这里以国家企业公示网站为例:
# -*- coding: utf-8 -*- import time import random from io import BytesIO from PIL import Image from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver import ActionChains from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class Slide(object): """滑动验证码破解""" def __init__(self, target): self.target = target # 要搜索的公司名称 self.driver = webdriver.Chrome() self.wait = WebDriverWait(self.driver, 10) def crop(self, left, top, right, bottom, pic_name): """截屏并裁剪""" ss = Image.open(BytesIO(self.driver.get_screenshot_as_png())) cp = ss.crop((left, top, right, bottom)) # 注意这里顺序 cp.save(pic_name) return cp def calc_move(self, pic1, pic2): """根据阈值计算移动距离""" pix1 = pic1.load() pix2 = pic2.load() threshold = 200 move = 0 # 因为滑块都从左向右滑动,而碎片本身宽度为60所以从60开始遍历 for i in range(60, pic1.size[0]): flag = False for j in range(pic1.size[1]): r = abs(pix1[i, j][0] - pix2[i, j][0]) g = abs(pix1[i, j][1] - pix2[i, j][1]) b = abs(pix1[i, j][2] - pix2[i, j][2]) # if r > threshold and g > threshold and b > threshold: # 方法1:分别判断rgb大于阈值 # flag = True # break if r + g + b > threshold: # 方法2:判断rgb总和跟阈值比较,效果比1好 为什么呢?? flag = True break if flag: move = i break return move def path1(self, distance): """绘制移动路径方法1,构造一个等比数列""" q = 0.4 # 测试后发现0.4效果最佳 n = 10 # 最多移动几次 a1 = ((1 - q) * distance) / (1 - q**n) result = [] for o in range(1, n + 1): an = a1 * q**(o - 1) if an < 0.1: # 小于移动阈值的就不要了 break t = random.uniform(0, 0.5) # 测试后0.5秒的间隔成功率最高 result.append([an, 0, t]) return result def path2(self, distance): """绘制移动路径方法2,模拟物理加速、减速运动,效果比1好""" result = [] current = 0 # 减速阈值 mid = distance * 4 / 5 # 计算间隔 t = 0.2 # 初速度 v = 0 while current < (distance - 10): if current < mid: # 加速度为正2 a = 2 else: # 加速度为负3 a = -3 # 初速度v0 v0 = v # 当前速度v = v0 + at v = v0 + a * t # 移动距离x = v0t + 1/2 * a * t^2 move = v0 * t + 0.5 * a * t * t # 当前位移 current += move # 加入轨迹 result.append([round(move), 0, random.uniform(0, 0.5)]) return result def run(self): self.driver.get("http://www.gsxt.gov.cn/index") input_box = self.driver.find_element_by_id('keyword') input_box.send_keys(self.target) search_btn = self.driver.find_element_by_id('btn_query') time.sleep(3) # 注意这里等一下再点,否则会出现卡死现象 search_btn.click() # 等待验证码弹出 bg_pic = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, "gt_cut_fullbg"))) # html中坐标原点是左上角,右为x轴正方向,下为y轴正方向 # 输出的x为正就是此元素距离屏幕左侧距离 # 输出的y为正就是此元素距离屏幕上侧距离 # 所以我们需要截图的四个距离如下: top, bottom, left, right = ( bg_pic.location['y'], bg_pic.location['y'] + bg_pic.size['height'], bg_pic.location['x'], bg_pic.location['x'] + bg_pic.size['width']) time.sleep(1) cp1 = self.crop(left, top, right, bottom, '1.png') # 获取滑块按钮并点击一下 slide = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, "gt_slider_knob"))) slide.click() time.sleep(3) # 等3秒报错信息消失 TODO 这里应该可以改进 cp2 = self.crop(left, top, right, bottom, '2.png') move = self.calc_move(cp1, cp2) result = self.path1(move) # result = self.path2(move) # 拖动滑块 ActionChains(self.driver).click_and_hold(slide).perform() for x in result: ActionChains(self.driver).move_by_offset(xoffset=x[0],yoffset=x[1]).perform() # ActionChains(driver).move_to_element_with_offset(to_element=slide,xoffset=x[0],yoffset=x[1]).perform() time.sleep(x[-1]) # 如果使用方法1则需要sleep time.sleep(0.5) ActionChains(self.driver).release(slide).perform() # 释放按钮 time.sleep(0.8) element = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, "gt_info_text"))) ans = element.text if u"通过" in ans: # 这里也需要等一下才能获取到具体的链接 element = self.wait.until(EC.presence_of_all_elements_located((By.CLASS_NAME, "search_list_item"))) for o in self.driver.find_elements_by_xpath(u"//a[@target='_blank']"): print(o.get_attribute("href")) self.driver.quit() else: print("识别失败") self.driver.quit() if __name__ == '__main__': s = Slide('中国平安') s.run()
代码中注释很详细就不多说了,如果运行时候提示
selenium.common.exceptions.WebDriverException: Message: 'chromedriver' executable needs to be in PATH. Please see https://sites.google.com/a/chromium.org/chromedriver/home
则需要到 https://sites.google.com/a/chromium.org/chromedriver/home 下载驱动后解压到/usr/local/bin目录即可。
使用服务器运行时使用phantomjs替换chrome,另外失败的时候可以进行判断自动重试,有兴趣的小伙伴可以自己补充完善。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。
稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!
昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。
这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。
而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?
更新动态
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]