0%

自动登录学校网站(终:优化)

程序优化

对于原来的代码,尽管能够成功登录学校的网站,但依旧有很多地方可以优化。

  • 模型加载较慢,python加载本地训练好的模型还是太慢了,并且要打包成exe文件的话,torch这个库太大了,不可能再用自己的模型来完成项目。所以这里可以再用到之前的验证码识别库ddddocr来代替自己的识别模型。

  • 考虑到项目迁移的方便,每次都下载一个chromedriver.exe文件是比较麻烦的,禁用谷歌浏览器自动升级是个好的选择,但是有更好的替代方法。可以使用webdriver-manager这个库,它会自动下载对应的driver到缓存里。具体的使用方式是

    1
    2
    3
    4
    from webdriver_manager.chrome import ChromeDriverManager
    options = webdriver.ChromeOptions()
    options.add_experimental_option('excludeSwitches', ['enable-logging'])#忽略dirver的信息打印
    driver = webdriver.Chrome(ChromeDriverManager().install(),options=options)#可以在缓存里下载最新的driver
  • 上面两个解决方式分别能够优化程序的运行速度项目迁移性,但直接打包成.exe文件还是会产生其他的问题。

可执行文件优化

  • 程序正常运行的情况下会在结束时关闭浏览器,除非我们在程序末尾sleep一个很长的时间等待。然而我们希望程序进程在打开浏览器后就可以退出,而不必等待我们将浏览器关闭。可以使用options.add_experimental_option("detach", True),这个语句使程序结束后浏览器不会关闭,不需要程序一直等待。

  • 我们打包的文件会调用chromedriver.exe,尽管我们可以设置我们的可执行文件(.exe)运行时不出现cmd窗口,但chromedriver.exe依然会出现自己的窗口展示运行信息,而这会影响简结,我们希望这个窗口不出现。解决方法是修改selenium包中的service.py(selenium->webdriver->common->service.py)源码。如下图,注意数字必须相同。

    image-20220512161629179
  • 最后的问题是,我们的程序自动退出了,因为不将浏览器关掉,所以chromedriver.exe这个进程会一直留在内存里。如果我们多次打开我们的.exe文件,就会有很多个chromedriver.exe进程,尽管占用的空间很小,我们也希望能在程序结束后终止掉这个进程,这并不会导致我们的网页关闭。

    • 我们可以用"taskkill /im chromedriver.exe /F"这个命令来杀死这个进程,一种容易想到的方式是用os.system(command)来执行这条命令,但是这个方式会在执行时闪现出cmd执行窗口,不是我们希望的方式。

    • 更好的方式是使用python的另一个标准库subprocess的subprocess.call(command, creationflags=0x08000000)。因此就可以写成

      1
      subprocess.call("taskkill /im chromedriver.exe /F", creationflags=0x08000000)

以上就把代码优化完毕了,两个代码文件如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#captcha_fast.py
from PIL import Image
from selenium.webdriver.common.by import By
import time
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')

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
#login_oc.py
from captcha_fast import get_image
from selenium import webdriver
import time
import os
import subprocess
from selenium.webdriver.common.by import By
import ddddocr

ocr = ddddocr.DdddOcr(use_gpu=True)
from webdriver_manager.chrome import ChromeDriverManager
options = webdriver.ChromeOptions()
options.add_experimental_option("detach", True)
options.add_experimental_option('excludeSwitches', ['enable-logging'])
driver = webdriver.Chrome(ChromeDriverManager().install(),options=options)
driver.maximize_window()#最大化

captcha_path='./captcha.png'
snap_path='./full_snap.png'
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("推理验证码......")
with open(captcha_path, 'rb') as f:
img_bytes = f.read()
captcha_res = ocr.classification(img_bytes)
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)
subprocess.call("taskkill /im chromedriver.exe /F", creationflags=0x08000000)#杀死进程,用os.system会有黑窗口闪现
print("ending......")

项目打包

最后进行项目的打包,我使用pyinstaller库来打包项目,用得比较习惯。首先在cmd窗口切换目录到python文件的目录,打包的命令为:

1
pyinstaller -w -i icon.ico login_oc.py
  • -w表明取消程序执行的命令行窗口
  • -i 表示添加打包文件的图标,后面的icon.ico就是我们使用的图标,要放在目录下,或者把路径写全。注意不能用其他后缀的图片,可以先去在线转换网站把图片转换成.ico格式,还需要注意的是,这个icon大小必须是16×16的。
  • 最后想说这里不使用 -F 这个参数打包的原因。
    • 我们希望程序响应快一些,-F会把所有的项目依赖的文件打包在一起,获得一个比较大的.exe文件,这会导致文件的加载速度变慢。而不用 -F 会导出一个文件夹,放置项目依赖的文件。
    • ddddocr这个库在打包的过程中,没有被打包进去。因此不使用 -F 恰好能让我们手动把这个库添加到工作目录下(所有的库和我们的.exe文件都在这个文件夹中,还包括很多其他的.dll文件等)。
    • 如果我们想做多个.exe文件登录不同的网站(我做了两个),程序使用的库基本都是一样的,仅仅是代码有一些修改,甚至只是url修改了以下。因此我们可以把多个.exe文件都放到一个工作目录下,共享这些项目依赖文件,这样就能很大地降低内存开销。

项目使用

在项目打包后,注意.exe文件不能移动到别的地方,要创建一个快捷方式,把这个快捷方式移动就可以在桌面或者其他地方使用了。

整个项目到此就结束了,有任何问题欢迎评论