基于 Python Selenium 的表单填写自动化程序

完整代码:GitHub

Selenium 简介

Selenium是一个用于测试网站的自动化测试工具,支持各种浏览器包括Chrome、Firefox、Safari等主流界面浏览器,同时也支持phantomJS无界面浏览器。

框架底层使用JavaScript模拟真实用户对浏览器进行操作。测试脚本执行时,浏览器自动按照脚本代码做出点击,输入,打开,验证等操作,就像真实用户所做的一样,从终端用户的角度测试应用程序。

这里使用 Python 的 Selenium 库实现一个表单填写自动化程序。

安装配置 Selenium 环境

1
$ pip install selenium

Selenium3.x 调用浏览器必须有一个 webdriver 驱动文件:

这里使用的是 Chrome 驱动文件。

下载Driver要注意和你当前已有的浏览器的版本是否一致。

添加到环境变量:

  • 如果你使用的是 Windows 操作系统:手动创建一个存放浏览器驱动的目录,如: C:\driver , 将下载的浏览器驱动文件(例如:chromedriver、geckodriver)丢到该目录下。转到 此电脑->属性->系统设置->高级->环境变量->系统变量->Path,将 C:\driver 目录添加到 Path 的值中。
  • 如果你使用的是 Linux/Unix/macOS 操作系统:直接将解压得到的驱动文件(如:chromedriver、geckodriver)拷贝到 /usr/loacl/bin 目录下即可。

环境变量path中加入路径后还是报错,建议先重启一下编辑器。

简单验证不同的浏览器驱动是否正常使用。

1
2
3
4
5
6
7
8
from selenium import webdriver

driver = webdriver.Firefox() # Firefox浏览器
driver = webdriver.Chrome() # Chrome浏览器
driver = webdriver.Ie() # Internet Explorer浏览器
driver = webdriver.Edge() # Edge浏览器
driver = webdriver.Opera() # Opera浏览器
driver = webdriver.PhantomJS() # PhantomJS

Selenium 使用

使用 selenium.webdriver 来创建一个对象,调用浏览器驱动。

1
browser = webdriver.Chrome()

通过 get 方法访问表单地址。

1
browser.get("http://xx.xxxx.edu.cn/xxx/xxx/xxx/jkxxtb/jkxxcj.jsp")

Selenium 定位元素的方式

定位一个元素 定位多个元素 含义
find_element_by_id find_elements_by_id 通过元素id定位
find_element_by_name find_elements_by_name 通过元素name定位
find_element_by_xpath find_elements_by_xpath 通过xpath表达式定位
find_element_by_link_text find_elements_by_link_tex 通过完整超链接定位
find_element_by_partial_link_text find_elements_by_partial_link_text 通过部分链接定位
find_element_by_tag_name find_elements_by_tag_name 通过标签定位
find_element_by_class_name find_elements_by_class_name 通过类名进行定位
find_elements_by_css_selector find_elements_by_css_selector 通过css选择器进行定位

这里主要使用到了元素 id、元素 name、类名和 xpath 表达式来定位元素。

控制浏览器操作的一些方法

方法 说明
set_window_size() 设置浏览器的大小
back() 控制浏览器后退
forward() 控制浏览器前进
refresh() 刷新当前页面
clear() 清除文本
send_keys(value) 模拟按键输入
click() 单击元素
submit() 用于提交表单
get_attribute(name) 获取元素属性值
is_displayed() 设置该元素是否用户可见
size 返回元素的尺寸
text 获取元素的文本

这里主要使用到了 send_keys(value) 和 click() 。

鼠标事件

在 WebDriver 中, 将这些关于鼠标操作的方法封装在 ActionChains 类提供。

方法 说明
ActionChains(driver) 构造ActionChains对象
context_click() 执行鼠标悬停操作
move_to_element(above) 右击
double_click() 双击
drag_and_drop() 拖动
move_to_element(above) 执行鼠标悬停操作
context_click() 用于模拟鼠标右键操作, 在调用时需要指定元素定位
perform() 执行所有 ActionChains 中存储的行为,可以理解成是对整个操作的提交动作

键盘事件

Selenium中的Key模块为我们提供了模拟键盘按键的方法,那就是send_keys()方法。它不仅可以模拟键盘输入,也可以模拟键盘的操作。

常用的键盘操作如下:

模拟键盘按键 说明
send_keys(Keys.BACK_SPACE) 删除键(BackSpace)
send_keys(Keys.SPACE) 空格键(Space)
send_keys(Keys.TAB) 制表键(Tab)
send_keys(Keys.ESCAPE) 回退键(Esc)
send_keys(Keys.ENTER) 回车键(Enter)

组合键的使用

模拟键盘按键 说明
send_keys(Keys.CONTROL, ‘a’) 全选(Ctrl+A)
send_keys(Keys.CONTROL, ‘c’) 复制(Ctrl+C)
send_keys(Keys.CONTROL, ‘x’) 剪切(Ctrl+X)
send_keys(Keys.CONTROL, ‘v’) 粘贴(Ctrl+V)
send_keys(Keys.F1…Fn) 键盘 F1…Fn

更多详见参考资料

问题解决

将各个操作封装到类 DailyReport 中。

定位(固定的)元素

在类中封装 __find_by 方法用于根据元素 id、元素 name、类名来定位元素:

1
2
3
4
5
6
7
def __find_by(self, key: str):
""" 元素选择器 """
return {
'id': lambda locator: self.__browser.find_element_by_id(locator),
'name': lambda locator: self.__browser.find_element_by_name(locator),
'class': lambda locator: self.__browser.find_element_by_class_name(locator)
}[key]

填写文本框

传入元素,清空原有内容并输入新的内容:

1
2
3
4
5
@staticmethod
def __send_text(elem, key: str):
""" 填写文本框 """
elem.clear()
elem.send_keys(key.strip())

模糊定位元素

世界上最远的距离大概就是明明看到一个页面元素站在那里,但是我却定位不到!!

在测试使用的网站中,有一些元素是动态加载的,其 id 属性不断变化,这就需要利用 xpath 来模糊定位元素。

XPath 使用路径表达式来选取 XML 文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。

xpath可以以标签定位,也可以@任意属性:

  • 如:以input标签定位:driver.find_element_by_xpath("//input[@id='kw']")
  • 如:@type属性:driver.find_elements_by_xpath("//input[@type='text']")

其中 contains 方法可以实现模糊匹配:driver.find_elements_by_xpath("//*[contains(@id, '省份')]")

关于 xpath 的具体用法可以参考资料

在测试使用的网站中,省份和地级市的标签是在点击了 select2-select_***-container 的 span 标签后才显示的,如下:

image.png

并且id属性是变化的,只能通过模糊匹配的方法:

1
2
3
4
def __fuzzy_click(self, val: str):
""" 模糊点击,xpath模糊查找id """
self.__browser.find_element_by_xpath('//*[contains(@id,\'{}\')]'.format(val)).click()
# val 为传入的要匹配的字段

智障操作:debug一晚上,结果发现上面的匹配完忘记 .click() 了。

登录

在测试使用的网站中,首次打开会转到登录页面。解决方案:先定位一个表单中的元素,如果未找到则说明进入了登录页面,执行登录操作。

1
2
3
4
try:
self.__find_by('name')('swtw')
except NoSuchElementException:
login()

具体的登录操作就是用 __find_by 方法定位到学号和姓名的输入框,把信息用 __send_text 方法输入进去,定位并点击登录按钮即可。

复选框的选择

首先寻找所有的已经被选中的元素全都点击一遍。

1
2
3
4
5
6
7
def __uncheck(self):
""" 将所有勾选的复选框取消勾选 """
try:
while True:
self.__click_class('icheckbox_square-green checked')
except NoSuchElementException:
pass

之后根据唯一的元素 id 找到并点击(__click_id 方法)即可。

下拉列表选择

首先点击下拉列表 self.__click_id('select2-select_***-container')

之后模糊定位要点击的元素 self.__fuzzy_click(...) 即可。

使用搜索:谷歌必应百度