開發必學的驗證碼,教你從零寫一個驗證碼

  • 2019 年 10 月 3 日
  • 筆記

這周一寫了一篇《2000字諫言,給那些想學Python的人,建議收藏後細看!》給大家講了如何快速學習python。

其中就有說到我們為什麼不要執迷於框架、模組的調用,而要自己先去造輪子。那今天就給大家造一個。

驗證碼是web開發中不可缺少的元素,而python又提供了非常多的驗證碼模組幫助大家快速生成各種驗證碼。

那你知道驗證碼生成的原理嗎?所謂知其然,還要知其所以然。面試中,面試官不會因為你對框架很熟悉就誇讚你。

那今天小胖就帶大家一層一層撥開驗證碼的衣服,看看其中的小奧秘 -<-

演示環境

  • 作業系統:windows10
  • python版本:python 3.7
  • 程式碼編輯器:pycharm 2018.2
  • 使用第三方模組:pillow

驗證碼的必須元素

  1. 一張圖片
  2. 文本
  3. 干擾元素
    • 線條幹擾
    • 小圓點干擾

熟悉pillow庫

我們既然需要使用pillow庫製作驗證碼,那麼首先我們先來熟悉一下我們需要用到的方法。

  1. Image.new(): 這個方法可以生成一張圖片,有三個參數。
    • mode:顏色空間模式,可以是'RGBA','RGB','L'等等模式
    • size:圖片尺寸,接收一個兩個整數的元祖
    • color:圖片的填充顏色,可以是red,green等,也可以是rgb的三個整數的元祖。也就是背景顏色
from PIL import Image    captcha = Image.new('RGB', (1080, 900), (255,255,255))

上面程式碼創建了一個億RGB為顏色空間模式,尺寸為1080*900,背景顏色為白色的圖片。

  1. Image.save(): 保存圖片到本地
    • fp: 本地文件名
    • format: 可選參數,制定文件後綴名。
from PIL import Image    captcha = Image.new('RGB', (1080, 900), (255,255,255))    # captcha.save('captcha.png')  captcha.save('captcha', format='png')

上面兩種方式保存效果是一樣的。

  1. Image.show():顯示圖片,會調用電腦自帶的顯示圖片的軟體。

  2. ImageFont.truetype(): 載入一個字體文件。生成一個字體對象。
from PIL import ImageFont  #                        字體文件路徑 字體大小  font = ImageFont.truetype('simkai.ttf', 16)
  1. ImageDraw.Draw(): 生成畫筆對象。
from PIL import Image, ImageDraw    captcha = Image.new('RGB', (1080, 900), 'red')  draw = ImageDraw.Draw(captcha)

上面就創建了一個在captcha這張圖片上的畫筆,我們在這個圖片上畫任何東西都會使用這個畫筆對象。

  1. ImageDraw.Draw().text():在圖片上繪製給定的字元
from PIL import Image, ImageDraw, ImageFont    captcha = Image.new('RGB', (1080, 900), 'red')  font = ImageFont.truetype('simkai.ttf', 16)  draw = ImageDraw.Draw(captcha)    #      字元繪製位置  繪製的字元    制定字體      字元顏色  draw.text((0,0), 'hello world', font=font, fill='black')
  1. ImageDraw.Draw().line():在圖片上繪製線條
from PIL import Image, ImageDraw, ImageFont    captcha = Image.new('RGB', (1080, 900), 'red')  draw = ImageDraw.Draw(captcha)    #         線條起點  線條終點  draw.line([(0,0),(1080,900)], fill='black')
  1. ImageDraw.Draw().point(): 在圖片上繪製點
from PIL import Image, ImageDraw, ImageFont    captcha = Image.new('RGB', (1080, 900), 'red')  font = ImageFont.truetype('simkai.ttf', 16)  draw = ImageDraw.Draw(captcha)    #           點的位置      顏色  draw.point((500,500), fill='black')

製作我們的驗證碼我們就會使用到上面的方法。當然,pillow肯定不止這些方法,這裡我們就只列舉這些了。

製作驗證碼

  1. 首先我們定義一個類,初始化一些需要的參數。
  import string    class Captcha():      '''      captcha_size: 驗證碼圖片尺寸      font_size: 字體大小      text_number: 驗證碼中字元個數      line_number: 線條個數      background_color: 驗證碼的背景顏色      sources: 取樣字符集。驗證碼中的字元就是隨機從這個裡面選取的      save_format: 驗證碼保存格式      '''      def __init__(self, captcha_size=(150,100), font_size=30,text_number=4, line_number=4, background_color=(255, 255, 255), sources=None, save_format='png'):          self.text_number = text_number          self.line_number = line_number          self.captcha_size = captcha_size          self.background_color = background_color          self.font_size = font_size          self.format = save_format          if sources:              self.sources = sources          else:              self.sources = string.ascii_letters + string.digits

這裡說一下string模組。

  • string.ascii_letters: 得到a-zA-Z所有字元
  • string.digits: 得到0-9所有數字
  1. 隨機從sources獲取字元
import random    def get_text(self):      text = random.sample(self.sources,k=self.text_number)      return ''.join(text)

random.sample()方法:從第一個參數中隨機獲取字元。獲取個數有第二個參數指定。

  1. 隨機獲取繪製字元的顏色
  def get_font_color(self):      font_color = (random.randint(0, 150), random.randint(0, 150), random.randint(0, 150))      return font_color
  1. 隨機獲取干擾線條的顏色
def get_line_color(self):      line_color = (random.randint(0, 250), random.randint(0, 255), random.randint(0, 250))      return line_color
  1. 編寫繪製文字的方法
def draw_text(self,draw, text, font, captcha_width, captcha_height, spacing=20):      '''      在圖片上繪製傳入的字元      :param draw: 畫筆對象      :param text: 繪製的所有字元      :param font: 字體對象      :param captcha_width: 驗證碼的寬度      :param captcha_height: 驗證碼的高度      :param spacing: 每個字元的間隙      :return:      '''      # 得到這一竄字元的高度和寬度      text_width, text_height = font.getsize(text)      # 得到每個字體的大概寬度      every_value_width = int(text_width / 4)        # 這一竄字元的總長度      text_length = len(text)      # 每兩個字元之間擁有間隙,獲取總的間隙      total_spacing = (text_length-1) * spacing        if total_spacing + text_width >= captcha_width:          raise ValueError("字體加中間空隙超過圖片寬度!")        # 獲取第一個字元繪製位置      start_width = int( (captcha_width - text_width - total_spacing) / 2 )      start_height = int( (captcha_height - text_height) / 2 )        # 依次繪製每個字元      for value in text:          position = start_width, start_height          print(position)          # 繪製text          draw.text(position, value, font=font, fill=self.get_font_color())          # 改變下一個字元的開始繪製位置          start_width = start_width + every_value_width + spacing
  1. 繪製線條的方法
def draw_line(self, draw, captcha_width, captcha_height):      '''      繪製線條      :param draw: 畫筆對象      :param captcha_width: 驗證碼的寬度      :param captcha_height: 驗證碼的高度      :return:      '''      # 隨機獲取開始位置的坐標      begin = (random.randint(0,captcha_width/2), random.randint(0, captcha_height))      # 隨機獲取結束位置的坐標      end = (random.randint(captcha_width/2,captcha_width), random.randint(0, captcha_height))      draw.line([begin, end], fill=self.get_line_color())
  1. 繪製小圓點
def draw_point(self, draw, point_chance, width, height):      '''      繪製小圓點      :param draw: 畫筆對象      :param point_chance: 繪製小圓點的幾率 概率為 point_chance/100      :param width: 驗證碼寬度      :param height: 驗證碼高度      :return:      '''      # 按照概率隨機繪製小圓點      for w in range(width):          for h in range(height):              tmp = random.randint(0, 100)              if tmp < point_chance:                  draw.point((w, h), fill=self.get_line_color())
  1. 製作驗證碼
def make_captcha(self):      # 獲取驗證碼的寬度, 高度      width, height = self.captcha_size      # 生成一張圖片      captcha = Image.new('RGB',self.captcha_size,self.background_color)      # 獲取字體對象      font = ImageFont.truetype('simkai.ttf',self.font_size)      # 獲取畫筆對象      draw = ImageDraw.Draw(captcha)      # 得到繪製的字元      text = self.get_text(        # 繪製字元      self.draw_text(draw, text, font, width, height)        # 繪製線條      for i in range(self.line_number):          self.draw_line(draw, width, height)        # 繪製小圓點 10是概率 10/100, 10%的概率      self.draw_point(draw,10,width,height)        # 保存圖片      captcha.save('captcha',format=self.format)      # 顯示圖片      captcha.show()

這樣,我們就生成了我們的圖片驗證碼了,效果圖.

程式碼已全部上傳至Github:https://github.com/MiracleYoung/You-are-Pythonista/tree/master/PythonExercise/App/captcha_project

關注公眾號「Python專欄」,後台回復「機器學習電子書」獲取100本免費機器學習相關的電子書哦~