2.2.9 12306火车订票验证码处理

本文探讨了在自动化测试中如何处理12306火车订票的图片验证码。通过分析12306页面,可以获取base64编码的验证码图片数据流,再转换成字节流保存。利用第三方服务器识别验证码,获取图序号后模拟点击。文章还详细解释了坐标转换和点击操作的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

       在实现自动化测试时,最为困难的就是验证码处理;那么常规的验证码都进行处理和识别,那么如果是类似12306等图片识别的验证码的话如何实现?那么今天小编就给大家分析下12306验证码如何实现自动化。

 

获取12306验证码图片

       按照常规思路无非两种,将截取的验证码图片发送给第三方服务器,然后通过第三方服务器进行识别后将结果进行返回;要么就是自己通过机器识别的方式不断进行学习,写大量的识别库然后完成识别操作;第二种方式耗时耗力,显然不可取,所以大部分都是选择第三方服务器进行完成;

       验证码图片的获取可以通过传统的Pillow模块完成图片的截取操作;但是此种方式会出现失真(是图片的质量下降,模糊),所以此处为了提高其图片的质量,可以直接获取图片的文件流对象,然后自己将文件流对象转换成图片的过程;

       通过分析系12306页面,使用元素定位发现验证码的src属性是可以直接获取对应图片通过base64加密的数据流,如果能够将此数据流转换成字节流,然后保存成图片不就获取到原始图片了。

那么获取到src属性的结果值则可以通过下面代码完成:

  self.get_code_elment=WebDriverWait(self.get_driver,5).until(lambda driver:driver.find_element_by_id("J-loginImg"))  get_src=self.get_code_elment.get_attribute("src")

但是得到的结果是base64编码数据流,所以可以通过base模块完成数据流转换成字节流,定义一个方法封装后即可获取到对应验证码图片

​​​​​​​

    def  get_code_image(self):        self.get_driver.find_element_by_class_name("login-hd-account").click()        try:            self.get_code_elment=WebDriverWait(self.get_driver,5).until(lambda driver:driver.find_element_by_id("J-loginImg"))            #此处src数据加载问题            time.sleep(3)            get_src=self.get_code_elment.get_attribute("src")            #得到base64格式编码数据            get_base64=get_src.split(",")[-1]            #获取到bytes对象,文件、图像实际都是基于byte流            get_byte=base64.b64decode(get_base64)            print(self.image_path)            with open(self.image_path,mode="wb") as fp:                #既可以实现以二进制流的形式读和写两种操作                fp.write(get_byte)        except:            print(sys.exc_info())

获取到验证码图片后,并将其保存到本地,最后将验证码图片发送到第三方服务器进行识别,此处使用的第三方服务器地址是:

http://littlebigluo.qicp.net:47720/访问后发现可以上传一张图片,然后会自动识别并返回对应验证码的图序号;后续只需要根据返回的图需要进行完成对应图的点击操作即可;(此处使用接口完成更加,但是此时为web自动化,所以使用了新创建一个浏览器对象进行操作),具体代码如下:

#-*- coding:utf-8 -*-##-------------------------------------------------------------------------#ProjectName:       Python2020#FileName:          Click_Code.py#Author:            mutou#Date:              2020/6/16 22:43#Description:#--------------------------------------------------------------------------from Day18.BaseModule.Base_Class import BaseClassfrom Day22.get_path import get_image_pathimport os# from Day22.Code12306.Get_Code_Image import GetCodeImageclass CickCode(BaseClass):    def __init__(self,url,browserType):        super(CickCode, self).__init__(url,browserType)
    #声明一个方法上传图片pic_xxfile    def  up_image(self,image_path):        self.get_driver.find_element_by_name("pic_xxfile").send_keys(image_path)        self.get_driver.find_element_by_xpath("/html/body/form/input[2]").click()        #识别后需要将识别的结果进行返回并获取        get_result=self.get_driver.find_element_by_xpath("/html/body/p[1]/font/font/b").text        #如果返回的是多个值的话,则值与值之间使用空格隔开        get_result_list=get_result.split(" ")        #获取结果后,此页面可以进行关闭        self.get_driver.close()        return get_result_list
if __name__ == '__main__':    get = GetCodeImage("https://kyfw.12306.cn/otn/resources/login.html", "Chrome","code.jpg")    get.get_code_image()    click=CickCode("http://littlebigluo.qicp.net:47720/","Chrome")    print(click.up_image(get.image_path))

获取到验证码的返回结果后,最后完成点击操作即可,此时点击需要注意以下几个问题:

    第一:每个小图对应的坐标点

    第二:如果存在多个图的话,则如何实现连续操作

同样还是通过鼠标定位发现验证码图片的长和高大小如下:其中长:300  高:188

那么如果每个小图取中间点的话,则可以把整个大验证码图片的中心作为原点,然后其他所有点全部相对该点进行偏移;最后可得到八个点:如下分析图:

 

然后由于浏览器的坐标并不是等价于数学的二维坐标方向,浏览器中是以左上角为原点,那么此时高就是y轴,从上往下的过程,宽还是x轴,所以上图分析的坐标点需要完成对称象限的操作;即结果为:

[-110,-30],[-40,-30],[40,-30],[110,-30],[-110,50],[-40,50],[40,50],[110,50]

最后可以通过鼠标模块中的move_by_offset方法完成点击操作;具体实现部分代码如下:​​​​​​​

#-*- coding:utf-8 -*-##-------------------------------------------------------------------------#ProjectName:       Python2020#FileName:          Get_Code_Image.py#Author:            mutou#Date:              2020/6/16 20:53#Description:获取12306验证码图片#--------------------------------------------------------------------------from Day18.BaseModule.Base_Class import BaseClassfrom selenium.webdriver.support.ui import WebDriverWaitimport sysimport base64import osimport timefrom Day22.get_path import get_image_pathfrom Day22.Code12306.Click_Code import CickCodefrom selenium.webdriver.common.action_chains import ActionChainsclass  GetCodeImage(BaseClass):    def __init__(self,url,browserType,image_name):        super().__init__(url,browserType)        self.image_name=image_name        self.image_path=os.path.join(get_image_path,image_name)        self.location=[[-110,-30],[-40,-30],[40,-30],[110,-30],[-110,50],[-40,50],[40,50],[110,50]]
    #获取12306验证码图片;最简单的方式就是通过Pillow模块完成验证码图片从首页中进行截取出来,直接截取出来的图片会影响图片的质量    #将图片变得模糊;如何提高图片质量的问题:图片都是以二进制数据进行传输的,如果能够直接获取图片的原二进制数据的话, 然后将二进制数据转写成    #一张图片,那么中间就不会发生质量问题;    def  get_code_image(self):        self.get_driver.find_element_by_class_name("login-hd-account").click()        try:            self.get_code_elment=WebDriverWait(self.get_driver,5).until(lambda driver:driver.find_element_by_id("J-loginImg"))            #此处src数据加载问题            time.sleep(3)            get_src=self.get_code_elment.get_attribute("src")            #得到base64格式编码数据            get_base64=get_src.split(",")[-1]            #获取到bytes对象,文件、图像实际都是基于byte流            get_byte=base64.b64decode(get_base64)            print(self.image_path)            with open(self.image_path,mode="wb") as fp:                #既可以实现以二进制流的形式读和写两种操作                fp.write(get_byte)        except:            print(sys.exc_info())
    #定义一个方法完成验证码的点击操作    def click_code(self,server_path,type):        #获取到第三方返回的结果        get_result_list=CickCode(server_path,type).up_image(self.image_path)        print(get_result_list)        #提取鼠标创建的对象        action=ActionChains(self.get_driver)        #遍历对应的图        for i in get_result_list:            #因为可以根据鼠标移动到指定的坐标点完成点击操作            #创建鼠标对象;此处的i表示的是第几幅图,而下面传入location中表示的是索引            get_value=self.location[int(i)-1]            #设定的坐标点是相对真个验证码图片而言,所以可以先讲鼠标对象移动到当前验证码元素对象上,然后实现偏移点击坐标点            action.move_to_element(self.get_code_elment).move_by_offset(get_value[0],get_value[1]).click()            #上面此种实现相当于完成每一次点击都重新创建一个鼠标对象。在程序开发中,实际是尽量的减少循环中创建对象;            #如果将鼠标创建对象提取的话,则必须将执行的动作于整个循环完毕后执行,否则在循环中有可能整个动作就已经被执行,则后续动作无法执行;        action.perform()

if __name__ == '__main__':    get=GetCodeImage("https://kyfw.12306.cn/otn/resources/login.html","Chrome","code.jpg")   # print(get.image_path)    get.get_code_image()    get.click_code("http://littlebigluo.qicp.net:47720/","Chrome")#注意:两个模块的相互引用容易造成模块初始化错误

如果需要获取整个所有的完整项目代码的话,则回复【12306】即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zemuerqi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值