本文使用到的主要第三方库:selenium
本文主要使用第三方API:腾讯通用文字识别API
由于最近出于出成绩时期,每天都会查一下成绩,学校的教务系统cookie不操作一段时间会过期,又要重新登入.需要输入验证码.就感觉挺麻烦的,所以就搞了个自动查询吧.
待解决问题:
如何访问学校教务系统网站,获取数据.一开始我是想采用http协议get,post直接获取html数据的…不过实在有点懒了,还是直接用之前就用过的selenium库吧.
验证码问题:验证码的问题,我前前后后想了好几种解决策略吧,首先是尝试了COOKIE,想试试免登入,不过失败了.然后就是尝试绕过验证码,事实证明,虽然教务系统的服务器是土豆,不过也没那么好破解.第三条路就是图像识别了.关于图像识别我尝试了两种方案:
首先是pytesseract.我的理解就是一个图像识别引擎,不过直接用的识别率真的太菜了.然后我尝试对学校验证码降噪,一开始是二值化后,去掉细线.不过虽然学校验证码看着挺草率的两条线,搞起来挺要命的,有粗有细.不太好根据线的宽度进行降噪.我又想到了根据颜色来降噪.因为线是单独的一种颜色,取色器取色,再遍历像素,如果是线的颜色,就改为背景色.即使这样,识别度还是不高.在我以为要失败的时候.
想起了QQ截图自带的文字识别功能,一试…流批!没处理过的验证码都识别出来了,然后我就立刻去找有没有开放API,还真有.那验证码的问题也解决了.
首先是对网站进行操作的相关源码
from selenium import webdriver
from tencentapi import api
from selenium.webdriver.support.ui import Select
#隐藏了浏览器窗口,以及selenium的日志
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('headless')
chrome_options.add_experimental_option('excludeSwitches', ['enable-logging'])
driver = webdriver.Chrome(chrome_options=chrome_options)
driver.get("https://web.jnu.edu.cn/") # 地址栏里输入网址
driver.find_element_by_id('user_login').send_keys("")#账号
driver.find_element_by_id('user_password').send_keys('')#密码
driver.find_element_by_name('commit').click()
Flag=True
#验证码识别率不是百分百,不成功就再试一次
while(Flag):
try:
driver.get("https://jwxt-443.web.jnu.edu.cn/") # 地址栏里输入网址
driver.find_element_by_id('txtYHBS').send_keys("")#账号
driver.find_element_by_id('txtYHMM').send_keys('')#密码
img = driver.find_element_by_xpath('/html/body/form/table/tbody/tr/td/table/tbody/tr/td/table/tbody/tr[2]/td[2]/table/tbody/tr[9]/td[3]/img')
#我只能说,这行代码真的很厉害
base64_data=img.screenshot_as_base64
FJM = api(base64_data)
driver.find_element_by_id('txtFJM').send_keys(FJM)
driver.find_element_by_id('btnLogin').click()
driver.get('https://jwxt-443.web.jnu.edu.cn/Secure/Cjgl/Cjgl_Cjcx_XsCxXqCj.aspx')
except:
pass
else:
break
# driver.get('https://jwxt-443.web.jnu.edu.cn/Secure/Cjgl/Cjgl_Cjcx_XsCxXqCj.aspx')
s1 = Select(driver.find_element_by_id('ddListXQ'))
s1.select_by_value('下')
driver.find_element_by_id('lbtnQuery').click()
print(driver.find_element_by_id('dgrdList').text)
在中间其实会遇到一个问题,我觉得挺棘手的,就是验证码的获取问题.由于验证码的src并不是静态的,如果重新通过src获取只会获取到新的验证码图片,网上的方法都是采用截图的方法,我看网上的方法感觉很复杂,很绝望.然后随手对img元素打了个.结果索引出了screenshot方法,更流批的是,还可以as_base64,我人都傻了,只能喊句selenium流批!(后面API的调用,使用的就是图片的base64编码)
关于腾讯API的调用源码
# with open("ValidateCode.png","rb") as f:
# # b64encode是编码,b64decode是解码
# base64_data = base64.b64encode(f.read())
# base64_data = str(base64_data,'utf-8')
# # print(base64_data)
import json
from tencentcloud.common import credential
from tencentcloud.common.profile.client_profile import ClientProfile
from tencentcloud.common.profile.http_profile import HttpProfile
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
from tencentcloud.ocr.v20181119 import ocr_client, models
def api(base64_data):
base64_data = base64_data
try:
cred = credential.Credential("这里是secretid", "这里是key")
httpProfile = HttpProfile()
httpProfile.endpoint = "ocr.tencentcloudapi.com"
clientProfile = ClientProfile()
clientProfile.httpProfile = httpProfile
client = ocr_client.OcrClient(cred, "ap-guangzhou", clientProfile)
req = models.EnglishOCRRequest()
params = '{\"ImageBase64\":\"'+base64_data+'\"}'
req.from_json_string(params)
resp = client.EnglishOCR(req)
# print(resp.to_json_string())
json1 = json.loads(resp.to_json_string())
FJM = json1['TextDetections'][0]['DetectedText']
# print(FJM)
except TencentCloudSDKException as err:
print(err)
return FJM
这个代码就没啥特别的了…不是简单,我也不会写,这是API的demo,原样复制…我只是加了个json操作方便读取识别结果,然后封装成函数方便调用.
腾讯文字识别API文档
登入账号获取id和key后要在这里实名注册,开通服务,每个月免费1000次识别,理论上够用
感慨:
下面是失败了的pytesseract尝试,也不算完全没用吧,降噪处理的话,说不定可以提高一下API的识别正确率
#coding = utf -8
from PIL import Image
import pytesseract
import cv2
import numpy as np
import matplotlib.pyplot as plt
import math
from PIL import Image,ImageEnhance,ImageFilter
from PIL import Image
color=[(255,0,0),(211,211,211)]
img = Image.open("ValidateCode.png")#读取系统的内照片
# print (img.size)#打印图片大小
# print (img.getpixel((4,4)))
width = img.size[0]#长度
height = img.size[1]#宽度
for i in range(0,width):#遍历所有长度的点
for j in range(0,height):#遍历所有宽度的点
data = (img.getpixel((i,j)))#打印该图片的所有点
# print (data)#打印每个像素点的颜色RGBA的值(r,g,b,alpha)
# print (data[0])#打印RGBA的r值
if (data[0]==105 and data[1]==105 and data[2]==105):#RGBA的r值大于170,并且g值大于170,并且b值大于170
count=[0,0]
for t in range(3):
for q in range(3):
if(i+t-1>=0 and j+q-1>=0 and i+t-1<width and j+q-1<height):
temp = img.getpixel((i+t-1,j+q-1))
if(temp[0]==255):
count[0]+=1
else:
count[1]+=1
img.putpixel((i,j),color[count.index(max(count))])#则这些像素点的颜色改成大红色
img.save('1.png')
im = Image.open("1.png")
text = pytesseract.image_to_string((im))
print (text)