zoukankan      html  css  js  c++  java
  • 第二十五节:scrapy爬虫识别验证码(四)手绘验证码识别

    一、介绍

    今天主要介绍的是微博客户端在登录时出现的四宫格手绘验证码,不多说直接看看验证码长成什么样。

          

    二、思路

    1、由于微博上的手绘验证码只有四个宫格,且每个宫格之间都有有向线段连接,所以我们可以判断四个宫格不同方向的验证码一共有24种,

    我们将四个宫格进行标号,得到的结果如下:

    则我们可以排列出24种不同的手绘方向的验证码,分别为一下24种

    1234 2134 3124 4321
    1243 2143 3142 4312
    1342 2314 3214 4123
    1324 2341 3241 4132
    1423 2413 3412 4213
    1432 2431 3421 4231

    2、我们通过获取到微博客户端的24种手绘验证码后需要进行模板匹配,这样通过全图匹配的方式进行滑动。

    三、代码实现

    1、首先是要通过微博移动端(https://passport.weibo.cn/signin/login)批量获取手绘验证码,但是这个验证码不一定出现,

    只有在账号存在风险或者频繁登录的时候才会出现。获取手绘验证码的代码如下:

    注意:需要将模拟浏览器所以元素(用户名框,密码框)加载完了才能发送用户名和密码,否则报错

     1 # -*- coding:utf-8 -*-
     2 import time
     3 from io import BytesIO
     4 from PIL import Image
     5 from selenium import webdriver
     6 from selenium.webdriver.common.by import By
     7 from selenium.common.exceptions import TimeoutException
     8 from selenium.webdriver.support.ui import WebDriverWait
     9 from selenium.webdriver.support import expected_conditions as EC
    10 
    11 
    12 class CrackWeiboSlide():
    13     def __init__(self):
    14         self.url = "https://passport.weibo.cn/signin/login?entry=mweibo&r=https://m.weibo.cn/"
    15         self.browser = webdriver.Chrome(r"D:chromedriver.exe")
    16         self.browser.maximize_window()
    17         self.wait = WebDriverWait(self.browser,5)
    18 
    19 
    20     def __del__(self):
    21         self.browser.close()
    22 
    23     def open(self):
    24         # 打开模拟浏览器
    25         self.browser.get(self.url)
    26         # 获取用户名元素
    27         username = self.wait.until(EC.presence_of_element_located((By.XPATH,'//*[@id="loginName"]')))
    28         # 获取密码框元素
    29         password = self.wait.until(EC.presence_of_element_located((By.XPATH,'//*[@id="loginPassword"]')))
    30         # 获取登录按钮元素
    31         submit = self.wait.until(EC.element_to_be_clickable((By.XPATH,'//*[@id="loginAction"]')))
    32         # 提交数据并登录
    33         username.send_keys("15612345678")
    34         password.send_keys("xxxxxxxxxxxx")
    35         submit.click()
    36 
    37 
    38     def get_image(self,name = "captcha.png"):
    39         try:
    40             # 获取验证码图片元素
    41             img = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME,"patt-shadow")))
    42             time.sleep(1)
    43             # 获取验证码图片所在的位置
    44             location = img.location
    45             # 获取验证码图片的大小
    46             size = img.size
    47             top = location["y"]  #
    48             bottom = location["y"] + size["height"]  #
    49             left = location["x"]  #
    50             right = location["x"] + size["width"]  #
    51             print("验证码的位置:", left, top, right, bottom)
    52             # 将当前窗口进行截屏
    53             screenshot = self.browser.get_screenshot_as_png()
    54             # 读取截图
    55             screenshot = Image.open(BytesIO(screenshot))
    56             # 剪切九宫格图片验证码
    57             captcha = screenshot.crop((left, top, right, bottom))
    58             # 将剪切的九宫格验证码保存到指定位置
    59             captcha.save(name)
    60             print("微博登录验证码保存完成!!!")
    61             return captcha
    62         except TimeoutException:
    63             print("没有出现验证码!!")
    64             # 回调打开模拟浏览器函数
    65             self.open()
    66 
    67 
    68     def main(self):
    69         count = 1
    70         while True:
    71             # 调用打开模拟浏览器函数
    72             self.open()
    73             # 调用获取验证码图片函数
    74             self.get_image(str(count) + ".png")
    75             count += 1
    76 
    77 
    78 if __name__ == '__main__':
    79     crack = CrackWeiboSlide()
    80     crack.main()
    批量获取手绘验证码

    得到的24种手绘验证码,同时需要对这些手绘验证码根据上边的编号进行命名

    上图就是我们需要的模板,接下来我们进行遍历模板匹配即可

    2、模板匹配

     通过遍历手绘验证码模板进行匹配

      1 import os
      2 import time
      3 from io import BytesIO
      4 from PIL import Image
      5 from selenium import webdriver
      6 from selenium.webdriver import ActionChains
      7 from selenium.webdriver.common.by import By
      8 from selenium.common.exceptions import TimeoutException
      9 from selenium.webdriver.support.ui import WebDriverWait
     10 from selenium.webdriver.support import expected_conditions as EC
     11 
     12 class CrackWeiboSlide():
     13     def __init__(self):
     14         self.url = "https://passport.weibo.cn/signin/login?entry=mweibo&r=https://m.weibo.cn/"
     15         self.browser = webdriver.Chrome(r"D:chromedriver.exe")
     16         self.browser.maximize_window()
     17         self.wait = WebDriverWait(self.browser,5)
     18 
     19 
     20     def __del__(self):
     21         self.browser.close()
     22 
     23     def open(self):
     24         # 打开模拟浏览器
     25         self.browser.get(self.url)
     26         # 获取用户名元素
     27         username = self.wait.until(EC.presence_of_element_located((By.XPATH,'//*[@id="loginName"]')))
     28         # 获取密码框元素
     29         password = self.wait.until(EC.presence_of_element_located((By.XPATH,'//*[@id="loginPassword"]')))
     30         # 获取登录按钮元素
     31         submit = self.wait.until(EC.element_to_be_clickable((By.XPATH,'//*[@id="loginAction"]')))
     32         # 提交数据并登录
     33         username.send_keys("15612345678")
     34         password.send_keys("xxxxxxxxxxxx")
     35         submit.click()
     36 
     37 
     38     def get_image(self,name = "captcha.png"):
     39         try:
     40             # 获取验证码图片元素
     41             img = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME,"patt-shadow")))
     42             time.sleep(1)
     43 
     44             # 获取验证码图片所在的位置
     45             location = img.location
     46 
     47             # 获取验证码图片的大小
     48             size = img.size
     49             top = location["y"]                      #
     50             bottom = location["y"] + size["height"]  #
     51             left = location["x"]                     #
     52             right = location["x"] + size["width"]    #
     53             print("验证码的位置:", left, top, right, bottom)
     54 
     55             # 将当前窗口进行截屏
     56             screenshot = self.browser.get_screenshot_as_png()
     57 
     58             # 读取截图
     59             screenshot = Image.open(BytesIO(screenshot))
     60 
     61             # 剪切九宫格图片验证码
     62             captcha = screenshot.crop((left, top, right, bottom))
     63 
     64             # 将剪切的九宫格验证码保存到指定位置
     65             captcha.save(name)
     66             print("微博登录验证码保存完成!!!")
     67 
     68             # 返回微博移动端的验证码图片
     69             return captcha
     70         except TimeoutException:
     71             print("没有出现验证码!!")
     72 
     73             # 回调打开模拟浏览器函数
     74             self.open()
     75 
     76     def is_pixel_equal(self,image,template,i,j):
     77 
     78         # 取出两张图片的像素点
     79         pixel1 = image.load()[i,j]              # 移动客户端获取的验证码
     80         pixel2 = template.load()[i,j]           # 模板文件里的验证码
     81         threshold = 20                          # 阈值
     82         pix_r = abs(pixel1[0] - pixel2[0])      # R
     83         pix_g = abs(pixel1[1] - pixel2[1])      # G
     84         pix_b = abs(pixel1[2] - pixel2[2])      # B
     85         if (pix_r< threshold) and (pix_g< threshold ) and (pix_b< threshold) :
     86             return True
     87         else:
     88             return False
     89 
     90     def same_image(self,image,template):
     91         """
     92         :param image: 微博移动端获取的验证码图片
     93         :param template: 通过模板文件获取的验证码图片
     94         """
     95         threshold = 0.99            # 相似度阈值
     96         count = 0
     97         # 遍历微博移动端获取的验证码图片的宽度和高度
     98         for i in range(image.width):
     99             for j in range(image.height):
    100 
    101                 # 判断两张图片的像素是否相等
    102                 if self.is_pixel_equal(image,template,i,j):
    103                     count += 1
    104         result = float(count)/(image.width*image.height)
    105         if result >threshold:
    106             print("匹配成功!!!")
    107             return True
    108         else:
    109             return False
    110 
    111 
    112     def detect_image(self,image):
    113         # 遍历手绘验证码模板文件内的所有验证码图片
    114         for template_name in os.listdir(r"D:photo	emplates"):
    115             print("正在匹配",template_name)
    116 
    117             # 打开验证码图片
    118             template = Image.open(r"D:photo	emplates{}".format(template_name))
    119 
    120             if self.same_image(image,template):
    121                 # 返回这张图片的顺序,如4—>3—>1—>2
    122                 numbers = [int(number) for number in list(template_name.split(".")[0])]
    123                 print("按照顺序进行拖动",numbers)
    124                 return numbers
    125 
    126     def move(self,numbers):
    127         # 获得四个按点
    128         circles = self.browser.find_element_by_css_selector('.patt-wrap .patt-circ')
    129         dx = dy = 0
    130         # 由于是四个宫格,所以需要循环四次
    131         for index in range(4):
    132             circle = circles[numbers[index] - 1]
    133             # 如果是第一次循环
    134             if index == 0:
    135                 # 点击第一个点
    136                 action = ActionChains(self.browser).move_to_element_with_offset(circle,circle.size["width"]/2,circle.size['height']/2)
    137                 action.click_and_hold().perform()
    138             else:
    139                 # 小幅度移动次数
    140                 times = 30
    141                 # 拖动
    142                 for i in range(times):
    143                     ActionChains(self.browser).move_by_offset(dx/times,dy/times).perform()
    144                     time.sleep(1/times)
    145 
    146                 # 如果是最后一次循环
    147                 if index == 3:
    148                     # 松开鼠标
    149                     ActionChains(self.browser).release().perform()
    150                 else:
    151                     # 计算下一次偏移
    152                     dx = circles[numbers[index + 1] - 1].location['x'] - circle.location['x']
    153                     dy = circles[numbers[index + 1] - 1].location['y'] - circle.location['y']
    154 
    155 
    156     def main(self):
    157         # 调用打开模拟浏览器函数
    158         self.open()
    159         image = self.get_image("captcha.png")   # 微博移动端的验证码图片
    160         numbers = self.detect_image(image)
    161         self.move(numbers)
    162         time.sleep(10)
    163         print('识别结束')
    164 
    165 
    166 if __name__ == '__main__':
    167     crack = CrackWeiboSlide()
    168     crack.main()
    匹配验证识别

    四、识别结果

    通过循环四次后绘出四条方向,最终得到效果图

  • 相关阅读:
    C语言面试题——大小端测试(一)
    C语言面试题——联合体测cpu的大小端
    C语言面试题——sizeof的注意点
    C语言面试题——联合体测cpu的大小端
    C语言面试题——指针运算
    poj2183
    poj1972
    poj2014
    poj1970
    poj1918
  • 原文地址:https://www.cnblogs.com/zhaco/p/11146254.html
Copyright © 2011-2022 走看看