在日常工作中常常需要重复填写某些表单, 如果人工完成, 费时费力, 而且网络延迟令人十分崩溃. 如果能够用程序实现自动填表, 效率可以提高一倍以上, 并且能够移植到多台计算机, 进一步提高工作效率. webdriver 是 python 的 selenium 库中的一个自动化测试工具, 它能完全模拟浏览器的操作, 无需处理复杂的 request,post, 对爬虫初学者十分友好.
一, 环境配置
python3.6+selenium 库 + xlrd 库 + xlwt 库
其中 xlrd 和 xlwt 库用于读写 excel 表中的数据.
还要下载一个浏览器的 driver 文件用于打开浏览器, 注意要选择与计算机系统相符合的版本(max/windows64 位 / windows32 位)
- ChromeDriver:http://npm.taobao.org/mirrors/chromedriver/ http://selenium-release.storage.googleapis.com/index.html
- IEDriver: http://selenium-release.storage.googleapis.com/index.html
将下载下来的 driver.exe 放到浏览器根目录和 python 的根目录
二, 打开网页
以 IE 浏览器为例, 以下两行代码就可以实现打开一个 IE 浏览器并且访问我们需要填表的网站
- driver= webdriver.Ie()
- driver.get('http://xxxx.com/')
如果网站需要登陆(需要填表的一般是公司内部网站), 再写一个 login 函数, 将 driver 作为参数调用
driver = login(driver)
注意一定要将 driver 传回, 这样 driver 才能继续接受程序的指令
三, 元素定位
webdriver 的工作原理是找到网页中某一个元素, 可以对其进行填入数据或点击等操作.
关于元素定位可以参考这篇博客 https://blog.csdn.net/bananasssss/article/details/51316369
我主要用到的元素定位方式有
- driver.find_element_by_id('someid')# 通过元素的 id 定位
- driver.find_element_by_CSS_selector("input[value='确定'")# 查找一个 input 元素, 它的 value 属性值为'确定'driver.find_element_by_xpath("//span[contains(@style,'COLOR: red')]/span[1]")# 查找一个 style 属性值为'COLOR:red'的 span 元素的第一个 span 子元素
(1)通过 id 定位
如果我们想在网页表单的某一个位置填某项值或者点击某个按钮, 我们首先要用开发者工具查看这个元素的源代码, 然后首先观察它有没有 id, 如果有 id, 直接用 id 定位该元素. 然后, 用
- driver.find_element_by_id('someid').click()# 点击元素
- driver.find_element_by_id('someid').send_keys('somekeys')# 填入'somekeys'
- driver.find_element_by_id('someid').clear()# 清空输入框中已有的值
实现我们想要做的操作.
(2)通过 ccs selector 定位
如果我们想要操作的元素没有 ID, 那么我们就要找到它跟网页其他元素不同的特征, ccs selector 是一种十分灵活的定位方式, 其中用 value 定位是一个不错的选择. 以
driver.find_element_by_css_selector("input[value='确定'")
为例, 双引号中的 input 可以换成任何网页元素(div,span,input,a 等), 中括号中是该元素的某一个属性(style,id,value,class 等), 等号后面是该属性的值.
注意, 如果网页中有多个元素同时满足 ccs selector 的条件, 如有多个 value="确定" 的 input, 那么 find_element_by_css_selector 只会定位到在 html 源代码中最靠前的一个, 而 find_elements_by_css_selector 会找到源代码中所有满足条件的元素, 并以列表的形式返回这些找到的元素. 例如, 网页中弹出很多个提示框, 我们要一一去点确定, 可以这样操作
- list=driver.find_elements_by_css_selector("input[value=' 确定 ']")
- for l in list:
- l.click()
但是, 如果这些提示框是重叠出现的, 而最上层的提示框实际上在源码中更靠后的位置, 那么列表中第一个 "确定" 元素就会被叠在上面的提示框遮挡, 无法点击, 这个时候倒序一下数组就可以了, 从最后一个 "确定" 元素开始点击
- query=driver.find_elements_by_css_selector("input[value=' 确定 ']")
- for q in query[::-1]:
- q.click()
(3)通过 xpath 定位
关于 xpath 定位详解可以参考 https://www.jianshu.com/p/820dcd013993
xpath 定位比较复杂但是非常全面, 当这个元素的 class,style 属性和其他元素一样, 实在没什么特点可以一步定位的时候, 我们就可以用 xpath, 先找到我们想要的元素的父子兄弟元素, 再定位到我们想要的元素. 例如
- driver.find_element_by_xpath('//*[@class="submit clear"]/input[1]').click()
- text =driver.find_element_by_xpath("//input[@value=' 确定 ']/../preceding-sibling::div[1]").text
- driver.find_elements_by_xpath("//span[contains(@style,'COLOR: red')]/span[1]")
引号中的 // 表示相对定位, 表示从源代码中任何地方开始寻找.
// 后可以跟任何元素,* 代表任意元素, 即定位符合属性筛选任何元素.
中括号内是属性的筛选条件,@后可以加任意属性. contains(@style,'COLOR: red')表示的筛选条件是: style 属性中包含 "COLOR:red". 这里为什么不直接用 @style='COLOR: red'
的原因是, 可能在我们审查源代码的时候这个元素的 style 属性只有'COLOR: red'这一条, 但是动态界面的 style 属性经常变化, 程序运行时直接用等于是定位不到这个元素的.
我们通常需要靠先找到某个有 id 的元素, 再通过层级关系定位到我们真正想要定位的元素, 关于兄弟父子元素定位请参考 https://blog.csdn.net/huilan_same/article/details/52541680
/.. 可以定位这个元素的父亲元素
/ 可以定位这个元素的子元素
/preceding-sibling:: 可以定位这个元素的哥哥元素
/following-sibling:: 可以定位这个元素的弟弟元素
如 / input[1]表示子元素中第一个 input,/../preceding-sibling::div[1]表示父元素的哥哥元素中的第一个 div
(4)通过当前节点定位
有时候我们会遇到需要判断一下元素当前的状态 (是否被选择) 再决定接下来的操作的情况, 这时就需要用一个变量来保存当前节点
LTE=driver.find_element_by_xpath("//input[@id='LTE']/../span[1]"
然后再用 get_attribute 获得当前节点元素的属性, 在这个例子里, 如果元素为蓝色, 就不需要点击. 代码实现为:
- if LTE.get_attribute("style")=="COLOR: blue":
- pass
- else:
- LET.click()
需要筛选出特定文本的情况:
- red=driver.find_elements_by_xpath("//span[contains(@style,'COLOR: red')]/span[1]")# 找出所有红色的文本
- for r in red:
- if '低消' in r.text:# 如果文本信息中包含'低消'
- r.find_element_by_xpath("./../preceding-sibling::input[1]").click()# 注意从当前节点定位的时候要以'./'开头
- break
如果寻找的元素需要滚动界面才能看到, 这个时候可以用 js 聚焦此元素, 页面便会滚动到该元素的位置
- target=driver.find_element_by_css_selector("input[value=' 确定 ']")
- driver.execute_script("arguments[0].scrollIntoView();", target)
- target.click()
四, 不确定情况处理
(1)有可能出现的弹窗
在填表过程中, 有些地方有可能出现一个弹框也有可能不出现, 这个时候, 无论这个弹窗是什么, 用 try..except 语句处理就可以解决
js 触发的弹窗:
- try:
- driver.find_element_by_css_selector("input[value=' 确定 ']").click()
- except Exception as e:
- pass
网页 alert 弹窗:
- try:
- driver.switch_to.alert.dismiss()
- except Exception:
- pass
dismiss()对应的是 alert 弹窗的 "取消" 项, accept()对应的是 "确定" 项, driver.switch_to.alert.text 可以获得弹窗的文本内容.
(2)数量不定的弹窗
对上文提到的多个提示框情况, 除了用 query=driver.find_elements_by_css_selector("input[value=' 确定 ']") 一次性找到所有元素再顺序或倒序点击之外, 还可以用一个 while 循环解决
- while(1):
- try:
- driver.find_element_by_css_selector("input[value=' 确定 ']").click()
- except Exception as e:
- break
(3)网络延迟
有些网页在点击查询信息之后需要加载一段时间, 加载中的页面是找不到我们接下来想找的元素的, 因此程序就会报错, 此时有两种解决方法.
一种是固定等待一段时间, 等待网页加载完毕, 这种方法的缺点是很难找到等待的最佳时间, 太短的话页面还没加载完, 太长就影响效率
time.sleep(2)
另一种是用一个 while 循环一直寻找下一个我们要找的元素
- while(1):
- try:
- driver.find_element_by_id('continueTrade').click()
- break
- except Exception:
- pass
这种方法的前提是下一个要找的元素必定会出现
五, frame 处理
关于 frame 处理这篇博客写得非常好 https://blog.csdn.net/huilan_same/article/details/52200586
总结起来就是: frameset 不用切, frame 层层切. 最好一系列填表操作完后都用 driver.switch_to.default_content() 回到原文档, 这样不容易混乱
这里再补充一点 frame 没有 id 时的切入方法
- frame= self.driver.find_element_by_xpath("/html/body/div[12]/iframe")# 先定位 frame 位置, 用一个变量储存这个节点
- self.driver.switch_to_frame(frame)# 再切入这个节点
六, excel 数据读写
excel 数据读写十分简单, 看代码就好了:
- def read(file):
- data = xlrd.open_workbook(file)# 打开 excel 文件
- table = data.sheets()[0]# 读取第一个 sheet 的数据
- phones = table.col_values(0)# 以列表形式存储第一列数据
- peoples = table.col_values(1)# 以列表形式存储第二列数据
- return phones,peoples
- def write(result):
- file=xlwt.Workbook()# 创建一个 excel 文件
- table = file.add_sheet('sheet1')# 添加一个 sheet
- for i in range(len(result)):# 写入数据
- table.write(i,0,result[i][0])
- table.write(i,1,result[i][1])
- table.write(i,2,result[i][2])
- file.save('result.xls')
结语: 希望技术能让人们从无意义的重复劳动中解脱: D
来源: https://www.cnblogs.com/luozx207/p/9003214.html