在capybara xpath选择器上调用.uniq并在逻辑上绕过Capybara :: ElementNotFound

时间:2013-12-04 17:12:15

标签: xpath selenium-webdriver capybara capybara-webkit

我正在使用capybara / dsl,selienium webdriver和电子表格gem开发支持javascript的屏幕抓取工具。非常接近所需的输出,但是出现了两个主要问题:

  1. 我无法找出确切的xpath选择器来过滤掉我正在寻找的元素;为了确保没有丢失,我使用的是一个广泛的选择器,我知道它会产生重复的元素。我打算只在那个选择器上调用.uniq,但这会引发错误。执行此操作的正确方法是什么导致所需的过滤。错误是'uniq'的未定义的无法。也许我没有正确使用它:results = all("//a[contains(@onclick, 'analyticsLog')]").uniq。我知道我选择提取hrefs://a[contains(@onclick, 'analyticsLog')]的xpath将定义比我预期更多的节点,因为使用find来检查页面元素显示144而不是72构成页面结果。我找了一个更具体的选择器,但是由于网站上使用的业务逻辑,我没有找到一个没有过滤掉一些所需链接的选择器。

  2. 我的save_item方法有两个选择器,它们并不总是在信息结果中找到,我希望脚本只是跳过那些找不到的那些,只保存那些但是我当前的迭代会抛出一个Capybara :: ElementNotFound并退出。我怎样才能将其配置为以预期的方式工作。

  3. 下面的代码

    require "capybara/dsl"
    require "spreadsheet"
    
     Capybara.run_server = false
     Capybara.default_driver = :selenium
     Capybara.default_selector = :xpath
     Spreadsheet.client_encoding = 'UTF-8'
    
     class Tomtop
       include Capybara::DSL
    
       def initialize
         @excel = Spreadsheet::Workbook.new
         @work_list = @excel.create_worksheet
         @row = 0
       end
    
       def go
         visit_main_link
       end
    
       def visit_main_link
         visit "http://www.some.com/clothing-accessories?dir=asc&limit=72&order=position"
         results = all("//a[contains(@onclick, 'analyticsLog')]")# I would like to use .uniq here to filter out the duplicates that I know will be delivered by this selector
         item = []
    
         results.each do |a|
           item << a[:href]
         end
         item.each do |link|
              visit link
              save_item
          end
         @excel.write "inventory.csv"
    
       end
    
       def save_item
    
         data = all("//*[@id='content-wrapper']/div[2]/div/div")
         data.each do |info|
           @work_list[@row, 0] = info.find("//*[@id='productright']/div/div[1]/h1").text
           @work_list[@row, 1] = info.find("//div[contains(@class, 'price font left')]").text
           @work_list[@row, 2] = info.find("//*[@id='productright']/div/div[11]").text
           @work_list[@row, 3] = info.find("//*[@id='tabcontent1']/div/div").text.strip
           @work_list[@row, 4] = info.find("//select[contains(@name, 'options[747]')]//*[@price='0']").text #I'm aware that this will not always be found depending on the item in question but how do I ensure that it doesn't crash the program
           @work_list[@row, 5] = info.find("//select[contains(@name, 'options[748]')]//*[@price='0']").text #I'm aware that this will not always be found depending on the item in question but how do I ensure that it doesn't crash the program
           @row = @row + 1
         end
    
       end
    
     end
    
    
     tomtop = Tomtop.new
     tomtop.go
    

1 个答案:

答案 0 :(得分:1)

问题1:获取独特元素

all返回的所有元素都是唯一的。因此,我假设“唯一”元素,你的意思是“onclick”属性是唯一的。

Capybara返回的元素集合是可枚举的。因此,您可以将其转换为数组,然后根据其onclick属性获取唯一元素:

results = all("//a[contains(@onclick, 'analyticsLog')]")
            .to_a.uniq{ |e| e[:onclick] }

请注意,重复链接看起来像是图像的一个,而图像下面的文本是一个。您可以将搜索范围限定为一个或另一个,然后您不需要执行uniq检查。要仅限于文本链接,请使用链接是h5的子项的事实:

results = all("//h5/a[contains(@onclick, 'analyticsLog')]")

问题2:如果元素存在则捕获文本

要解决第二个问题,可以使用first找到元素。如果存在匹配元素,则返回匹配元素,如果不存在,则返回nil。然后,如果找到该元素,则可以保存文本。

例如:

el = info.first("//select[contains(@name, 'options[747]')]//*[@price='0']")
@work_list[@row, 4] = el.text if el

如果您需要所有匹配元素的文本,请使用all

options = info.all(".//select[contains(@name, 'options[747]')]//*[@price='0']")
@work_list[@row, 4] = options.collect(&:text).join(', ')

当有多个匹配选项时,您将获得类似“绿色,粉红色”的内容。如果没有匹配选项,您将获得“”。