Ruby 机械化:按标签文本匹配字段
在抓取表单时,我更喜欢按标签查找字段,因为我处理的页面的大多数ID和名称都是自动生成的,我不能相信它们不会更改,而且标签比名称更具描述性 而不是在我的脚本中一直这样做: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
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
。现在修好。