【Python】Selenium自動化測試之動態識別驗證碼圖片方法(附靜態圖片文字獲取)


目錄

  

一、前提  返回目錄

經常會遇到登錄系統時候需要輸入動態驗證碼的情況,但是自動化如何識別圖片然后登陸系統

需要用到pytesseract識別驗證碼圖片以及PIL圖像處理方法

import pytesseract
from PIL import Image, ImageEnhance

二、獲取驗證碼  返回目錄

1、思路

  • 步驟①:定位圖片的元素,並且截取當前瀏覽器的頁面圖片
  • 步驟②:獲取驗證碼坐標點,以及驗證碼圖片、瀏覽器、截圖的長和寬
  • 步驟③:截取截圖里的驗證碼圖片,獲得的驗證碼圖片並保存
  • 步驟④:獲得驗證碼code

2、實踐方法

以下步驟都在getCodeImg方法里面:

    def getCodeImg(self):
        """獲取驗證碼"""

1)步驟①

imgPath:瀏覽器截圖圖片路徑

savePath:保存驗證碼圖片路徑

加了一個點擊驗證碼圖片方法,目的是為了后面重新獲取驗證碼用的。

        # 步驟①:
        basePath = Fun().upPath() + "/utils/img/"
        imgPath = basePath + "code.png"
        savePath = basePath + "saveCode.png"
        # 定位圖片元素
        imgElement = self.webDriverWait(Loc.codeImg_loc)
        # 點擊驗證碼圖片
        imgElement.click()
        # 截取當前頁面的圖並放到目錄里
        self.driver.save_screenshot(imgPath)

2)步驟②

獲取驗證碼坐標是為了下面計算驗證碼占據整個瀏覽器的百分比。

        # 步驟②:
        # 獲取驗證碼x,y軸,x&y代表左上角的坐標點
        imgLocation = imgElement.location
        print(f"圖片坐標點:{imgLocation}")
        # 獲取驗證碼長、寬
        imgSize = imgElement.size
        print(f"圖片長、寬:{imgSize}")
        # 獲取瀏覽器的長、寬
        windowSize = self.driver.get_window_size()
        print(f"瀏覽器長、寬:{windowSize}")
        # 打開截圖
        openImg = Image.open(imgPath)
        # 獲取保存截圖的長、寬(寬:2700, 高:1950)
        screenImgSize = openImg.size
        print(f"保存截圖的長、寬:{screenImgSize}")

 

3)步驟③

計算圖片四個邊距在真實瀏覽器的百分比,用這個百分比乘以瀏覽器截圖的長和寬,得出截圖里面的驗證碼大概位置,然后再自己進行調整截圖里的邊距大小。

最后再把驗證碼圖片進行圖片處理,灰色度和增強對比度等等,提高獲取驗證碼圖片的識別率。

        # 步驟③:截取截圖的驗證碼圖片
        # 圖片左邊距占據整個瀏覽器的百分比
        left = imgLocation['x']/windowSize['width']
        # 圖片上邊距占據整個瀏覽器的百分比
        top = imgLocation['y']/windowSize['height']
        # 圖片右邊距占據整個瀏覽器的百分比
        right = (imgLocation['x'] + imgSize['width'])/windowSize['width']
        # 圖片下邊距占據整個瀏覽器的百分比
        bottom = (imgLocation['y'] + imgSize['height'])/windowSize['height']

        # 需要截取的坐標
        screenLocation = (
            left * screenImgSize[0],
            top * screenImgSize[1]+150,
            right * screenImgSize[0],
            bottom * screenImgSize[1]+150
        )
        # 打開截圖並截取區域並保存
        img = openImg.crop(screenLocation)
        img = img.convert('L')  # 轉換模式:L | RGB

     # 提高識別率(見下面
        # enhancer = ImageEnhance.Color(img)
        # enhancer = enhancer.enhance(0)
        # enhancer = ImageEnhance.Brightness(enhancer)
        # enhancer = enhancer.enhance(2)
        # enhancer = ImageEnhance.Contrast(enhancer)      # 增強對比度
        # enhancer = enhancer.enhance(8)
        # enhancer = ImageEnhance.Sharpness(enhancer)
        # img = enhancer.enhance(20)


        img = ImageEnhance.Contrast(img)  # 增強對比度
        img = img.enhance(2.0)
        img.save(savePath)

不過識別率還是比較低的,下面有種可以提高識別率的方法,但是見仁見智,實測有時很快有時很慢

參考文章:

pytesseract 識別率低提升方法

【python圖像處理】圖像的增強(ImageEnhance類詳解)

4)步驟④

獲取的驗證碼可能不是我們想要的,會出現中間有大小空格、換行情況,需要替換掉

    def remove(self,string):
        """字符串去除空格或換行"""

        str = string.replace(" ", "")  #大空格
        str = str.replace("", "")     #小空格
        str = str.replace('\n', "")    #換行符 return str


    def getCodeImg(self):
        """獲取驗證碼"""

        # 步驟④:獲得code驗證碼
        code = pytesseract.image_to_string(img).strip()
        print(f"提取的驗證碼為:【{self.remove(code)}】")
        return self.remove(code)

三、獲取4位驗證碼  返回目錄

1、思路

  • 因為實際效果我們要獲取4位驗證碼
  • 雖然上一步驟獲取到了驗證碼,但是還是會出現不足4位或者超過4位的驗證碼
  • 需要進行判斷篩選,只要4位的驗證碼

2、實踐方法

先判斷是否滿足4位,不滿足的話while循環重新獲取驗證碼,滿足4位跳出循環,並return出來

    def getCode(self):
        """獲取4位數驗證碼"""

        # 循環前獲取code字數
        code = self.getCodeImg()
        print(f"驗證碼位數:【{len(code)}】位")
        while len(code) != 4:
            # 重新獲取驗證碼
            code = self.getCodeImg()
           # print(f"驗證碼位數:【{len(code)}】位")
            # if len(code) != 4:
            #     print("驗證碼不是4位數!")
        print(f"輸出4位驗證碼為:{code}")
        return code

四、判斷驗證碼是否正確  返回目錄

1、思路

  • 雖然我們獲取了4位驗證碼,但是因為識別率的問題,獲取的驗證碼仍然不對,導致提示驗證碼錯誤

  •  我們還得重新獲取一遍驗證碼,直到獲取成功能夠登陸為止

2、實踐方法

判斷頁面如果有錯誤提示,則重新獲取4位驗證碼

    def checkCode(self):
        """判斷驗證碼是否正確"""

        try:
            errorMsg = self.get_text(Loc.errorMsg_loc)
            if errorMsg == "驗證碼錯誤":
                self.inputCodeAction()
        except:
            print("驗證碼正確,進入首頁!")

五、輸入驗證碼登錄  返回目錄

1、思路

  • 封裝一個Action方法,把之前的各種方法封裝在一起
  • 實現輸入驗證碼登錄
  • 此封裝方法只是在獲取驗證碼的類里,輸入用戶名和密碼的方法在另一個類里

2、實踐方法

    def inputCodeAction(self):
        """輸入驗證碼登錄"""

        code = self.getCode()                    # 獲取4位驗證碼
        self.el_clear_sendKeys(Loc.code_loc, code)        # 清空驗證碼輸入框並輸入驗證碼
        self.el_click(Loc.loginButton_loc)             # 點擊登錄按鈕
        self.checkCode()                        # 判斷驗證碼是否正確

六、登錄頁面類  返回目錄

登錄頁面類,封裝輸入用戶名、密碼、驗證碼、登錄的操作方法

class LoginPage(BasePage):
    """登錄頁面"""

    def login_action(self,username,password):
        """登錄操作"""
        self.el_sendKeys(Loc.username_loc, username)    # 輸入用戶名
        self.el_sendKeys(Loc.password_loc, password)    # 輸入密碼
        GetCode(self.driver).inputCodeAction()          # 輸入驗證碼並登錄

七、完整的獲取驗證碼類代碼  返回目錄

import pytesseract
from PIL import Image, ImageEnhance
from page_object.page.basePage import BasePage
from page_object.utils.functions import Functions as Fun
from page_object.locator.loginPageLoc import LoginPageLoc as Loc


class GetCode(BasePage):

    def remove(self,string):
        """字符串去除空格或換行"""

        str = string.replace(" ", "")
        str = str.replace("", "")
        str = str.replace('\n', "")
        return str

    def getCodeImg(self):
        """獲取驗證碼"""

        # 步驟①:
        basePath = Fun().upPath() + "/utils/img/"
        imgPath = basePath + "code.png"
        savePath = basePath + "saveCode.png"
        # 定位圖片元素
        imgElement = self.webDriverWait(Loc.codeImg_loc)
        # 點擊驗證碼圖片
        imgElement.click()
        # print(f"點擊【{next(iter(Fun()))}】次驗證碼圖片")
        # 截取當前頁面的圖並放到目錄里
        self.driver.save_screenshot(imgPath)

        # 步驟②:
        # 獲取驗證碼x,y軸,x&y代表左上角的坐標點
        imgLocation = imgElement.location
        print(f"圖片坐標點:{imgLocation}")
        # 獲取驗證碼長、寬
        imgSize = imgElement.size
        print(f"圖片長、寬:{imgSize}")
        # 獲取瀏覽器的長、寬
        windowSize = self.driver.get_window_size()
        print(f"瀏覽器長、寬:{windowSize}")
        # 打開截圖
        openImg = Image.open(imgPath)
        # 獲取保存截圖的長、寬(寬:2700, 高:1950)
        screenImgSize = openImg.size
        print(f"保存截圖的長、寬:{screenImgSize}")

        # 步驟③:截取截圖的驗證碼圖片
        # 圖片左邊距占據整個瀏覽器的百分比
        left = imgLocation['x']/windowSize['width']
        # 圖片上邊距占據整個瀏覽器的百分比
        top = imgLocation['y']/windowSize['height']
        # 圖片右邊距占據整個瀏覽器的百分比
        right = (imgLocation['x'] + imgSize['width'])/windowSize['width']
        # 圖片下邊距占據整個瀏覽器的百分比
        bottom = (imgLocation['y'] + imgSize['height'])/windowSize['height']

        # 需要截取的坐標
        screenLocation = (
            left * screenImgSize[0],
            top * screenImgSize[1]+150,
            right * screenImgSize[0],
            bottom * screenImgSize[1]+150
        )
        # 打開截圖並截取區域並保存
        img = openImg.crop(screenLocation)
        img = img.convert('L')  # 轉換模式:L | RGB

        # enhancer = ImageEnhance.Color(img)
        # enhancer = enhancer.enhance(0)
        # enhancer = ImageEnhance.Brightness(enhancer)
        # enhancer = enhancer.enhance(2)
        # enhancer = ImageEnhance.Contrast(enhancer)      # 增強對比度
        # enhancer = enhancer.enhance(8)
        # enhancer = ImageEnhance.Sharpness(enhancer)
        # img = enhancer.enhance(20)

        img = ImageEnhance.Contrast(img)  # 增強對比度
        img = img.enhance(2.0)
        img.save(savePath)

        # 步驟④:獲得code驗證碼
        code = pytesseract.image_to_string(img).strip()
        print(f"提取的驗證碼為:【{self.remove(code)}】")
        return self.remove(code)


    def getCode(self):
        """獲取4位數驗證碼"""

        # 循環前獲取code字數
        code = self.getCodeImg()
        print(f"驗證碼位數:【{len(code)}】位")
        while len(code) != 4:
            # 重新獲取驗證碼
            code = self.getCodeImg()
            print(f"驗證碼位數:【{len(code)}】位")
            if len(code) != 4:
                print("驗證碼不是4位數!")
        print(f"輸出4位驗證碼為:{code}")
        return code


    def checkCode(self):
        """判斷驗證碼是否正確"""

        try:
            errorMsg = self.get_text(Loc.errorMsg_loc)
            if errorMsg == "驗證碼錯誤":
                self.inputCodeAction()
        except:
            print("驗證碼正確,進入首頁!")


    def inputCodeAction(self):
        """輸入驗證碼登錄"""

        code = self.getCode()
        self.el_clear_sendKeys(Loc.code_loc, code)
        self.el_click(Loc.loginButton_loc)
        self.checkCode()

八、附錄:靜態圖片文字提取  返回目錄

1、思路:

  • 首先獲取圖片的src地址
  • 然后進行GET接口請求,獲取圖片的二進制數據
  • 再創建一個圖片文件,把二進制數據寫到圖片里
  • 最后讀取圖片,獲取驗證碼code

2、實踐方法

獲取圖片地址,有兩種情況,一種是通過解析頁面獲取src,一種是通過頁面元素獲取src

1)獲取src

①通過前端頁面代碼獲取src

這種方式比較簡單,直接通過前端頁面代碼獲取src

    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36'
    }
    # 打開登錄頁面地址
    url = 'https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx'
    # 獲取前端頁面代碼
    page_text = requests.get(url=url, headers=headers).text    
    # 解析字符串格式的HTML文檔對象
    tree = etree.HTML(page_text)
    # 解析出頁面中圖片的地址
    cod_img_src ='https://so.gushiwen.cn' + tree.xpath('//*[@id="imgCode"]/@src')[0]

注:etree.HTML()可以用來解析字符串格式的HTML文檔對象,將傳進去的字符串轉變成_Element對象。作為_Element對象,可以方便的使用getparent()、remove()、xpath()等方法。

②頁面元素獲取src

當獲取不到前端代碼時候,可以用Selenium自動化獲取元素屬性值

    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36'
    }
    # 打開瀏覽器
    driver = webdriver.Chrome()
    # 瀏覽器打開登錄地址
    driver.get("https://so.gushiwen.cn/user/login.aspx?from=http://so.gushiwen.cn/user/collect.aspx")
    # CSS定位元素,獲取src屬性值
    src = driver.find_element_by_css_selector("#imgCode").get_attribute("src")

2)獲取圖片二進制數據

cod_data = requests.get(url=src,headers=headers).content

打印結果:

b'GIF89a7\x00\x16\x00\xf7\x00\x00\x00\x00\x00\x00\x003\x00\x00f\x00\x00\x99\x00\x00\xcc\x00\x00\xff\x00+\x00\x00+3\x00+f\x00+\x99\x00+\xcc\x00+\xff\x00U\x00\x00U3\x00Uf\x00U\x99\x00U\xcc\x00U\xff\x00\x80\x00\x00\x803\x00\x80f\x00\x80\x99\x00\x80\xcc\x00\x80\xff\x00\xaa\x00\x00\xaa3\x00\xaaf\x00\xaa\x99\x00\xaa\xcc\x00\xaa\xff\x00\xd5\x00\x00\xd53\x00\xd5f\x00\xd5\x99\x00\xd5\xcc\x00\xd5\xff\x00\xff\x00\x00\xff3\x00\xfff\x00\xff\x99\x00\xff\xcc\x00\xff\xff3\x00\x003\x0033\x00f3\x00\x993\x00\xcc3\x00\xff3+\x003+33+f3+\x993+\xcc3+\xff3U\x003U33Uf3U\x993U\xcc3U\xff3\x80\x003\x8033\x80f3\x80\x993\x80\xcc3\x80\xff3\xaa\x003\xaa33\xaaf3\xaa\x993\xaa\xcc3\xaa\xff3\xd5\x003\xd533\xd5f3\xd5\x993\xd5\xcc3\xd5\xff3\xff\x003\xff33\xfff3\xff\x993\xff\xcc3\xff\xfff\x00\x00f\x003f\x00ff\x00\x99f\x00\xccf\x00\xfff+\x00f+3f+ff+\x99f+\xccf+\xfffU\x00fU3fUffU\x99fU\xccfU\xfff\x80\x00f\x803f\x80ff\x80\x99f\x80\xccf\x80\xfff\xaa\x00f\xaa3f\xaaff\xaa\x99f\xaa\xccf\xaa\xfff\xd5\x00f\xd53f\xd5ff\xd5\x99f\xd5\xccf\xd5\xfff\xff\x00f\xff3f\xffff\xff\x99f\xff\xccf\xff\xff\x99\x00\x00\x99\x003\x99\x00f\x99\x00\x99\x99\x00\xcc\x99\x00\xff\x99+\x00\x99+3\x99+f\x99+\x99\x99+\xcc\x99+\xff\x99U\x00\x99U3\x99Uf\x99U\x99\x99U\xcc\x99U\xff\x99\x80\x00\x99\x803\x99\x80f\x99\x80\x99\x99\x80\xcc\x99\x80\xff\x99\xaa\x00\x99\xaa3\x99\xaaf\x99\xaa\x99\x99\xaa\xcc\x99\xaa\xff\x99\xd5\x00\x99\xd53\x99\xd5f\x99\xd5\x99\x99\xd5\xcc\x99\xd5\xff\x99\xff\x00\x99\xff3\x99\xfff\x99\xff\x99\x99\xff\xcc\x99\xff\xff\xcc\x00\x00\xcc\x003\xcc\x00f\xcc\x00\x99\xcc\x00\xcc\xcc\x00\xff\xcc+\x00\xcc+3\xcc+f\xcc+\x99\xcc+\xcc\xcc+\xff\xccU\x00\xccU3\xccUf\xccU\x99\xccU\xcc\xccU\xff\xcc\x80\x00\xcc\x803\xcc\x80f\xcc\x80\x99\xcc\x80\xcc\xcc\x80\xff\xcc\xaa\x00\xcc\xaa3\xcc\xaaf\xcc\xaa\x99\xcc\xaa\xcc\xcc\xaa\xff\xcc\xd5\x00\xcc\xd53\xcc\xd5f\xcc\xd5\x99\xcc\xd5\xcc\xcc\xd5\xff\xcc\xff\x00\xcc\xff3\xcc\xfff\xcc\xff\x99\xcc\xff\xcc\xcc\xff\xff\xff\x00\x00\xff\x003\xff\x00f\xff\x00\x99\xff\x00\xcc\xff\x00\xff\xff+\x00\xff+3\xff+f\xff+\x99\xff+\xcc\xff+\xff\xffU\x00\xffU3\xffUf\xffU\x99\xffU\xcc\xffU\xff\xff\x80\x00\xff\x803\xff\x80f\xff\x80\x99\xff\x80\xcc\xff\x80\xff\xff\xaa\x00\xff\xaa3\xff\xaaf\xff\xaa\x99\xff\xaa\xcc\xff\xaa\xff\xff\xd5\x00\xff\xd53\xff\xd5f\xff\xd5\x99\xff\xd5\xcc\xff\xd5\xff\xff\xff\x00\xff\xff3\xff\xfff\xff\xff\x99\xff\xff\xcc\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\xf9\x04\x01\x00\x00\xfc\x00,\x00\x00\x00\x007\x00\x16\x00\x00\x08\xff\x00\x95\x11\x13Hp\xa0\xc1\x82\x08\x0f*L\xc8p\xa1\xc3}\x10#J\xfc$1\x93\xb2}\x99$\xee\xa3\x08\xd1\xa2\xc6\x8f 5\x12\xe3\xf8\xf1\x13\xb1\x8f\x03\xf7)\xcb\x08\xf1dD\x96!cJ\x14\x08\xd2%H\x8b\'W\xb6\x84x\xd1\xa6L\x8c\x175z\xd4H2d\xce\xa3\x1c]\xc2\x04\xb92\xe8G\x9a\x115a)\xb0i\x1f1P\xbe\x06\xb8x`\xb0\xd1\x80\x16-\xe0\x10K6P\x930\x16V\xd0\x02\xe9\xa8\xefO\x951\x9a \xea\xf3S\xa5\xaa\xc4\x91\x11\x93\xed\x1bD\xe0\x0eDw\x04\\\xec\x88\x06\xb1\x17X\xb0,\xe0\xc4%\xc6\xa8\x85\x15\xc7hYh\x1ak\xa5\x8a\x15Md\x93\xfd\xa1BI\'O\x97\x93\x95\xb9 PU\x9f\x0b\x17\x10"\xea\xc3\xd2"\xd4\xbeF-t\xa8\x94\xe4X\x8d\\@V\xba|j[y\x0c\xe1\xb6U\x96\t\x15\xa8\x17b\x16\x17F\x88aA\x1d\x13\xb6m}\x81X\xc4\xb1J\xfc\x0f\x0bJ\xfa\x00U\xd9^:\x15\x15\xbb\x11\xa1F\xff\xc4Bz\xb4\x8b\xc9\xa0\xee\xea\xbb\xd2b\xc6>M\xfa\x1ek\x1a\x96\xfe\xaa-+cx3\xaar%\x1ap\xe1w\xa5\'\x91 \x05\x04\x16\x98\x05-\r\xa3\x8c&\xca\xb8s\xd8)\xfb\xf4\xf2X}\x11\xf1b\x85\x0e\xc9h\xa7\x89[\xf0yW\xd5HGiD\x9e\x0b\x05h2\x9ak;\xed\xa3\xe0$\xed\x11\x93I\x0b\xfdAD\x962\xc2\xe0\xc7[(\xedX\xa6L\x15\x7fLvWq\x11\x1dW\x00a\x8e\xb8\xa0\x03aA1\xb8\x0f=\xec\xcdwEb61\xc6\x02\x19\xc9T\xd6Y!U\x88\xb1YU8\xa9\xe4\xd3>\xe4\xd9\x05\x98\x0bj\xe8\xe5\xa0\x0b\xd1\x103\x0cX\xa7\xacgE\x99\x0cfRe\x15\x9ad\xe2\x16\x1c\xc3\xe4\xe3\x16\x87>e"\xa0\x8a\xca\x1c\x07\x04A\x88\xb4@\x80&\x93\xb1\xe7W;`M\xd6\x0b\x0b\xd2\x11g\xc5\xa4\x98Y)\xd0.\xdb}w\x97x\x10\x91\x07GT\xa7\xfd\xa0\xcc0\xb0\x1d\x16\xd6X\x8c=\x99\xd6\xa4\x97\x8d\xfa\x87\x15v\r\x89\xb3\'\x8ax\xa5$\x11_q\x0cc\xd5>\xbe\xb4\xe0\x02\x84\xfbPr\x98]\xe9)\xf3\xe8\xaa9\xa8\xa6\xdd$\xa0\x0c\x14\xc9\x96\x80~\x16\x93S\xd3\xa2\xeaSA\xa0\xd07P\xb3-\x05\xb5`Dx\xfd\xa4\x11B\n2\x88\x19\x83_\xf2\xa4+D\x02\x1a4S\xba\x1aaF\x0c\x83\xc3\x9c+\xd1\xba\xe2\xde\xab\xd2P\x11)h\x94I\xf9R\x9b\xef>\xdc^\x84\xaf\x97&5\x05"A\xda\x8e\xda\xac@&5K/\xc4c\xd1\xdb\xec\xc2\nV|\xd5U\xe5\x12\x13\x10\x00;'

3)數據寫入到圖片中,並識別驗證碼

    # 讀取二進制數據寫入到圖片中
    with open('./code.jpg', 'wb') as fp:
        fp.write(cod_data)
    # 識別圖片中的文字
    text = pytesseract.image_to_string(Image.open(r'./code.jpg'))

3、總結

但是這種通過接口方式獲取的驗證碼並不是你想要的,因為每請求一次接口,圖片就會發生變化,所以只能適用於靜態不變的圖片比較合適。

 

參考文章:《Pytesseract的安裝與使用

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM