获取12306验证码图片

获取方式

缺点

将截取的验证码图片发送给第三方服务器进行识别后,结果返回;


通过机器识别方式不断学习,写大量的识别库完成识别操作;

耗时耗力

通过传统的Pillow模块完成图片的截取操作

失真(备注:直接获取图片文件流对象并转换成图片)

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

python自动化测试Selenium:9 12306火车订票验证码处理_二进制数

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


# 获取12306验证码图片;最简单的方式就是通过Pillow模块完成验证码图片从首页中进行截取出来,直接截取出来的图片会影响图片的质量,将图片变得模糊;
    # 如何提高图片质量的问题:图片都是以二进制数据进行传输的,如果能够直接获取图片的原二进制数据的话,然后将二进制数据转写成一张图片,那么中间就不会发生质量问题;
    def get_code_image(self):
        self.get_driver.find_element_by_class_name("login-hd-account").click()
        try:
            # 获取到src属性的结果值
            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())
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.

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

#FileName:          Click_Code.py

from Day18.BaseModule.Base_Class import BaseClass
from Day22.get_path import get_image_path
import os
# from Day22.Code12306.Get_Code_Image import GetCodeImage
class 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))
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.

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

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

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

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

python自动化测试Selenium:9 12306火车订票验证码处理_二进制数_02

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

python自动化测试Selenium:9 12306火车订票验证码处理_Chrome_03


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

[-110,-30],[-40,-30],[40,-30],[110,-30],[-110,50],[-40,50],[40,50],[110,50]
  • 1.

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

#Description:获取12306验证码图片

from Day18.BaseModule.Base_Class import BaseClass
from selenium.webdriver.support.ui import WebDriverWait
import sys
import base64
import os
import time
from Day22.get_path import get_image_path
from Day22.Code12306.Click_Code import CickCode
from selenium.webdriver.common.action_chains import ActionChains
class  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.locatinotallow=[[-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)
        #提取鼠标创建的对象
        actinotallow=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")
#注意:两个模块的相互引用容易造成模块初始化错误
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.