[Python自动化]selenium之验证码识别

这一专栏,将以目的为导向,以简化或自动化完成工作任务为目标,将Python运用于实践中,解决实际问题,以激发读者对这门脚本语言的学习兴趣。在开始Python自动化相关实战的学习前,建议对 Python语言本身 以及 Python 爬虫 的相关知识展开一定的学习与了解。对此博客已开设相关专栏,可直达。


往期内容提要:


“验证码的识别”无论是在网络爬虫,还是自动化领域,都是无法绕开的话题。此前作者曾在 [Python爬虫] 九、机器视觉与机器图像识别之Tesseract 一文中,对验证码的识别进行过初步介绍。在 教务管理系统:成绩、课表查询接口设计及抢课、监控功能实现 一文中,对图形验证码的识别进行了实战展示。在这一篇文章中,将对验证码的识别作出一个相对系统的概括与总结。

一般而言,在自动化工作中,如遇到验证码问题,我们一般通过三个方式解决。其一,手动解决;其二, 打码平台解决;三,代码解决。

针对第一种方法,如在入门篇【Python自动化】 selenium之网课学习自动化 一文中,在登录环节,为节省学习成本,我们便是采用的手动输入的方式处理验证码,通过图像识别。

在这里插入图片描述

def Login():
        print('***************正在加载必要元素,请耐心等待***************')
        browser.get('https://www.******.cn/home/')
        browser.find_element_by_xpath('//*[@id="accountFrom"]/label[1]/input').send_keys(username)
        browser.find_element_by_xpath('//*[@id="accountFrom"]/label[3]/input').send_keys(pwd)
        input("请输入验证码登陆后回车确认:")

针对第二种方法,正所谓利用“钞能力”解决问题,仅需足够预算,简单高效。在这里便不再多述。本文将主要讲述如何通过代码,实现验证码的识别。鉴于当前验证码形式的多样性,这里选用字符识别和滑动拼图两种最常见的验证方式,展开介绍。


验证码识别基本步骤梳理:
  1. 定位识别元素
  2. 获取识别(完整)图片
  3. 编写识别方法
  4. 通过识别验证

一、字符验证码识别

在这里插入图片描述

  1. 获取并保存验证码:

这一步的基本逻辑是:访问网页,保存验证码。但值得注意的是,每次页面刷新后验证码的值也将改变,因此请求验证码时候的cookie值应当与访问网页时的cookie值保持一致。

(1)访问网页
url1 = '手动打码/Login.aspx'
def get_cookie():
        headers1 = {
    
    
            "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0",
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
            "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
            "Accept-Encoding": "gzip, deflate",
            "Content-Type": "application/x-www-form-urlencoded",
            "Origin": "手动打码",
            "Connection": "keep-alive",
            "Referer": "手动打码",
            "Upgrade-Insecure-Requests": "1"
        }
        main = session.get(url1, headers=headers1)
        gb_headers = main.headers
        return gb_headers
(2)储存验证码
test = get_cookie()
url2 = '手动打码/Image.aspx'
def get_pic():
    # 验证码请求头
    headers2 = {
    
    
        "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0",
        "cookie": "varPartNewsManage.aspx=10" + test["Set-Cookie"]
    }

    re_pic = requests.get(url2, headers=headers2)
    response = re_pic.content

    file = "C:\\Users\\john\\Desktop\\1\\" + ".png"
    playFile = open(file, 'wb')
    playFile.write(response)
    playFile.close()
  1. 识别验证码:
def recognize_captcha(img_path):
    im = Image.open(img_path)
    num = pytesseract.image_to_string(im)
    return num

get_pic()
pic_res = recognize_captcha("C:\\Users\\john\\Desktop\\1\\" + ".png")
#print(pic_res)  # 验证码识别结果
  1. 登陆
def post_login():
    headers3 = {
    
    
        "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
        "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
        "Accept-Encoding": "gzip, deflate",
        "Content-Type": "application/x-www-form-urlencoded",
        "Origin": "手动打码",
        "Connection": "keep-alive",
        "Referer": "手动打码",
        "Upgrade-Insecure-Requests": "1",
        "cookie": "varPartNewsManage.aspx=10;" + test["Set-Cookie"]
    }
    data = {
    
     "Flag": "Login",
            "username": "手动打码",
            "password": "手动打码",
            "ddlUserClass": "1",
            "code1": pic_res,
            "ImageButton2.x": "64",
            "ImageButton2.y": "10"}
    res = session.post(url=url,data=data,headers=headers3)
    #print(res.request.headers)  #核验cookie是否有效带上
    #print(res.text)

post_login()

二、滑动拼图验证码识别

在这里插入图片描述

  1. 获取验证按钮与验证码的位置
def get_geetest_button(self):
    """
    获取初始验证按钮
    :return:
    """
    # 验证按钮
    button = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME,'geetest_radar_tip')))
    return button

def get_position(self):
    """
    获取验证码位置
    :return: 验证码位置元组
    """
    img = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'geetest_canvas_img')))
    print('img')
    location = img.location
    size = img.size
    top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size['width']

进入验证页面,获取验证按钮与验证码的位置

  1. 获取完整图片
    在这里插入图片描述
    在这里插入图片描述
def get_screenshot(self):
    """
    获取网页截图
    :return: 截图对象
    """
    screenshot = self.browser.get_screenshot_as_png()
    screenshot = Image.open(BytesIO(screenshot))
    return screenshot
def get_geetest_image(self, name='captcha.png'):
    """
    获取验证码图片
    :return: 图片对象
    """
    top, bottom, left, right = self.get_position()
    print('验证码位置', top, bottom, left, right)
    screenshot = self.get_screenshot()
    captcha = screenshot.crop((left, top, right, bottom))
    captcha.save(name)
    return captcha
def delete_style(self):
    '''
    执行js脚本,获取无滑块图
    :return None
    '''
    js = 'document.querySelectorAll("canvas")[2].style=""'
    self.browser.execute_script(js)
def get_gap(self, image1, image2):
    """
    获取缺口偏移量
    :param image1: 带缺口图片
    :param image2: 不带缺口图片
    :return:
    """
    left = 60
    print(image1.size[0])
    print(image1.size[1])
    for i in range(left, image1.size[0]):
        for j in range(image1.size[1]):
            if not self.is_pixel_equal(image1, image2, i, j):
                left = i
                return left
    return left

def is_pixel_equal(self, image1, image2, x, y):
    """
    判断两个像素是否相同
    :param image1: 图片1
    :param image2: 图片2
    :param x: 位置x
    :param y: 位置y
    :return: 像素是否相同
    """
    # 取两个图片的像素点
    pixel1 = image1.load()[x, y]
    pixel2 = image2.load()[x, y]
    threshold = 60
    if abs(pixel1[0] - pixel2[0]) < threshold and abs(pixel1[1] - pixel2[1]) < threshold and abs(
            pixel1[2] - pixel2[2]) < threshold:
        return True
    else:
        return False

通过调整css样式,获取完整图片。同时获取带缺口的图片加入带缺口图片进行对比,获取偏移量!(PIL)

  1. 验证

根据偏移量获取移动轨迹,控制滑块,填补缺口!

def get_track(self, distance):
    """
    根据偏移量获取移动轨迹
    :param distance: 偏移量
    :return: 移动轨迹
    """
    # 移动轨迹
    track = []
    # 当前位移
    current = 0
    # 减速阈值
    mid = distance * 4 / 5
    # 计算间隔
    t = 0.2
    # 初速度
    v = 0
    while current < distance:
        if current < mid:
            # 加速度为正2
            a = 2
        else:
            # 加速度为负3
            a = -1
        # 初速度v0
        v0 = v
        # 当前速度v = v0 + at
        v = v0 + a * t
        # 移动距离x = v0t + 1/2 * a * t^2
        move = v0 * t + 1 / 2 * a * t * t
        # 当前位移
        current += move
        # 加入轨迹
        track.append(round(move))
    return track

def move_to_gap(self, slider, track):
    """
    拖动滑块到缺口处
    :param slider: 滑块
    :param track: 轨迹
    :return:
    """
    ActionChains(self.browser).click_and_hold(slider).perform()
    for x in track:
        ActionChains(self.browser).move_by_offset(xoffset=x, yoffset=0).perform()
    time.sleep(0.5)
    ActionChains(self.browser).release().perform()

这里值得注意的是,由于验证码的特殊性,我们是不能控制滑块匀速滑过去的(人类是做不到的),极验在此也是做了验证的!所以我们按照人操作,先加速后减速做个优化!

此外,不管是电脑设置还是浏览器设置的缩放都要改成100%。否则将会影响图片的截取与偏移量的错误计算。


至此,本文也就进入尾声了。本文的撰写来自于开发中的一点心得体会,主要目的在于通过实践提高读者Python学习兴趣,解决实际问题。供对这一领域感兴趣的读者以参考借鉴。希望本文能够起到抛砖引玉之效,也欢迎大家的批评交流。


如果您有任何疑问或者好的建议,期待你的留言、评论与关注!

猜你喜欢

转载自blog.csdn.net/deng_xj/article/details/119912564