【Mac + Python + Selenium】之获取验证码图片code并进行登录
自己新总结了一篇文章,对代码进行了优化,另外附加了静态图片提取文字方法,两篇文章可以结合着看:《【Python】Selenium自动化测试之动态识别验证码图片方法(附静态图片文字获取)》
初稿代码,可以忽略不计(自己留着看)
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2020/01/15 13:27 # @Author : zc # @File : 115test.py from selenium import webdriver from selenium.webdriver.common.by import By from time import sleep from PIL import Image,ImageEnhance import pytesseract imgPath1 = "/Users/zhangc/zh/111/test/img/识别失败图片.png" imgPath = "/Users/zhangc/zh/111/test/img/img.png" driver = webdriver.Chrome("/Users/zhangc/zh/111/test/chromedriver") def remove(string): return string.replace(" ","") def open(code): url = 'http://192.168.2.213:9100' driver.get(url) driver.maximize_window() login(code) def login(code): if (code == "") | (len(remove(code)) != 4): getCodeImg() else: # 输入用户名 telephone = driver.find_element(By.NAME,"telephone") telephone.clear() telephone.send_keys("13642040631") # 输入密码 password = driver.find_element(By.NAME,"password") password.clear() password.send_keys("123456") # 输入验证码 code_loc = driver.find_element(By.NAME,"code") code_loc.clear() code_loc.send_keys(code) # 点击登录按钮 button = driver.find_element(By.XPATH,"//button[@type='button']") button.click() try: # 后台获取验证码校验内容 text1 = driver.find_element(By.CSS_SELECTOR, ".el-message__content").text print(text1) while text1 == "": button.click() text1 # 前台获取验证码校验内容 text2 = driver.find_element(By.CSS_SELECTOR, ".el-el-form-item__error").text print(text2) if text1 == "验证码不正确": print("验证码不正确") driver.find_element(By.XPATH, "//div[@class='divIdentifyingCode']/img").click() getCodeImg() code2 = test() login(code2) elif text2 == "请输入正确的验证码": print("请输入正确的验证码") driver.find_element(By.XPATH, "//div[@class='divIdentifyingCode']/img").click() getCodeImg() code2 = test() login(code2) else: print("登陆成功!") except: print("进入首页!!!!") pass def getCodeImg(): '''获取code图片''' size2 = driver.get_window_size() print("页面的总size:"+str(size2)) img_el = driver.find_element(By.XPATH,"//div[@class='divIdentifyingCode']/img") # 获取图片的坐标 location = img_el.location print("页面坐标点的size:"+str(location)) # 获取图片的大小 size = img_el.size print("验证码的size:"+str(size)) left = location['x']/size2['width'] top = location['y']/size2['height'] right = (location['x'] + size['width'])/size2['width'] bottom = (location['y'] + size['height'])/size2['height'] print(left,top,right,bottom) # 截图操作 driver.get_screenshot_as_file(imgPath) imgSize = Image.open(imgPath).size print("截图的size:"+str(imgSize)) sleep(2) img = Image.open(imgPath).crop((left*imgSize[0], top*imgSize[1]+100, right*imgSize[0]+20, bottom*imgSize[1]+150)) img = img.convert('L') #转换模式:L | RGB img = ImageEnhance.Contrast(img)#增强对比度 img = img.enhance(2.0) #增加饱和度 img.save(imgPath) def test(): img2 = Image.open(imgPath) code = pytesseract.image_to_string(img2).strip() print("=============输出的验证码为:"+remove(code)) while (code == "") | (len(remove(code)) != 4): # 重新获取验证码 driver.find_element(By.XPATH, "//div[@class='divIdentifyingCode']/img").click() getCodeImg() img2 = Image.open(imgPath) code = pytesseract.image_to_string(img2).strip() if code == "": print("code获取为空值=================") continue elif len(remove(code)) != 4: print("code获取不是4位数字=============") continue else: print("识别成功!") # break break print("=============输出的验证码为:" + remove(code)) return remove(code) if __name__ == '__main__': code = "" open(code) code1 = test() login(code1) # 页面的总size:{'width': 1206, 'height': 1129} # 页面坐标点的size:{'x': 961, 'y': 518} # 验证码的size:{'height': 28, 'width': 70} # 0.796849087893864 0.4588131089459699 0.8548922056384743 0.48361381753764393 # 截图的size:(2412, 1950)
====================下面看正文=========================
之前有个问题一直在困扰着我,如何获取验证码的值
后来查询大神们的文章得知:
用pytesseract库来读取图片的文字。
先安装两个库:
# python有着更加优雅的方式调用系统的tesseract工具,首先安装pytesseract模块;pytesseract是对tesseract的封装,要和PIL联合使用 # 安装pytesseract sudo pip install pytesseract # 安装图片处理器PIL pip install pillow
好了接下来看看我是怎么一步一步实现的吧
第一步、保存验证码图片
核心代码:
getCodeImg()方法
import pytesseract from selenium import webdriver from selenium.webdriver.common.by import By from time import sleep from PIL import Image,ImageEnhance class LoginCode(object): # 用户名框元素 telephone_loc = (By.NAME,"telephone") # 密码框元素 password_loc = (By.NAME,"password") # 验证码框元素 code_loc = (By.NAME,"code") # 登录按钮元素 button_loc = (By.XPATH,"//button[@type='button']") # 验证码图片元素 img_el_loc = (By.XPATH, "//div[@class='divIdentifyingCode']/img") # 验证码错误提示元素 codeErrMsg_loc = (By.CSS_SELECTOR, ".el-message__content") imgPath = "/Users/zhangc/zh/111/test/img/img.png" driver = webdriver.Chrome("/Users/zhangc/zh/111/test/chromedriver") def find_element(self,*loc): # 实验得知方法中的参数loc↑加上*,定位会更稳定 '''定位单个元素''' return self.driver.find_element(*loc)
# 核心代码↓ def getCodeImg(self): '''获取验证码图片''' # 验证码图片的元素定位 img_el = self.find_element(*self.img_el_loc) # 获取整个浏览器的size chromeSize = self.driver.get_window_size() print("页面的总size:"+str(chromeSize))
# 页面的总size:{'width': 1202, 'height': 1129} # 获取图片的坐标 self.location = img_el.location print("页面坐标点的size:"+str(self.location))
# 页面坐标点的size:{'x': 959, 'y': 518} x:指的图片左边距;y:指的图片上边距
# 获取图片的大小 imgSize = img_el.size print("验证码的size:"+str(imgSize))
# 验证码的size:{'height': 28, 'width': 70}
# 左边距占整个浏览器的百分比
left = self.location['x']/chromeSize['width']
# 上边距占整个浏览器的百分比 top = self.location['y']/chromeSize['height']
# 右边距占整个浏览器的百分比 right = (self.location['x'] + imgSize['width'])/chromeSize['width']
# 下边距占整个浏览器的百分比 bottom = (self.location['y'] + imgSize['height'])/chromeSize['height']
print(left,top,right,bottom)
# 0.7978369384359401 0.4588131089459699 0.8560732113144759 0.48361381753764393 # 浏览器截屏操作 self.driver.get_screenshot_as_file(self.imgPath) screenshotImgSize = Image.open(self.imgPath).size print("截图的size:"+str(screenshotImgSize))
# 截图的size:(2404, 1950) 宽:2404,高:1950 sleep(2) # 从文件读取截图,截取验证码位置再次保存 img = Image.open(self.imgPath).crop(( # left*screenshotImgSize[0], # top*screenshotImgSize[1]+100, # right*screenshotImgSize[0]+20, # bottom*screenshotImgSize[1]+150
# 左边距百分比*截图的高≈截图左边距,再加上微调的距离+350
left * screenshotImgSize[1]+350,
# 上边距百分比*截图的高≈截图左边距,再加上微调的距离-100 top * screenshotImgSize[0]-100,
# 右边距百分比*截图的宽≈截图上边距,再加上微调的距离+400 right * screenshotImgSize[1]+400,
# 下边距百分比*截图的高≈截图右边距,再加上微调的距离-50 bottom * screenshotImgSize[0]-50 )) img = img.convert('L') # 转换模式:L | RGB img = ImageEnhance.Contrast(img) # 增强对比度 img = img.enhance(2.0) # 增加饱和度 img.save(self.imgPath) # 再次保存图片
获取页面中验证码图片的截图:
第二步、循环获取验证码截图的code
为什么要循环获取呢,一次获取不就可以了吗,原因是图片识别文字不准确,经常会识别为空或者不是4位数的值。
所以要循环做判断,下面上代码:
核心方法是:
①getCode():先获取图片code值
②loopGetCode():如果获取的code值不符合条件,循环判断获取正确的图片code值
import pytesseract from selenium import webdriver from selenium.webdriver.common.by import By from time import sleep from PIL import Image,ImageEnhance class LoginCode(object): # 用户名框元素 telephone_loc = (By.NAME,"telephone") # 密码框元素 password_loc = (By.NAME,"password") # 验证码框元素 code_loc = (By.NAME,"code") # 登录按钮元素 button_loc = (By.XPATH,"//button[@type='button']") # 验证码图片元素 img_el_loc = (By.XPATH, "//div[@class='divIdentifyingCode']/img") # 验证码错误提示元素 codeErrMsg_loc = (By.CSS_SELECTOR, ".el-message__content") imgPath = "/Users/zhangc/zh/111/test/img/img.png" driver = webdriver.Chrome("/Users/zhangc/zh/111/test/chromedriver") def remove(self,string): '''字符串去除空格''' return string.replace(" ","") def find_element(self,*loc): # 实验得知方法中的参数loc↑加上*,定位会更稳定 '''定位单个元素''' return self.driver.find_element(*loc)
# 核心代码↓ def getCode(self): '''获取图片的code''' # 再次读取识别验证码 img = Image.open(self.imgPath) code = pytesseract.image_to_string(img).strip() print("=============输出的验证码为:" + self.remove(code)) return code
# 核心代码↓ def loopGetCode(self): '''循环判断获取正确的图片code''' code = self.remove(self.getCode()) # 循环前获取code字数 codeNumBf = len(code) # 如果获取图片的code值为空或者不满足4位数进行循环 while (code == "") | (codeNumBf != 4): # 重新获取验证码 self.find_element(*self.img_el_loc).click() self.getCodeImg() # 获取验证码图片 code = self.remove(self.getCode()) # 循环后获取code字数 codeNumAf = len(code) if code == "": print("code获取为空值=================") continue elif codeNumAf != 4: print("code获取不是4位数字=============") continue else: print("识别成功!") # 识别成功退出循环 break print("=============输出的验证码为:" + code) # 输出满足条件的code return code
三、进行登录操作并判断验证码正确性
为什么登陆操作时还要判断验证码正确性呢,因为还是图片识别文字不准确,虽然识别出4位数但是并不是验证码图片中的值,所以需要再次判断,如果验证码错误就重新获取验证码的值。
核心代码:
login():登录操作
import pytesseract from selenium import webdriver from selenium.webdriver.common.by import By from time import sleep from PIL import Image,ImageEnhance class LoginCode(object): # 用户名框元素 telephone_loc = (By.NAME,"telephone") # 密码框元素 password_loc = (By.NAME,"password") # 验证码框元素 code_loc = (By.NAME,"code") # 登录按钮元素 button_loc = (By.XPATH,"//button[@type='button']") # 验证码图片元素 img_el_loc = (By.XPATH, "//div[@class='divIdentifyingCode']/img") # 验证码错误提示元素 codeErrMsg_loc = (By.CSS_SELECTOR, ".el-message__content") imgPath = "/Users/zhangc/zh/111/test/img/img.png" driver = webdriver.Chrome("/Users/zhangc/zh/111/test/chromedriver") def open(self): '''打开浏览器''' url = 'http://192.168.2.213:9100' self.driver.get(url) self.driver.maximize_window() self.driver.implicitly_wait(10) # 获取验证码图片 self.getCodeImg() # 核心代码↓ def login(self,code): '''进行登录操作''' # 输入用户名 telephone = self.find_element(*self.telephone_loc) telephone.clear() telephone.send_keys("13642040631") # 输入密码 password = self.find_element(*self.password_loc) password.clear() password.send_keys("123456") # 输入验证码 code_loc = self.find_element(*self.code_loc) code_loc.clear() code_loc.send_keys(code) # 点击登录按钮 button = self.find_element(*self.button_loc) button.click() sleep(0.5) try: # 后台获取验证码校验内容 codeErrMsg = self.find_element(*self.codeErrMsg_loc).text print("打印后台:"+codeErrMsg) if codeErrMsg == "验证码不正确": print("验证码不正确111") self.find_element(*self.img_el_loc).click() self.getCodeImg() self.loopGetCode_action() else: print("登陆成功!") except: print("进入首页!!!!") pass # 核心相关代码↓ def loopGetCode_action(self): '''循环验证code的正确性''' resultCode = self.loopGetCode() self.login(resultCode) # 核心相关代码↓ def login_action(self): '''执行验证码登录操作''' # 打开浏览器并获取验证码图片 self.open() self.loopGetCode_action()
以上三步就是完整获取图片文字的方法了,虽然有点多但是都是必不可少的步骤。
==========================================================
注:需要安装tesseract,不然会提示:
# CHANGE THIS IF TESSERACT IS NOT IN YOUR PATH, OR IS NAMED DIFFERENTLY
pytesseract.pytesseract.TesseractNotFoundError: tesseract is not installed or it's not in your path
未找到tesseract的路径,需要进入pytesseract.py文件
# Mac安装tesseract brew install --with-training-tools tesseract # 查看版本 tesseract -v tesseract 4.0.0 leptonica-1.77.0 libgif 5.1.4 : libjpeg 9c : libpng 1.6.36 : libtiff 4.0.10 : zlib 1.2.11 : libwebp 1.0.2 : libopenjp2 2.3.0 Found AVX2 Found AVX Found SSE # 查看安装路径 which tesseract /usr/local/bin/tesseract
==========================================================
下面上完整代码:
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2020/01/15 13:27 # @Author : zc # @File : 115test.py import pytesseract from selenium import webdriver from selenium.webdriver.common.by import By from time import sleep from PIL import Image,ImageEnhance class LoginCode(object): # 用户名框元素 telephone_loc = (By.NAME,"telephone") # 密码框元素 password_loc = (By.NAME,"password") # 验证码框元素 code_loc = (By.NAME,"code") # 登录按钮元素 button_loc = (By.XPATH,"//button[@type='button']") # 验证码图片元素 img_el_loc = (By.XPATH, "//div[@class='divIdentifyingCode']/img") # 验证码错误提示元素 codeErrMsg_loc = (By.CSS_SELECTOR, ".el-message__content") imgPath = "/Users/zhangc/zh/111/test/img/img.png" driver = webdriver.Chrome("/Users/zhangc/zh/111/test/chromedriver") def remove(self,string): '''字符串去除空格''' return string.replace(" ","") def find_element(self,*loc): # 实验得知方法中的参数loc↑加上*,定位会更稳定 '''定位单个元素''' return self.driver.find_element(*loc) def open(self): '''打开浏览器''' url = 'http://192.168.2.213:9100' self.driver.get(url) self.driver.maximize_window() self.driver.implicitly_wait(10) # 获取验证码图片 self.getCodeImg() def getCodeImg(self): '''获取验证码图片''' # 验证码图片的元素定位 img_el = self.find_element(*self.img_el_loc) # 获取整个浏览器的size chromeSize = self.driver.get_window_size() print("页面的总size:"+str(chromeSize)) # 页面的总size:{'width': 1202, 'height': 1129} # 获取图片的坐标 self.location = img_el.location print("页面坐标点的size:"+str(self.location)) # 页面坐标点的size:{'x': 959, 'y': 518} x:指的图片左边距;y:指的图片上边距 # 获取图片的大小 imgSize = img_el.size print("验证码的size:"+str(imgSize)) # 验证码的size:{'height': 28, 'width': 70} # 左边距占整个浏览器的百分比 left = self.location['x']/chromeSize['width'] # 上边距占整个浏览器的百分比 top = self.location['y']/chromeSize['height'] # 右边距占整个浏览器的百分比 right = (self.location['x'] + imgSize['width'])/chromeSize['width'] # 下边距占整个浏览器的百分比 bottom = (self.location['y'] + imgSize['height'])/chromeSize['height'] print(left,top,right,bottom) # 0.7978369384359401 0.4588131089459699 0.8560732113144759 0.48361381753764393 # 浏览器截屏操作 self.driver.get_screenshot_as_file(self.imgPath) screenshotImgSize = Image.open(self.imgPath).size print("截图的size:"+str(screenshotImgSize)) # 截图的size:(2404, 1950) 宽:2404,高:1950 sleep(2) # 从文件读取截图,截取验证码位置再次保存 img = Image.open(self.imgPath).crop(( # left*screenshotImgSize[0], # top*screenshotImgSize[1]+100, # right*screenshotImgSize[0]+20, # bottom*screenshotImgSize[1]+150 # 左边距百分比*截图的高≈截图左边距,再加上微调的距离+350 left * screenshotImgSize[1]+350, # 上边距百分比*截图的宽≈截图上边距,再加上微调的距离-100 top * screenshotImgSize[0]-100, # 右边距百分比*截图的高≈截图右边距,再加上微调的距离+400 right * screenshotImgSize[1]+400, # 上边距百分比*截图的宽≈截图下边距,再加上微调的距离-50 bottom * screenshotImgSize[0]-50 )) img = img.convert('L') # 转换模式:L | RGB img = ImageEnhance.Contrast(img) # 增强对比度 img = img.enhance(2.0) # 增加饱和度 img.save(self.imgPath) # 再次保存图片 def getCode(self): '''获取图片的code''' # 再次读取识别验证码 img = Image.open(self.imgPath) code = pytesseract.image_to_string(img).strip() print("=============输出的验证码为:" + self.remove(code)) return code def loopGetCode(self): '''循环判断获取正确的图片code''' code = self.remove(self.getCode()) # 循环前获取code字数 codeNumBf = len(code) # 如果获取图片的code值为空或者不满足4位数进行循环 while (code == "") | (codeNumBf != 4): # 重新获取验证码 self.find_element(*self.img_el_loc).click() self.getCodeImg() # 获取验证码图片 code = self.remove(self.getCode()) # 循环后获取code字数 codeNumAf = len(code) if code == "": print("code获取为空值=================") continue elif codeNumAf != 4: print("code获取不是4位数字=============") continue else: print("识别成功!") # 识别成功退出循环 break print("=============输出的验证码为:" + code) # 输出满足条件的code return code def login(self,code): '''进行登录操作''' # 输入用户名 telephone = self.find_element(*self.telephone_loc) telephone.clear() telephone.send_keys("13642040631") # 输入密码 password = self.find_element(*self.password_loc) password.clear() password.send_keys("123456") # 输入验证码 code_loc = self.find_element(*self.code_loc) code_loc.clear() code_loc.send_keys(code) # 点击登录按钮 button = self.find_element(*self.button_loc) button.click() sleep(0.5) try: # 后台获取验证码校验内容 codeErrMsg = self.find_element(*self.codeErrMsg_loc).text print("打印后台:"+codeErrMsg) if codeErrMsg == "验证码不正确": print("验证码不正确111") self.find_element(*self.img_el_loc).click() self.getCodeImg() self.loopGetCode_action() else: print("登陆成功!") except: print("进入首页!!!!") pass def loopGetCode_action(self): '''循环验证code的正确性''' resultCode = self.loopGetCode() self.login(resultCode) def login_action(self): '''执行验证码登录操作''' # 打开浏览器并获取验证码图片 self.open() self.loopGetCode_action() if __name__ == '__main__': LoginCode().login_action()
四、附录
感谢下面的参考文章:
①感谢作者:薛定谔的DBA的《Python selenium自动化识别验证码模拟登录操作(二)》
②感谢作者:大王大大王的《python -使用pytesseract识别验证码中遇到的问题》
③感谢作者:潇雨危栏的《Mac上tesseract-OCR的安装配置》
④感谢作者:anno_ym雨 的《【Python】关于键盘键入值、str的与或非问题?【报错:TypeError: unsupported operand type(s) for |: 'str' and 'str'】》
⑤感谢作者:github_39655029的《Python中的条件判断、循环以及循环的终止》
⑥感谢作者:青灯夜游的《Python如何删除字符串中所有空格》
⑦感谢作者:silencement的《python如何跳出while循环》
⑧感谢作者:d0main的《Python读取图片尺寸、图片格式》
⑨感谢作者:奔跑的豆子_的《Python-获取图片的大小》
🔟感谢作者:地空神一 的《python selenium UI自动化解决验证码的4种方法》