0%

自动登录学校网站(续)

解析登录网址

首先我们要定位到登录的网址以便driver访问。

将浏览器打开到登录页面,可以看到对应的网址为

1
https://jaccount.sjtu.edu.cn/jaccount/jalogin?sid=jaoauth220160718&client=CAzoSoajfvlBJKYW7q11rIlS5ucaE%2FIWcLi2eNuNEJDa&returl=CPDcg4bZMUVfWbZWFi2BIAB%2BzZprZ2JnKxq%2Bd6MsjeS0nMw73qkvsCxG47FeRZmpnkzSb7Gf%2FCPIbwa%2BNH267zBN%2BexUD2RL%2BhgfQzpYNJ96UJUYoajLq%2Fqgx6g%2BL2Ol6i3p5RWYYIcKoV28CmbnNbPia3K1RfdgUgktPh4yNrgYsIciWlBvtMDF%2FLrJfK4rGV9Z%2BbdiXK6EFaQJId4mZXj45XlS2pmQtR5P6qOeSj3nvLDaxZ6bablX7cNO5IhPAmaj%2BMOH%2Fx5F7YpIbYoZg3R01iV6Rd7oO8pSmHFTFkvhJFWPTWI1C01nBDaKato1IDv%2BP0%2FF8C6e%2BTDf6MOGmge0SAXgweU0o4I2v06Xb5nDWAGGTAtyB2Vm0qxD7RB6C74n77jiGIGju4y13a%2Fi10RO4pmfPcik9st2unhTWfVMqDtYd%2BJ9jCjl8UOoel8G99qS7ET%2FVN%2FbP%2BfUkBJb7I8%3D&se=CBZz3nk3K70uQiJvss5rVxhD4A5GYffm65sDqZ6Zh2PbEFVqISnzjU2JVjCO1VYIYuNZMv%2F8W6s3

很明显这是拼接了随机数的网址而不是真正应该访问的,为了得到真正的登录页面的网址,需要动态解析。

打开一开始的登录选项,要点击校内用户登录才会加载出真正的登录页面,因此可以猜测这是一个动态加载的页面。所以我们打开页面控制台,点击Network选项,一开始是空白的,等待新加载的信息。

image-20220512114437557

然后点击登录按钮,会跳转到登录页面。左边的控制台已经加载了许多信息,要在这些信息中找到加载网址的信息,一般来说我们查看第一条条目即可。

image-20220512114740458

选中第一条,可以看到请求的url,这就是真正的登录页面的网址,这里可以复制这个地址打开看看是不是正确的,如果不是就继续找。

image-20220512114923472

模拟登录

得到网址后,可以进行登录。登录一般来说有两种方式,一种是用requests库的post请求,一种是用selenium的driver直接进行网页模拟登录。这里选择用driver的方式主要基于以下两个方面:

  • 目的上看,我们想要自动登录后还能够操纵我们的网页干其他事情,用driver的话刚刚好
  • 从可行性的角度上看,登录过程我们需要验证码识别,如果用requests库抓取,前面提到抓取下来的验证码是不一样的,每次抓取都会改变内容。因此我能想到的解决方法就是用driver的页面进行静态的截图,实际上这符合我们人眼识别的过程。

因此,我们对于验证码的识别就是根据截图来进行的。同样,这里使用我们之前训练的模型,因此要把模型先放到model.py里面。下面给出处理验证码的代码文件。首先定义验证码信息参数,如长度、大小等。然后我们要把模型初始化,加载参数的过程会比较慢(推理很快),我们不想浪费这段时间,由于用driver打开网页也需要一段时间,因此就可以开一个线程来加载参数,与此同时打开网页,可以节省一些时间。

然后定义了截图操作,先截全屏再截下验证码。这里验证码的位置信息通过网页源代码(html)的信息获取,用xpath、class、id定位都行,在webdriver都定义了相关的操作。需要注意的是scaling_ratio这一变量,要截下正确的验证码位置与我们屏幕的缩放比例有关。可以在桌面上右键点击显示设置,然后查看屏幕的缩放与布局,根据缩放的比例设置变量。

image-20220512121304774

最后还有一个验证码解码的方法,实际上这跟前面predict.py的操作类似。所有代码如下:

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
# captcha.py
from model import CNN_sjtu
import torch.nn
import torch
from PIL import Image
from torchvision.transforms import Compose, ToTensor, Resize,Normalize
import time
from selenium.webdriver.common.by import By
from threading import Thread
import warnings

#pytorch版本关系,会有warning,实际上可以忽略
warnings.filterwarnings("ignore")

#定义验证码信息参数
numchar = 4
alphabet = 'abcdefghijklmnopqrstuvwxyz'
width = 100
height = 40

#模型初始化
model_net = CNN_sjtu(num_class=len(alphabet), num_char=int(numchar), width=width, height=height)
model_net = model_net.cuda()
model_net.eval()

def load_net():#load参数 比较慢,用一个额外的线程先load
global model_net
print("load net......")
model_net.load_state_dict(torch.load(r'C:\Users\14242\PycharmProjects'
r'\DL\Pytorch_project\captcha-CNN-验证码识别\weights/model_sjtu_4.path'))#参数位置

thread_load= Thread(target=load_net)
thread_load.start()

def decode_captcha(img_path):
global model_net
with torch.no_grad():
img=Image.open(img_path)
img = img.convert('RGB')
transforms = Compose([Resize((height, width)), ToTensor(), Normalize(0, 1)])
img = transforms(img)
img = img.view(1, 3, height, width).cuda()
output = model_net(img)
output = output.view(-1, len(alphabet))
output = torch.nn.functional.softmax(output, dim=1)
output = torch.argmax(output, dim=1)
output = output.view(-1, numchar)[0]
return ''.join([alphabet[i] for i in output.cpu().detach().numpy()])

def get_snap(driver): # 对目标网页进行截屏。这里截的是全屏
driver.save_screenshot('full_snap.png')
page_snap_obj=Image.open('full_snap.png')
return page_snap_obj

def get_image(driver): # 对验证码所在位置进行定位,然后截取验证码图片
scaling_ratio=1#系统显示的缩放比例
img = driver.find_element(By.XPATH, '//*[@id="captcha-img"]')#获取验证码在网页中的位置信息
time.sleep(0.10)#等待一会
location = img.location#位置
size = img.size#大小
left = location['x']*scaling_ratio
top = location['y']*scaling_ratio
right = left + size['width']*scaling_ratio
bottom = top + size['height']*scaling_ratio
page_snap_obj = get_snap(driver)#获取整个页面截图
image_obj = page_snap_obj.crop((left, top, right, bottom))#截下验证码
image_obj.save('captcha.png')

下面是我们登录的主程序,注意这种方式要先下载对应于谷歌浏览器版本号的chromedriver.exe放到当前的目录下。

通过send_keys()方法填入用户名、密码、验证码;接着通过click()方法自动点击登录按钮即可,注意操作网页的过程一定要适当地等待加载(sleep)。

由于验证码可能识别错误,因此要循环判断是否成功登录,直到成功再停止。我的方法是判断当前页面的title是否发生了改变,这是一个比较简单方便的方式。

最后,由于一开始我们生成了两个截图,可以用os.remove()将其删除。

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
# login.py
from captcha import decode_captcha as decode_c
from captcha import get_image
from selenium import webdriver
import time
import os
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By

#调用谷歌driver的基本操作
options = Options()
driver = webdriver.Chrome(options=options)#调用当前路径下的chromedriver.exe
driver.maximize_window()#最大化

#截图的保存地址
captcha_path='./captcha.png'
snap_path='./full_snap.png'

#两个地址链接
url = 'https://i.sjtu.edu.cn/jaccountlogin'
url ='https://oc.sjtu.edu.cn/login/openid_connect'

driver.get(url) #打开网页

cur_title=driver.title
while(driver.title==cur_title):#失败则一直试,因为验证码可能错误,两次应该就能成功
time.sleep(0.10) # 加载等待
get_image(driver)
print("推理验证码......")
captcha_res=decode_c(captcha_path)
driver.find_element(By.NAME, 'user').send_keys('username') # 填入用户名
driver.find_element(By.NAME, 'pass').send_keys('password') # 填入密码
driver.find_element(By.NAME,'captcha').send_keys(captcha_res) # 填入验证码
driver.find_element(By.ID,"submit-button").click()

os.remove(captcha_path)
os.remove(snap_path)
print("ending......")