python验证码识别教程之滑动验证码
作者:Hi!Roy! 发布时间:2023-12-24 00:19:59
标签:python,滑动,验证码
前言
上篇文章记录了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,另外失败的时候可以进行判断自动重试,有兴趣的小伙伴可以自己补充完善。
来源:http://www.hi-roy.com/2017/09/21/Python验证码识别3/


猜你喜欢
- 本文实例讲述了Golang算法之田忌赛马问题实现方法。分享给大家供大家参考,具体如下:【田忌赛马问题】输入:输入有多组测试数据。 每组测试数
- 正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。re 模块使 Python 语言拥有全部的正则表达式功能。
- 前言变量的作用域是指程序代码能够访问该变量的区域,如果超出该区域,再访问时就会出现错误。在程序中,一般会根据变量的 “有
- TensorFlow 2.0测试版在今年春季发布,新版本比1.x版本在易用性上有了很大的提升。但是由于2.0发布还没有多久,现在大部分论文的
- deque 是 double-ended queue的缩写,类似于 list,不过提供了在两端插入和删除的操作。appendleft 在列表
- 本文实例讲述了php输出xml必须header的解决方法。分享给大家供大家参考。具体方法如下:问题描述:最近在做一个xml输出时发现我们直接
- 本文实例讲述了python通过wxPython打开一个音频文件并播放的方法。分享给大家供大家参考。具体如下:这段代码片段使用wx.lib.f
- CSV文件是一种纯文本文件,它使用特定的结构来排列表格数据。CSV文件内容看起来应该是下面这样的:column 1 name,column
- 分享一个 * 真网页拾色器(调色板),颜色丰富216色,使用方便。运行截图:<html id="container"
- 对于使用虚拟主机的站长朋友,我们可能不知道该服务器是否安装了某种我们需要的组件。这时我们可以使用下面的代码来判断。该函数功能:检查是否存在系
- 读写文件是最常见的IO操作。Python内置了读写文件的函数,用法和C是兼容的。读写文件前,我们先必须了解一下,在磁盘上读写文件的功能都是由
- 我们在使用ASP 内置的ADO组件进行数据库编程时,通常是在脚本的开头打开一个连接,并在脚本的最后关闭它,但是就较大脚本而言,在多
- RESTful API在Web项目开发中广泛使用,本文针对Go语言如何一步步实现RESTful JSON API进行讲解, 另外也会涉及到R
- 写在前面的话作为有个 Python 菜逼,之前一直用的 Pycharm,但是在主题这一块怎么调整都感觉要么太骚,看起来不舒服,要么就是简直不
- 1.表结构 2.表数据 3.查询teacher_name字段不能等于空并且也不能等于空字符SELECT * FROM s
- 一、匿名块和命名块◆PL/SQL块分为良好总:命名块和匿名块。◆匿名块:以declare或begin开始,每次执行匿名块都要通过客户端工具将
- 概述在数据库当中,索引就跟树的目录一样用来加快数据的查找速度,对于一个SQL查询操作,根据索引快速过滤掉不符合要求的数据并定位到符合要求的数
- VBSCRIPT中的日期,时间,星期函数很丰富,给我们使用带来了很大的方便,我个人使用最多的就是用now()来获取服务器的当前日期和时间。但
- 在flask中可以像go和angular那样使用页面模版(template),可以将HTML页面显示进行模版化,通过参数传递与页面进行数据交
- vue2.0中使用mapState及mapActions的方式 // 使用mapStatecomputed: { &nb