最近将watir更新到了1.9.1,忽然发现以前承诺的带大家读waitr源码的”夸夸其谈”还尚未实现,甚表歉意,暂且先说明一下watir定位(locate)元素的基本原来,聊以将功补过。

以下说明均以watir 1.9.1为例。在这里建议大家最好将watir升级到最新版本,因为最新版本增加了对IE9的支持,尽管支持的不是很全面,但聊胜于无,优势总是有的。

在watir的源码中找到locator.rb文件。该文件一般位于your_disk:\Ruby192\lib\ruby\gems\1.9.1\gems\watir-1.9.1\lib\watir\目录下。

locator文件定义了1个Locator类,这个类是定位对象的1个helper类。该类中如如下2个方法:

  • normalize_specifiers!: 该方法的作用是构造specifiers,而specifiers正是定位对象所要用到的”标识”。
  • matchwithspecifiers?: 该方法的作用是判断元素是否符合specifiers所定义的特征。如果符合,那么这个元素肯定就是我们要找的目标元素了。

简单看一下这2个方法的源码

首先简单介绍一下specifiers。specifiers看上去很陌生,但是在我们的watir脚本中,specifiers是无处不在的。考虑下面的代码:

ie.div(:id => 'my_div')
ie.link(:class => 'qq')

在这2个方法里面specifiers就是 :id => 'my_div'和:class => 'qq'。于是可以推断specifiers应该是Hash对象,像这样div(:id, 'my_div')非Hash形式的参数应该是会被转成Hash的。


def normalize_specifiers!(specifiers)specifiers.each do |how, what|  # 遍历所有的specifiers,于是我们能推断出watir是支持多属性识别的。# 例如`div(:id => 1, :class => 'red')`case how  # how就是:id, :class之类的元素属性,what就是这些属性的属性值when :index # 当用index定位对象时候就将what转成Integer类型what = what.to_iwhen :urlhow = :href # 如果how是url的话,那么将url属性变成href属性# 这就证明了url和href属性的作用是相同的when :class # 如果how是class,就将class变成class_name,这个比较复杂,不解释how = :class_namewhen :caption # 可以看出caption就是value属性how = :valuewhen :method # 可以看出method 实际上是form的method,也就是post或gethow = :form_methodend@specifiers[how] = what # 按照上面的规则重新生成specifiers,并赋值给@specifiersend
enddef match_with_specifiers?(element) # 判断element是否符合定位的条件# 也就是说element的属性是不是跟@specifiers定义的相同# 如果相同,这个element当然是我们需要寻找的了@specifiers.each do |how, what| # 遍历specifiersnext if how == :index # 如果how是index的话则跳过return false unless match? element, how, what #如果match(element, how, what)为false,则返回falseendreturn true # 否则就返回true
end

end在这里要重点说一下match方法,match方法有3个参数,element how what, 该方法一般在Locator的子类中定义,下面会具体讲到。match方法的作用就是如果element能够被how what匹配上则返回true,否则false


由于Locator只是基类,所以更加具体的识别任务就交由其子类完成。

TaggedElementLocator是Locator的子类,其具体作用是根据所需定位元素的tag及users(脚本编写者)提供的specifiers来定位页面元素。

下面介绍一下TaggedElementLocator类的一些方法:

class TaggedElementLocator < Locator
def initialize(container, tag) #初始化时候指定了container和tag,其中tag就是定位的关键@container = container@tag = tag
enddef set_specifier(how, what) #处理了how和what,将how和what变成了Hash,印证了我们上面的猜测if how.class == Hash and what.nil?specifiers = howelsespecifiers = {how => what}end@specifiers = {:index => 1} # 如果没指定how和what,那么默认认为how是index,what是1,就是默认返回第1个元素normalize_specifiers! specifiers #调用了基类的方法,规整了specifiers并赋值给@specifiers
enddef each_element tag # 很关键的1个方法,其作用是根据tag值,并调用js来获取页面上所有的tag是给定值的ole对象,并封装成Watir的Element对象
#在container上调用getElementsByTagName,这是js的dom操作代码,不懂请google@container.document.getElementsByTagName(tag).each do |ole_element| yield Element.new(ole_element)end
enddef locate #比较复杂的方法#作用实际就是找到页面上所有的tag为给定值的watir element,然后遍历这些元素,如果这些元素能够被specifiers匹配则可能返回count = 0each_element(@tag) do |element|next unless match_with_specifiers?(element) # 如果该element不匹配则继续count += 1return element.ole_object if count == @specifiers[:index] #如果有多个元素满足条件,则返回第index个#默认情况下index是1,也就是返回第1个匹配end # elements # 于是我们可以写出如下的代码ie.div(:class => 'red', :index => 3),返回class是red的第3个div元素nil
enddef match?(element, how, what) #核心的匹配方法,用来判断元素是否符合specifiersbeginmethod = element.method(how) # 如果元素定义了element.how方法则获取这个方法的binding#举例来说如果元素是div,how是id,则返回Div#id这个方法的bindingrescue NameError # 否则元素没有定义element.how方法,则抛出下面的错误raise MissingWayOfFindingObjectException,"#{how} is an unknown way of finding a <#{@tag}> element (#{what})"endcase method.arity # 判断element.how的参数个数when 0 # 如果没有参数,则直接调用how方法,将返回的结果跟what值进行比较what.matches method.callwhen 1 # 如果有1个参数则将what值作为这个参数传给how方法method.call(what)elseraise MissingWayOfFindingObjectException, # 其他情况抛出错误"#{how} is an unknown way of finding a <#{@tag}> element (#{what})"end
end # 该方法的返回值是what.matches或者是element.how(what),一般都是booleanend

元素的定位就是这么简单,可能看了上面的代码大家都有些糊涂了。下面我们举例说明一下元素的定位之旅。

假设有下面的方法

ie.div(:class => 'red', :index => 3)
  • 首先watir会生成这样的1个@specifiers变量,其值为{:class_name => 'red', :index => 3}
  • 然后watir会遍历页面上(实际上是container上,这里为了简单起见,简化了一下)所有的tag是div的元素,并将这些ole元素封装成了watir的element
  • 最后在每一个element上调用how方法,在这个例子里就是调用@specifiers中的class_name方法。为什么没有调用index方法?因为index是跳过的,详见matchwith_specifiers?方法
  • 如果element.how等于what(为了理解又简化了,专家见谅),也就是element.class_name = 'red'的话,则将看这个element是不是第index个,如果是则返回element的ole元素,元素定位之旅结束

在这里我们可以看到waitr定位元素一般是通过遍历页面上所有与给定元素拥有相同tag的元素,并比较其属性值的方式进行的。

其比较的方法和原理都很简单。当然,如果任意1个元素都通过遍历tag的方式进行的话,那么watir的效率将是比较低下的。为此watir也提供了快速定位的方法,这个我们以后再慢慢讨论。

watir是如何定位元素的相关推荐

  1. 对比四种爬虫定位元素方法,你更爱哪个?

    作者 | 陈熹 来源 | 早起Python 头图 | 下载于视觉中国 在使用Python本爬虫采集数据时,一个很重要的操作就是如何从请求到的网页中提取数据,而正确定位想要的数据又是第一步操作.本文将对 ...

  2. Selenium如何通过location和size定位元素坐标?

    前面我是用过python写过验证形式的,这次利用Selenium如何通过location和size定位元素 识别极验验证码的时候遇到了关于location和size两个属性,由于之前学习python的 ...

  3. iframe内联元素有白边原因_Selenium无法定位元素的几种解决方案

    01frame/iframe表单嵌套 WebDriver只能在一个页面上对元素识别与定位,对于frame/iframe表单内嵌的页面元素无法直接定位. 解决方法: driver.switch_to.f ...

  4. Selenium之定位元素常用的8种方法整理(第一篇)

    在使用selenium webdriver进行元素定位时,通常使用findElement或findElements方法结合By类返回的元素句柄来定位元素.其中By类的常用定位方式共八种,现分别介绍如下 ...

  5. python android自动化元素定位_linux下Appium+Python移动应用自动化测试实战---3.手把手教你定位元素编写测试用例...

    linux下Appium+Python移动应用自动化测试实战-3.手把手教你定位元素编写测试用例 前言 有很多童鞋环境搭建好了却没有进行下一步,是因为缺少step by step的资料. 互联网上ap ...

  6. 相对定位android,appium相对位置定位元素----父节点/兄弟节点定位

    appium相对位置定位元素----父节点/兄弟节点定位 发布时间:2020-06-30 05:51:41 来源:51CTO 阅读:5620 作者:niedongri 讲一下定位手机app上元素时定位 ...

  7. CSS一个冒号是伪类:用于监控动作、两个冒号是伪元素::用于定位元素

    一个冒号两个冒号:::   一个冒号是伪类,两个冒号是伪元素 伪类有----:first-child ,:link , :vistited,:hover:,active:focus,:lang用于监控 ...

  8. js下拉 selenium_selenium 难定位元素,时间插件,下拉框定位,string

    1.元素定位 ID定位元素: findElement(By.id("")); 通过元素的名称定位元素: findElement(By.name("")); 通过 ...

  9. [Python从零到壹] 九.网络爬虫之Selenium基础技术万字详解(定位元素、常用方法、键盘鼠标操作)

    欢迎大家来到"Python从零到壹",在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界.所有文章都将结合案例.代码和作者的经验讲 ...

最新文章

  1. PCL:点云中的超体素数据
  2. 写cookies注意事项
  3. android使用bintray发布aar到jcenter
  4. PHP实现中文字符串截取无乱码
  5. Kubernetes通过一行shell命令给pod中的zk节点添加权限
  6. 第三章 MongoDb Java应用 3.1
  7. 树莓派4支持多大tf卡_陪你一起玩树莓派-系统安装
  8. win10 多开 vpn
  9. visio画图复制粘贴到word_visio复制粘贴到word中
  10. Mybatis的pooled连接池工作原理
  11. 给我一篇假论文,我能骗倒半个地球
  12. java linux常用命令_Linux常用命令
  13. 一份招聘公告暴露英特尔外包芯片计划
  14. 药物从研发到上市需要经历哪些流程?||新药研发
  15. 二年级计算机课,小学二年级信息技术课程教案三篇
  16. 价值十个亿的淘宝搜索功能
  17. Flash 视频蓬勃发展
  18. 树莓派怎么运行python程序?
  19. 海关数据到底在哪里查?
  20. MATLAB中勒让德多项式程序

热门文章

  1. 计算机英语及教学法,对高职计算机专业英语教学方法的探讨
  2. 周长为定长的所有平面四边形P中,面积最大的为正方形。
  3. Python后端开发(主Django)面试题
  4. 计算机网络:应用层基本概念
  5. 我胡汉三回来了!!!
  6. C++生成简单WAV文件(三)——根据简谱生成菊花台
  7. Java实现 幸运数字
  8. 直播预告| CVPR专场四来了!
  9. 中科创达与高通成立合资公司
  10. 数据库For Web