`
liaofeng_xiao
  • 浏览: 127304 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

pycaptcha

阅读更多
pycaptcha是python的开源图片验证码库,很基础,但也记录一下吧。

1. 目录结构
下载pycaptcha,基本目录结构如下:
.
| -- Base.py: 定义BaseCaptcha,提供Factory接口供外面生成、验证验证码
| -- data: 用来存放数据的目录,验证码字体、验证码背景图片以及验证码词库都存在该目录下
|    | -- fonts
|    | -- pictures
|    | -- words
|-- File.py: 定义RandomFileFactory,用来在data目录中获取随机的字体、背景图片和词语
|-- __init__.py: 作为一种良好的习惯,__init__.py不定义任何东西,仅import
|-- Visual: 顾名思义,提供生成图片验证码功能,最关键的部分
|   |-- Backgrounds.py: 定义了几种验证码背景,比如CroppedImage、RandomDots
|   |-- Base.py: 定义ImageCaptcha,把验证码图片的背景、文字、扭曲操作抽象成Layer
|   |-- Distortions.py:扭曲验证码图片,提供了WigglyBlocks和SineWarp两种
|   |-- __init__.py
|   |-- Pictures.py: 定义ImageFactory,继承RandomFileFactory,用来在data/pictures文件夹选背景
|   |-- Tests.py:通过继承ImageFactory实现三个具体验证码类型,通过重写getLayers()方法实现
|   `-- Text.py:定义FontFactory用来选择随机字体,TextLayer用来生成验证码文字layer
`-- Words.py: WordList封装了验证码的词库,从data/words目录的指定词库选择一个词语作为验证码

2. 文件随机选择
File.py定义了RandomFileFactory,后面选择字体、单词和背景图片的class都继承自该类。
dataDir = os.path.join(os.path.split(os.path.abspath(__file__))[0], "data")
class RandomFileFactory(object):                                                                                                
    extensions = []                                                               
    basePath = "."                                                                
                                                                                  
    def __init__(self, *fileList):                                                
        self.fileList = fileList                                                  
        self._fullPaths = None

dataDir指向data文件夹,默认数据是存在该目录。
extensions在具体情景下指定,比如选择图片可能是.png/.jpg,选择字体是.ttf。
fileList指定搜索文件的起始目录列表,_findFullPaths()方法会迭代fileList,把所有符合extensions的文件都加入self._fullPaths,然后提供pick()函数,通过random.choice(self._fullPaths)随机选择。
def _findFullPaths(self):                                                  
        """From our given file list, find a list of full paths to files"""     
        paths = []                                                             
        for name in self.fileList:                                             
            path = os.path.join(dataDir, self.basePath, name)                  
            if os.path.isdir(path):                                            
                for content in os.listdir(path):                               
                    if self._checkExtension(content):                          
                        paths.append(os.path.join(path, content))              
            else:                                                              
                paths.append(path)                                             
        return paths

具体使用:
class ImageFactory(File.RandomFileFactory):                                                              
    extensions = [".png", ".jpeg"]                                                
    basePath = "pictures"                                                     
   
# 选择data/abstract/目录下的随机背景图片                                                                                                                       
abstract = ImageFactory("abstract")

单词的选择没使用RandomFileFactory,而是直接指定文件名,然后加载所有的单词到一个list,同样通过random.choice(list)选择。存在一些过滤,比如单词的最大长度最小长度。  

3. 验证码
先看BaseCaptcha:
class BaseCaptcha(object):                
    minCorrectSolutions = 1                                                    
    maxIncorrectSolutions = 0                                                  
                                                                               
    def __init__(self):                                                        
        self.solutions = []                                                    
        self.valid = True                                                                          
        self.id = randomIdentifier()                                           
        self.creationTime = time.time()

验证码有对应的solution,这里的solution竟然是一个列表。同时,验证码有时效性。
addSolution(self, solution)
testSolutions(self, solutions):大部分应用solutions都只包含一个答案吧。
这个两个方法很简单,略过。

提供了一个Captcha的工厂Factory用来封装验证码的获取、验证:
class Factory(object):                                     
    def __init__(self, lifetime=60*15):                                        
        self.lifetime = lifetime                                               
        self.storedInstances = {}

lifetime定义了验证码的有效时长,默认15分钟。
再看一下其他几个方法:
    def new(self, cls, *args, **kwargs): 生成一个验证码,cls是验证码的类,都定义在Tests.py。每生成一个新的验证,都保存在storedInstance里。
    def get(self, id): 通过id获取验证码
    def clean(self): 迭代self.storedInstances,清除过时的验证码
    def test(self, id, solutions): 测试验证码是否正确,检查之前先调用self.clean()清除所有过期验证码

说到验证码的存储,这里直接存在实例里,但在应用时量太大不靠谱,所以考虑实现,可能是:
1. 缓存:优点是不用手动clean()过期验证码了,没有持久化,但即便缓存系统奔溃了影响也不大。
2. 数据库: 真持久化了反而考虑有没有必要,假设网站崩溃,回复之后大量验证码也都超过过期时间(比如15分钟)了吧?
3. 文件:如果是测试,还不如例子里来的简单呢。


这里逻辑很清晰易懂。有点可以借鉴的就是验证码的id生成器:
def randomIdentifier(alphabet = string.ascii_letters + string.digits,          
                     length = 24):                                             
    return "".join([random.choice(alphabet) for i in xrange(length)])


关于验证码的test()方法,现在有验证码A,客户端输入答案并提交,携带验证码A的id和输入的答案跑去服务器验证结果,如果验证失败,原来的验证码A还存在于服务器端。一般网站的做法是又返回一个新的验证码B,同时把B的id埋在form中,这样用户再输入,验证的便是另一个新的验证码B,而不能多次尝试验证码A的答案。

但,客户端可以把验证码A的id记录下来,第一次失败后,第二次还拿验证码A的id跑去验证,这样就可以实现对一个验证码的多次尝试了。

所以,建议把test()方法改造一下,每个验证码只尝试一次,无论结果正确与否,马上删除。

4. 生成图片验证码
这部分是验证码的最核心部分,也是最难的部分。验证码的作用就是防止spammer,如何生成人类肉眼容易识别和理解但程序通过图像处理难以识别的文字是一门学问。
先看看ImageCaptcha:
class ImageCaptcha(Captcha.BaseCaptcha):                         
    defaultSize = (256,96)                                                                                                                                
    def __init__(self, *args, **kwargs):                                       
        Captcha.BaseCaptcha.__init__(self)                                     
        self._layers = self.getLayers(*args, **kwargs)

ImageCaptcha继承了BaseCaptcha,defaultSize定义了验证码图片的尺寸大小。
_layers属性非常关键,来看getLayers方法便会发现只是返回空的列表[],这个方法定义验证码生成的具体算法,由子类来覆盖实现,后面详细讲。
getImage()方法用来获取验证码图片,它又调用了render()方法,而render方法没做任何实质性的工作,本质上调用了_renderList()方法,直接来看看这个方法,代码如下:
def _renderList(self, layers, img):                                             
        for i in layers:                                                            
            if type(i) == tuple or type(i) == list:                            
                img = self._renderList(i, img)                                 
            else:                                                              
                img = i.render(img) or img                                     
        return img
layers参数就是getLayers()方法子类实现返回的,img则是验证码图片,新生成的:
img = Image.new("RGB", size)

_renderList()方法是,从子类获得Layers,然后,根据依次调用layer.render(img)方法,得到新的img,循环调用下去,最后得到的img就是最终的验证图片。
Layer是什么?验证码图片刚开始只是一张新生成的空白图片(img),然后经过添加背景、添加验证码文字、图片扭曲处理这三个步骤,得到最终的图片。这里的每个步骤都可以看做一个Layer,即抽象成对图片img的一个处理步骤。每个步骤的输入都是最开始的img对象,如何处理呢,不外img.paste()、img.transform()等几个PIL图像处理的常用方法,所以使用python做验证码还需要研究PIL。
另外,layer对象支持返回带list/tuple元素的列表对象。
这里getLayers()方法只返回[]的做法很妙,对Layer的抽象也很妙,参考设计模式的Strategy/Template Pattern。

下面看看如何生成背景图片、添加验证码文字、图片扭曲的具体算法就不介绍了,具体在:
背景图片生成算法:Backgrounds.py文件
添加验证码文字:Text.py(包括字体选择)
图片扭曲算法:Distortions.py(最核心的算法)
分享到:
评论

相关推荐

    python验证码生成第三方模块

    2. `pycaptcha`:这是一个轻量级的验证码生成库,提供了多种样式和配置选项。它允许开发者自定义字体、颜色、扭曲程度等,生成高质量的图像验证码。 3. `captcha`:这是一个功能更加强大的验证码库,支持生成多种...

    基于SVM的验证码破解程序

    svm 一个基于SVM的验证码破解程序 ##起因 弄这个项目的原因,是目前还没有发现一个比较好用的验证码识别模块,目前的验证码识别主要是基于google的光学字符识别Tesseract-OCR如PyOCR,识别精度...生成验证码pycaptcha

    图形验证码-.zip

    Python中有一些现成的库,如`pycaptcha`和` captcha`,它们简化了图形验证码的创建过程,提供了预设的样式和配置选项。 对于框架部分,这个案例可能涉及使用`Flask`或`Django`这样的Web框架来集成图形验证码。这些...

    toupiao_system.zip_python在线投票系统_投票统计

    可能还需要使用验证码库如pyCaptcha防止机器人投票。 3. 菜单模块: 菜单模块设计用于呈现投票系统的导航结构,帮助用户在不同的投票活动、设置和信息之间切换。它可以是一个多级菜单,根据权限显示不同内容。HTML...

    可以在python爬虫中用于验证码识别的库.rar

    5. **pycaptcha**:这个库主要用于生成和识别简单的数学验证码,例如加减乘除问题。 6. **Deep Learning 库**:对于更复杂的验证码,可以使用深度学习模型进行识别,如TensorFlow、Keras或PyTorch。这些库可以训练...

    captcha.rar

    Python社区有专门处理验证码的库,如`pycaptcha`,它可以方便地生成和验证图像验证码。此外,还有一些库如`opencv-python`可以帮助处理图像,提高验证码的识别效率。 6. **验证码的应用场景:** - 注册和登录验证...

    whatmail:简单的python cgi联系表格。 精通utf8,可刮胡子。 可选的验证码(Winograd,Pycaptcha)和gpgme加密

    @TheRealDod的 这是一个简单的cgi联系人表格,可以处理utf-8,这对我来说是至关重要的功能,因为并非我的所有朋友都用英语写信给我:) 它还具有一个。 默认情况下启用它,但是您可以配置whatmail禁用它。...

    Python实现简单生成验证码功能【基于random模块】

    除了自定义验证码生成器,还可以使用现成的库,如`pycaptcha`,它提供了更多的定制选项和图像验证码的生成。 在线随机生成工具可以作为辅助手段,帮助快速生成测试数据,但它们通常不适用于实际项目中的安全验证。...

Global site tag (gtag.js) - Google Analytics