图书馆座位总是抢不到,打算写一个python脚本实现自动化
初步构想:
1.0
实现基本功能:座位捡漏,输出座位信息,记住密码,保存签到二维码
实现基本ui: 输入密码,输入学号,选择校区,信息输出提示,浏览器内核选择
2.0
新加功能:明日预约,做一道复杂题目实现->超时前自动退座
新加ui: 优化ui卡顿,明日预约选择按钮,超时前自动退座选项,置顶/提示音
3.0
手机端,自动悬浮,自动提示音
...
1.0阶段调试心得---非ui:
定位元素:
找到元素不代表元素就可以点击(猜测没有彻底加载完),所以在点击前,最好先确认可以点击:is_enabled
wait.until(lambda divers: browser.find_element(By.XPATH, 'xpath路径').is_enabled())
对于有的元素则是需要先确定可以看到:
wait.until(lambda divers: browser.find_element(By.XPATH, 'xpath路径').is_displayed())
其次,就算元素可以点击也可能点击不到,可能是因为selenium的click真的是模拟,页面不划动到元素所在位置就不能点击(欢迎大佬指教,应该与ajax无关,因为这个网页不含ajax)。可能有两种情况,需要根据自己的网页判断:
1.被点击元素需要页面下滑 才能出现
2.被点击元素需要页面缩小(注意不是浏览器窗口缩放) 才能出现
# 实现滑动
js_glide = "window.scrollBy(0,1200);" # (左-右+,上-下+)
browser.execute_script(js_glide)
# 有个问题,执行多次是会下滑多次吗,还是不变??
# 实现页面内部缩放
js_zoom_in = "document.body.style.zoom='0.5'" # >1放大 <1缩小
browser.execute_script(js_zoom_in)
关于这上述代码的介绍:
https://developer.mozilla.org/zh-CN/docs/Web/API/Window/scrollBy
https://blog.csdn.net/J_____Q/article/details/118576550
还有一个粗心犯的错,find_elements()返回的是一个列表,所以需要列表索引来实现点击等等
还有一个很容易忽略的地方,定位元素的顺序,尤其包括要点击的元素。比如说要定位页面A的元素a,b。其中a元素用来点击实现页面跳转,b用来获取文本信息。一定要先获取b后再点击a。
获取元素属性
(86条消息) WebElement接口获取值_weixin_30652879的博客-CSDN博客
size 获取元素的尺寸
text 获取元素的文本
get_attribute(name) 获取属性值
location 获取元素坐标,先找到要获取的元素,再调用该方法
page_source 返回页面源码
driver.title 返回页面标题
current_url 获取当前页面的URL
is_displayde() 判断该元素是否可见
is_enabled() 判断元素是否被使用
is_selected() 判断元素是否被选中
tag_name 返回元素的tagName
text获取时候,不知道为什么之前对,后来啥也没改就不对了,换成下边这个就好啦
myroom = room_number[0].get_attribute('textContent') # room_number[0]是一个定位到的元素
延时
介绍了常用的三种延时,看完后我把所有的sleep都换成了wait.untill哈哈哈哈,果然快了不少
异常捕捉机制
有的异常出现是概率事件的必然,不是代码的问题。比如说加载一个页面,可能由于网速问题加载不出来,代码会直接终止报错,这时候异常捕捉就起到了大作用。
(86条消息) Python异常捕获与处理_python异常捕获和处理_宗而研之的博客-CSDN博客
这篇比较全面,让我怀疑我的用法很低效,还有什么更好的方法吗?
# 出现异常(找不到元素),就下滑,直到找到元素
while 1:
try:
optional_room[0].click()
except:
js = "window.scrollBy(0,1200);"
browser.execute_script(js)
else:
break
下载图片,以日期命名
qrcode_url = browser.find_element(By.XPATH,'//img[@id="qrcode"]').get_attribute('src')
qrcode_data = requests.get(url=qrcode_url).content
img_path = datetime.datetime.now().strftime('%Y-%m-%d')+'签到.jpg'
1.0阶段调试心得---ui:
textBrowser输出信息
self.textBrowser.append(mes) 输出信息的时候,不论如何追加,都是等代码全部运行完,才更新上去(好像,不重要,先知道正确的吧)。如果不想换行,append()换成insertPlainText()可以。
def printf(self, mes):
self.textBrowser.append(mes)
self.cursot = self.textBrowser.textCursor()
self.textBrowser.moveCursor(self.cursot.End)
QApplication.processEvents() # 加上这一行就会实时更新了
pyqt三个工具的使用
designer 用来设计ui,会自动生成.ui代码
pyuic 将上述工具生成的ui代码转换为py代码
pyrcc 将图片转换为.qrc资源文件后,转换为py代码。(主要是打包exe时候,不知道怎么带图片,网上推荐这样)
qrc格式如下,其中文件名需要自己手动更改。网上有自动生成的脚本下次找找
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/image">
<file alias="background2.png">image/background2.png</file>
<file alias="background.png">image/background.png</file>
<file alias="icon2.ico">image/icon2.ico</file>
</qresource>
</RCC>
前缀: prefix
别名: alias
好像有别名的话,只能用别名,别名可以不加(待考证),相对路径,注意qrc文件的位置
使用方法:
self.setWindowIcon(QIcon(':/icon2.ico'))
界面和逻辑分离
之前自动生成的ui,经过pyuic转换为py脚本(名字:ui.py) 后,就不要在里边添加管控其逻辑的代码啦~不然稍微一改ui功能就没有了。所以最好的方法是单独放一个文件(名字:ui_function.py)。然后主函数调用(文件名:main.m)!
其中ui_function.py涉及到的常用模块(ctrl+f可以搜索):
设置信号
设置图标
设置背景
实时输出信息到textBrowser
获取lineEdit输入
# ui.py
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'ugly.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_StudyCat(object):
def setupUi(self, StudyCat):
StudyCat.setObjectName("StudyCat")
StudyCat.resize(500, 500)
StudyCat.setMinimumSize(QtCore.QSize(500, 500))
StudyCat.setMaximumSize(QtCore.QSize(500, 500))
self.pushButton = QtWidgets.QPushButton(StudyCat)
self.pushButton.setGeometry(QtCore.QRect(70, 450, 93, 28))
self.pushButton.setStyleSheet("background-color:rgba(255,255,255,200);")
self.pushButton.setObjectName("pushButton")
self.label_4 = QtWidgets.QLabel(StudyCat)
self.label_4.setGeometry(QtCore.QRect(20, 10, 471, 51))
self.label_4.setStyleSheet("font: 30pt \"Arial\";")
self.label_4.setTextFormat(QtCore.Qt.PlainText)
self.label_4.setObjectName("label_4")
self.textBrowser = QtWidgets.QTextBrowser(StudyCat)
self.textBrowser.setGeometry(QtCore.QRect(210, 100, 271, 381))
self.textBrowser.setStyleSheet("background-color:rgba(255,255,255,100);")
self.textBrowser.setLineWrapColumnOrWidth(0)
self.textBrowser.setObjectName("textBrowser")
self.label_5 = QtWidgets.QLabel(StudyCat)
self.label_5.setGeometry(QtCore.QRect(210, 80, 72, 15))
self.label_5.setObjectName("label_5")
self.checkBox = QtWidgets.QCheckBox(StudyCat)
self.checkBox.setGeometry(QtCore.QRect(70, 400, 91, 19))
self.checkBox.setObjectName("checkBox")
self.layoutWidget = QtWidgets.QWidget(StudyCat)
self.layoutWidget.setGeometry(QtCore.QRect(30, 80, 171, 281))
self.layoutWidget.setObjectName("layoutWidget")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.layoutWidget)
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.label = QtWidgets.QLabel(self.layoutWidget)
self.label.setObjectName("label")
self.verticalLayout_2.addWidget(self.label)
self.lineEdit = QtWidgets.QLineEdit(self.layoutWidget)
self.lineEdit.setStyleSheet("background-color:rgba(255,255,255,100);")
self.lineEdit.setObjectName("lineEdit")
self.verticalLayout_2.addWidget(self.lineEdit)
self.label_2 = QtWidgets.QLabel(self.layoutWidget)
self.label_2.setObjectName("label_2")
self.verticalLayout_2.addWidget(self.label_2)
self.lineEdit_2 = QtWidgets.QLineEdit(self.layoutWidget)
self.lineEdit_2.setStyleSheet("background-color:rgba(255,255,255,100);")
self.lineEdit_2.setText("")
self.lineEdit_2.setEchoMode(QtWidgets.QLineEdit.Password)
self.lineEdit_2.setObjectName("lineEdit_2")
self.verticalLayout_2.addWidget(self.lineEdit_2)
self.label_3 = QtWidgets.QLabel(self.layoutWidget)
self.label_3.setObjectName("label_3")
self.verticalLayout_2.addWidget(self.label_3)
self.comboBox = QtWidgets.QComboBox(self.layoutWidget)
self.comboBox.setAutoFillBackground(False)
self.comboBox.setStyleSheet("background-color:rgba(255,255,255,100);")
self.comboBox.setObjectName("comboBox")
self.comboBox.addItem("")
self.comboBox.addItem("")
self.verticalLayout_2.addWidget(self.comboBox)
self.retranslateUi(StudyCat)
QtCore.QMetaObject.connectSlotsByName(StudyCat)
def retranslateUi(self, StudyCat):
_translate = QtCore.QCoreApplication.translate
StudyCat.setWindowTitle(_translate("StudyCat", "StudyCat"))
self.pushButton.setText(_translate("StudyCat", "登录"))
self.label_4.setText(_translate("StudyCat", "好好学习,天天向上"))
self.label_5.setText(_translate("StudyCat", "选座信息:"))
self.checkBox.setText(_translate("StudyCat", "记住密码"))
self.label.setText(_translate("StudyCat", "学号:"))
self.label_2.setText(_translate("StudyCat", "密码:"))
self.label_3.setText(_translate("StudyCat", "校区:"))
self.comboBox.setItemText(0, _translate("StudyCat", "b"))
self.comboBox.setItemText(1, _translate("StudyCat", "w"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
StudyCat = QtWidgets.QWidget()
ui = Ui_StudyCat()
ui.setupUi(StudyCat)
StudyCat.show()
sys.exit(app.exec_())
# ui_function.py
import ctypes
from PyQt5.QtGui import QPainter, QPixmap, QIcon
import ui
from ugly import *
from PyQt5.QtWidgets import QApplication,QMainWindow,QFileDialog
import get
import time
import os
import main
from PyQt5 import QtCore, QtGui, QtWidgets
import resource_rc
# 本脚本用于实现ui的功能
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("myappid")
# 用于解决任务栏图标和exe图标不一致:设置任务栏图标
class MainCode(QMainWindow,ui.Ui_StudyCat): #继承自自动生成的界面类
def __init__(self):
QMainWindow.__init__(self)
ugly.Ui_StudyCat.__init__(self)
self.setupUi(self)
self.pushButton.clicked.connect(self.onclick)
self.checkBox.clicked.connect(self.rem_password) # 设置信号
self.setWindowIcon(QIcon(':/icon2.ico')) # 设置图标
def onclick(self):
main.login =True
get.get_seat(self,[self.lineEdit.text(), self.lineEdit_2.text(), self.comboBox.currentText()])
def printf(self, mes): #实时输出信息到textBrowser
self.textBrowser.append(mes)
self.cursot = self.textBrowser.textCursor()
self.textBrowser.moveCursor(self.cursot.End)
QApplication.processEvents()
# 简陋实现记住密码:输入行为空,则写入密码到输入行,输入行有密码,则写入密码到password.txt,后期改进考虑mysql
def rem_password(self):
if self.checkBox.isChecked(): # 记住密码被选中
if len(self.lineEdit_2.text())!=0: # 保存密码
Note = open('password.txt', mode='w')
Note.write(self.lineEdit.text() + '\n' + self.lineEdit_2.text() )#+ '\n' + self.comboBox.currentText())
Note.close()
else: # 输出已保存的密码
Note = open('password.txt', mode='r')
contents = Note.read().splitlines()
Note.close()
self.lineEdit.setText(contents[0])
self.lineEdit_2.setText(contents[1])
else: #未被选中 清空
self.lineEdit.clear()
self.lineEdit_2.clear()
# 设置背景
def paintEvent(self, event):
painter = QPainter(self)
pixmap = QPixmap(":/background2.png")
# 绘制窗口背景,平铺到整个窗口,随着窗口改变而改变
painter.drawPixmap(self.rect(), pixmap)
# main.py
import ui #导入自动生成的ui.py文件
import ui_function
from ui_function import *
import sys
if __name__ == '__main__':
# 只有直接运行这个脚本,才会往下执行
# 别的脚本文件执行,不会调用这个条件句
# 实例化,传参
app = QApplication(sys.argv)
# 创建对象
mainWindow = QMainWindow()
md = ui_function.MainCode()
md.show()
sys.exit(app.exec_())
样式表-设置控件的透明度
background-color:rgba(255,255,255,100);
最后一个数字越大,透明度越低
1.0阶段调试心得---体会:
写博客总结时,才发现好多东西真就cv别人,倒也没啥,只是比较基础的部分,自己也还是不懂原理,甚至函数具体参数的含义,单纯的有用就行,大概记住涉及的函数名,不理解,不吸收,某种角度讲只是增强了cv能力。而且这样调试起来很慢,不知道问题出在哪里。
如上述,导致很多东西都是拼凑起来的,没有系统的学习,可能效率很低,代码乱七八糟。。。。考完试继续学习吧~
debug不熟练,好多都是不停的print输出看结果的,倒也可以忍受,但是我的print输出的都是乱码单纯的用来标志哪一行代码有问题,,,,其实大部分代码系统的报错就足以说明问题,如果说except捕捉了所有异常,导致查不出原因,那就不该设置捕捉所有异常。不过还学到了一个很有用的东西,编辑配置,选择模拟输出控制台的终端,好多不知道为啥不报错的错就报了。
![](https://i-blog.csdnimg.cn/blog_migrate/bb3e450b755ebdc160e21d0326f133b4.png)
pyqt的界面可视化操作,学的不行,好多自动化的功能没用上,比如自动选择资源文件。
github不太会整,副本乱七八糟
不过,最后还是有成就感的,耶,但还需要继续努力!!!!
![](https://i-blog.csdnimg.cn/blog_migrate/518599f89912d2cb5d758b40b97329bf.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e89b15f6aa03bc2e454629b27a9df098.png)
(之前手机拍的,,,懒得截屏了)
待更新。。。