前面我们通过四个步骤,使用Python开发了一个自动打卡程序:
一、调用Pyautogui库模拟鼠标操作,初步实现了一个简单得自动打卡程序。(文章链接)
二、调用Webbrowser库访问指定网页和登录,完善了自动打卡程序。(文章链接)
三、调用Random库实现了随机延时,避免每天自动打卡时间相同的问题。(文章链接)
四、使用Pystaller库打包Exe文件,将自动打卡程序分享给同事。(文章链接)
但同事没几分钟就跑过来说程序运行有问题:网页自动打开后,鼠标定位出问题了,账号和密码都没有成功输入。
查找原因,原来是自动打卡程序在开发过程中,使用了我个人电脑上账号输入框等网页元素的坐标去,而同事显示器的分辨率和我的不一样,账号输入框等网页元素的坐标并不相同,所以调用Pyautogui库模拟键鼠操作时给的坐标错误,导致程序运行出现问题。
针对这个问题,要提升自动打卡程序的适用范围,有两个解决思路:
一、改变所要操作网页元素的定位方法
主要思路:即不再是通过屏幕坐标来定位网页元素,而是通过网页元素在HTML文档中的路径表达式来获得它的定位,并使用Selenium或是Playwright等Web自动化框架来实现选择和操作网页元素。
那么,如何准确定位并选择网页元素呢?本文使用Selenium框架和XPath路径表达式为例来做演示。(Selenium自动化框架、XPath路径表达式和浏览器开发者工具的相关内容,需要大家做一些准备。具体可以参考《Selenium开发环境和相关工具准备》)。
大致分为3个步骤:
- 通过浏览器开发者工具找到网页元素对应的代码
通过F12键调出开发者工具,使用元素选择箭头,点中左侧登录界面中的账号输入框,便能看到右侧代码栏中高亮显示的对应代码。
- 分析识别代码中能够精确选择网页元素的XPath路径表达式
可以使用搜索栏来帮助分析识别,在上一步骤得到账号输入框的代码后(如下),Ctrl+F调出关键字搜索栏。
<input class="node_modules--anyshare-components-lib-Login-styles-desktop---input-login" value="" placeholder="请输入账号" data-reactid=".0.1.1.$content.0.0.0.1.1.0.0.0.1">
试探性输入input标签和class属性组成的XPath表达式,看看能不能选择到唯一的元素。
结果搜索栏中显示找到2个匹配项(如下图红框所示),并没能唯一确定要定位的账号输入框元素。
再次使用input标签和placeholder属性来尝试,这次只有1个匹配项,精确命中。
注意:可能在搜索栏中通过Div标签或者其他标签的路径表达式看起来也能唯一定位到网页元素,但是这些标签不能支持文字输入,所以还要结合后面的步骤一起尝试。
- 使用Selenium框架选择和操作网页元素
主要代码如下所示:
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from time import sleep
#创建 WebDriver 对象,指明使用chrome浏览器驱动,就是驱动压缩包解压的路径
wd = webdriver.Chrome(service=Service(r'E:\python-packages\selenium\chromedriver109_win32\chromedriver.exe'))
#implicitly_wait 函数是一种隐式等待,用于处理元素不存在以及元素加载时间不够的情况
wd.implicitly_wait(10)
# 调用WebDriver 对象的get方法 可以让浏览器打开指定网址
wd.get('https://www.baidu.com/')
#等待网页加载
sleep(3)
#根据Xpath定位账号输入框
element=wd.find_element(By.XPATH,'//input[@placeholder="请输入账号"]')
element.send_keys("wangwu")
......
#input('等待回车键结束程序')
由于程序运行完会自动关闭浏览器,速度很快,所以调试的时候最好加上一行input代码,等待用户输入后再退出程序。
上面场景比较简单,所以实现起来非常快速。但在实际选择和操作网页元素时,可能还会遇到需要要切换iFrame、自动翻滚网页等复杂情况,需要进行更细致的分析判断。
经过上面的改造,本单位同事通过文件拷贝方式,就能实现自动打卡程序的即插即用。但如果是其他单位,因为每个单位的打卡系统不一样,所以上述方法无效,应当考虑下面的解决思路。
二、允许用户自定义打卡操作步骤和内容
主要思路:通过一个用户可编辑的文本文件,将键鼠操作函数化、坐标位置和输入内容参数化。使得针对不同的打卡系统、不同的显示环境,用户可以灵活便捷地个性化维护设置打卡操作的步骤和内容。
大致有两部分工作:
- 确定用户自定义操作的语法规则,并维护到指定文件
简单示例:在程序相同目录下新建一个名为action.txt的文本文件,内容如下:
文件规则:每一行前面是操作命令,后面是参数,中间使用空格分隔。
- 改造程序,将原来固定的键鼠操作转变成函数
import pyautogui as gui
import webbrowser as web
import re
#按照URL打开网页
def open_url(url):
web.open(url,new=0,autoraise=True)
gui.sleep(3)
#鼠标移动
def mouse_move(x,y):
gui.moveTo(x,y,duration=1)
gui.sleep(1)
#鼠标单击
def mouse_leftclick():
gui.leftClick()
gui.sleep(0.5)
#输入字符
def input_str(str):
gui.typewrite(str,interval=0.1)
gui.sleep(0.5)
#休息等待
def sleep(sec):
gui.sleep(sec)
#操作命令解析
def parse_act(txt):
#根据空格符和回车符分割文本
action=re.split(' |\n',txt)
if action[0]=='open_url':
open_url(action[1])
elif action[0]=='mouse_move':
pos=re.split(',',action[1])
mouse_move(int(pos[0]),int(pos[1]))
elif action[0]=='mouse_leftclick':
mouse_leftclick()
elif action[0]=='input_str':
input_str(action[1])
elif action[0]=='sleep':
sleep(int(action[1]))
if __name__=='__main__':
#按行读取文件内容
with open("action.txt",'r') as act_file:
line=act_file.readline()
while line:
#调用操作命令解析函数
parse_act(line)
line=act_file.readline()
上述代码虽然比较简单,但也涉及到文件的读取,使用正则表达式解析字符串等内容,大家感兴趣可以深度研究。
改造结束,只需要将程序打包好的exe文件和action.txt文件拷贝到其他电脑上(注意2个文件放在相同目录),然后按需自行维护action.txt中的操作步骤和输入参数,即可实现自动打卡。