Ruby 机械化:按标签文本匹配字段

Ruby 机械化:按标签文本匹配字段,ruby,label,mechanize,Ruby,Label,Mechanize,在抓取表单时,我更喜欢按标签查找字段,因为我处理的页面的大多数ID和名称都是自动生成的,我不能相信它们不会更改,而且标签比名称更具描述性 而不是在我的脚本中一直这样做: Mechanize::Page.elements_with 'label' #... some_form.field_with( some_form.page.label_with(:text => "Address").node['for'] ).value = "..." some_form.field_with

在抓取表单时,我更喜欢按标签查找字段,因为我处理的页面的大多数ID和名称都是自动生成的,我不能相信它们不会更改,而且标签比名称更具描述性

而不是在我的脚本中一直这样做:

Mechanize::Page.elements_with 'label'
#...
some_form.field_with(
  some_form.page.label_with(:text => "Address").node['for']
).value = "..."

some_form.field_with(
  some_form.page.label_with(:text => "Zipcode").node['for']
).value = "..."
class Mechanize::Form::Field
  def label_text
    # hack to get the document root
    root = node.ancestors.last
    # look up the label for this field
    label = root.at("label[for=#{dom_id.inspect}]") if dom_id
    label && label.text
  end
 end
我已经开始在我的脚本顶部添加monkeypatch:

Mechanize::Page.elements_with 'label'
#...
some_form.field_with(
  some_form.page.label_with(:text => "Address").node['for']
).value = "..."

some_form.field_with(
  some_form.page.label_with(:text => "Zipcode").node['for']
).value = "..."
class Mechanize::Form::Field
  def label_text
    # hack to get the document root
    root = node.ancestors.last
    # look up the label for this field
    label = root.at("label[for=#{dom_id.inspect}]") if dom_id
    label && label.text
  end
 end
所以我可以这样做:

some_form.field_with( :label_text => "Address" ).value = "..."
some_form.field_with( :label_text => "Zipcode" ).value = "..."

这是一个黑客,但它现在起作用。有没有一个更优雅的解决方案我可以使用?

我找到了一个更好的解决方案,不涉及猴子修补。由于具有条件的
{element}\u是使用
==
匹配的,因此我可以向它传递一个
lambda

# convenince methods to define matcher lambdas

def has_title expected
  lambda { |node| expected === node['title']
end

def has_label expected
  lambda do |node|
    # hack to get the document root
    root = node.ancestors.last
    dom_id = node['id']
    # look up the label for this field
    label = root.at("label[for=#{dom_id.inspect}]") if dom_id
    # check if it matches
    expected === (label && label.text)
  end
end

some_form.field_with( :node => has_label("Address") ).value = "..."
some_form.field_with( :node => has_label("Zipcode") ).value = "..."
some_form.field_with( :node => has_title("Description") ).value = "..."
...

你可以这样做:

def get_key page, str
  id = page.at("label[text()*='#{str}']")[:for]
  key = page.at("##{id}")[:name]
end
然后

form[get_key(page, 'Address')] = value

那有点干净,但还是一团糟。我这样做需要有一个很好的理由,如果在我继承的代码中发现它,我会很恼火。

所以你相信标签不会改变,但不会形成字段名?在我看来,你应该重新审视这一点。我相信标签更改的频率会降低。更改标签文本可能是一个很好的理由,而且不会破坏任何东西。你认为这样的可能性比没有理由的事情要小,而且会破坏事情吗?我认为我不想依赖
id
s,比如
ctl00\u m\u g\u 8cecb01f\u 9b31\u 4ee6\u 952c\u eea 26359d2d\u ctl00\u ctl02\u ctl00\u ctl00\u ctl00\u ctl00\u ctl04\u ctl00\u ctl0\u TextField
(毫不夸张)。我猜,如果他们改变任何两个字段的表示顺序,情况就会改变。我知道我宁愿通过标签文本“Description”来查找它,也不必为我正在处理的每个字段复制/粘贴它。当然,如果标签更改,脚本将中断,但是标签对于代码维护来说更具描述性。您应该发布表单的html。也许有人能想出一个更好的主意。在这段代码中,你如何定义
dom\u id
的局部变量?jonkratz:好主意。看起来这是我的monkeypatch的遗留问题,它指的是
Mechanize::Form::Field#dom_id
。现在修好。