一、人工方式处理数字验证码
通过selenium打开浏览器获取验证码,然后进行人工输入,从而实现登录
访问的学习通网页链接:
xxt_link = r'https://passport2.chaoxing.com/'
使用selenium访问页面,输入用户名和密码:
from selenium import webdriver
driver=webdriver.Firefox()
driver.get(xxt_link) #请求链接页面
username="test"
password="123456"
driver.find_element_by_xpath('//*[@id="unameId"]').send_keys(username) #输入用户名
driver.find_element_by_xpath('//*[@id="passwordId"]').send_keys(password) #输入密码
selenium是可以打开浏览器的,即验证码直接可见,这里直接手动输入验证码:
element=driver.find_element_by_xpath('//*[@id="numcode"]') #获取验证码输入框元素
print("please input the captcha\n")
words=input(">>>")
element.send_keys(words) #输入验证码
#模拟点击:
driver.find_element_by_xpath('/html/body/div[1]/div[1]/form/table/tbody/tr[7]/td[2]/label/input').click()
二、OCR处理验证码:
ocr(Optical Character Recognition,光学字符识别),一种将图片提供的形状转化为计算机字符的方法,python通过模块pytesseract实现ocr。
pytesseract的安装:
pip install pytesseract --target "xxx\site-packages"
安装Tesseract-ocr:
直接下载exe格式文件
通过图片链接保存验证码(然,通过链接下载和网页通过的图片不一致):
在selenium打开的登录页面,通过get_attribute()方法获取验证码元素的链接,通过requests模块和文件读写操作保存验证码图片:
import requests
element=driver.find_element_by_xpath('//*[@id="numVerCode"]') #获取验证码图片链接
si_code_link=element.get_attribute("src")
r=requests.get(si_code_link) #链接请求
print(r.url)
print(r.stutas_code)
with open("code.jpg","wb") as f: #图片保存
f.write(r.content)
f.close()
保存的验证码图片code.jpg:
很苟的一点是,再次请求到的验证码图片虽说和页面内的是同一个链接,但是却是不同的图片(......)
通过模拟右键+键盘按键V下载页面验证码(失败拉):
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
#模拟页面图片保存:
element==driver.find_element_by_xpath('//*[@id="numVerCode"]') #定位验证码图片元素
action = ActionChains(driver).move_to_element(element) #移动到该元素
action.context_click(element) #右键点击该元素
action.send_keys(Keys.ARROW_DOWN) #点击键盘向下箭头
action.send_keys('v') #键盘输入V保存图
action.perform() #执行上述操作
行不通,连下载提示窗口(windows弹窗)都没有弹出,而且弹窗不属于页面内元素,selenium是操作不了的(ε=(´ο`*)))唉......)
利用pyautogui进行页面内验证码图片的保存(有戏):
pyautogui可以通过程序自动控制鼠标和键盘进行操作。
安装pyautogui:
pip install pyautogui
配合selenium进行页面验证码图片的保存:
from selenium.webdriver.common.action_chains import ActionChains
import pyautogui
import time
element==driver.find_element_by_xpath('//*[@id="numVerCode"]') #定位验证码图片元素
#模拟鼠标移动和右键点击:
action = ActionChains(driver).move_to_element(element) #移动到该元素
action.context_click(element) #右键点击该元素
action.perform() #执行上述操作
#利用pyautogui进行图片保存:
pyautogui.typewrite(['v']) # 敲击V进行保存
# 单击图片另存之后等1s敲回车
time.sleep(3)
pyautogui.typewrite(['enter'])
最后停留3秒才按回车,保存足够时间等待弹窗出现。
现在,保存的图片和当前页面内的验证码是一样的了
对保存图片进行降噪,去除干扰信息(转灰度图,二值化处理):
利用PIL模块将彩色的RGB图像转化为灰度图像,起到降噪作用:
安装PIL模块:
pip install PIL #安装
进行彩色图转灰度图:
from PIL import Image #导入模块
im=Image.open("code.jpg") #打开图片
gray_im=im.convert("L") #转化为灰度图
gray_im.show()
查看效果:
进行二值化处理:
灰度图只有一个通道,通道可以看做一个0~255的列表,0为黑色,255表示白色,随数字的变大,颜色由浅变深。二值化通过一个自定义的截断数值(灰度界限)将灰度通道变得只有0和1两个数字,即只有黑和白两种颜色。
这个方法对于一些带有干扰项背景的验证码有很好的降噪作用。
#自定义一个二值化通道:
cutoff=200 #设置一个截断距离,值越小,二值化通道里黑色比值越小
table=[]
for i in range(256):
if i < cutoff:
table.append(0)
else:
table.append(1)
#对灰度图转为二值化灰度图:
out=gray_im.point(table,"1")
out.show()
out.save("product.jpg")
处理效果:
利用pytesseract进行图片字识别,最后完成登录:
import pytesseract
from PIL import Image
th=Image.open("product.jpg") #打开二值化灰度图
strings=pytesseract.image_to_string(th).strip() #进行图片字符识别
print(strings)
将图片提取的文件填入到验证码框内,模拟点击登录:
#模拟输入验证码
driver.find_element_by_xpath('//*[@id="numcode"]').send_keys(strings)
#模拟点击登录
driver.find_element_by_xpath('//*[@id="form"]/table/tbody/tr[7]/td[2]/label/input').click()
避免重复保存出现覆盖提示的弹窗,在代码的末尾将保存位置的同名文件进行删除:
调用os模块:
import os
try:
os.remove(r'C:\***\***\Downloads\code.jpg')
except:
print("pass")
三、小小的调整
ε=(´ο`*)))唉, 这个pytesseract识别度不高啊(......)
将验证码下载、识别出字符串,验证码删除封装为一个函数test_code()
将输入账号、密码和验证码还有点击登录封装为一个函数login()
写个while循环,进行循环登录,直到完成登录为止。登录的标准是匹配到登录的个人主页特有的元素。设置一个变量login_state,初始状态下,login_state=True,当成功登录,匹配到独特的元素后,login_state=False,结束循环。
在while循环里先对验证码进行识别,直到识别出来的是4个数字再进行尝试登录。
对识别的字符进行判断和清洗:
对pytesseract识别的字符进行清洗(去掉英文字符和特殊字符,只保留数字):
update_strings=""
for i in strings: #strings是ocr识别的字符串
i_ = ord(i) #获得其十进制ascii码
if 48 <= i_ <= 57 :
update_strings += i
若是字符串为空或是长度不等于4就换一张识别码重新匹配:
if update_strings == "":
continue #识别的字符为空,就跳过本次循环
elif len(update_strings) != 4:
continue
终于有一次成功识别到4个都是数字的验证码字符串后,进行登录:
login(user,pwd,strings)
try-except结构,尝试识别个人主页特有的元素,没出错就是成功登录,要是出错,重新给我去识别验证码:
若是识别到个人主页的特有元素就结束循环,否则刷新验证码,继续识别和尝试登录
try :
words=driver.find_element_by_xpath('/html/body/div[2]/div[1]/div[1]/div/div/a').text
if words=="账号管理"
login_state=False
print("成功登陆")
except:
driver.find_element_by_xpath('/html/body/div[1]/div[1]/form/t \
able/tbody/tr[5]/td[3]/a').click()
time.sleep(1)
print("尝试再次登录")
成功的完整代码:
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import pyautogui
import time
from PIL import Image
import pytesseract
import os
def test_code():
#保存验证码:
element=driver.find_element_by_xpath('//*[@id="numVerCode"]')
action = ActionChains(driver).move_to_element(element)#移动到该元素
action.context_click(element)#右键点击该元素
action.perform()#执行
#利用pyautogui进行图片保存:
pyautogui.typewrite(['v']) # 敲击V进行保存
# 单击图片另存之后等2s敲回车
time.sleep(3)
pyautogui.typewrite(['enter'])
time.sleep(3)
#图片转灰度图:
im=Image.open(r"C:\Users\TSPC\Downloads\code.jpg")
gray_im=im.convert("L")
gray_im.save("product.jpg")
th=Image.open("product.jpg")
strings=pytesseract.image_to_string(th)
strings=strings.strip()
print("识别的字符为>>>",strings)
#识别出字符后删掉图片:
try:
os.remove(r'C:\Users\TSPC\Downloads\code.jpg')
except:
print("pass")
try:
os.remove(r"C:\Users\TSPC\Desktop\product.jpg")
except:
print("pass")
#老是匹配到非数字,这里保证只有数字:
updates_strings=""
for i in strings:
i_=ord(i)
if 48<=i_<=57:
updates_strings+=str(i)
print("清洗后的字符>>>",updates_strings)
return updates_strings
def login(username,passwords,strings):
#模拟用户密码输入:
user_input=driver.find_element_by_xpath('//*[@id="unameId"]')
pwd_input=driver.find_element_by_xpath('//*[@id="passwordId"]')
user_input.clear()
user_input.send_keys(username)
pwd_input.clear()
pwd_input.send_keys(passwords)
#模拟输入验证码:
driver.find_element_by_xpath('//*[@id="numcode"]').clear()
driver.find_element_by_xpath('//*[@id="numcode"]').send_keys(strings)
#模拟点击登录
driver.find_element_by_xpath('//*[@id="form"]/table/tbody/tr[7]/td[2]/label/input').click()
time.sleep(3)
if __name__ == "__main__":
xxt_link = r'https://passport2.chaoxing.com/'
driver=webdriver.Firefox()
driver.get(xxt_link)
driver.set_window_size(500,650)
username="test"
passwords="123456"
login_state=True
while login_state:
strings=test_code() #获取验证码数字
if strings == "":
driver.find_element_by_xpath('/html/body/div[1]/div[1]/form/table/tbody/tr[5]/td[3]/a').click()
time.sleep(1)
print("识别错误,尝试再次识别")
continue
elif len(strings) != 4:
driver.find_element_by_xpath('/html/body/div[1]/div[1]/form/table/tbody/tr[5]/td[3]/a').click()
time.sleep(1)
print("识别错误,尝试再次识别")
continue
login(username,passwords,strings) #进行登录
try :
words=driver.find_element_by_xpath('/html/body/div[2]/div[1]/div[1]/div/div/a').text
if words=="账号管理":
login_state=False
print("成功登陆")
except:
driver.find_element_by_xpath('/html/body/div[1]/div[1]/form/table/tbody/tr[5]/td[3]/a').click()
time.sleep(1)
print("尝试再次登录")
循环几次后登录进学习通:
参考文献:
Python网络爬虫从入门到实践 编者:唐松