为什么 XPath 使用 Ruby、Nokogiri 和 Watir 返回'0'值?



我正在开发一个白帽网络爬虫,它会定期登录我的帐户,并使用Ruby与Watir和Nokogiri为我检查一些信息。

这是我尝试从中提取信息的简化 HTML:

<div class="navbar navbar-default navbar-fixed-top hidden-lg hidden-md" style="z-index: 1002">
    <div class="banner-g">
        <div class="container">
            <div id="user-info">
                    <div id="acct-value">
                        <a href="https://www.testsite.org/Profile/MyShares" title="Change in value of your shares">GAIN/LOSS <span class="SPShares">-$12.85</span></a>
                    </div>
                    <div id="committed">
                        <a href="https://www.testsite.org/Profile/MyShares" title="Amount paid for your shares">INVESTED <span class="SPPortfolio">$152.11</span></a>
                    </div>
                    <div id="avail">
                        <a href="https://www.testsite.org/Profile/MyShares">AVAILABLE <span class="SPBalance">$26.98</span></a>
                    </div>

我试图拉$26.98.摘录的底部。

以下是我正在使用的三个代码片段。除了XPath之外,它们几乎都相同。前两个完美地返回其值,但第三个始终返回值"0",即使它"应该"返回"$26.98"或"26.98"。

 val_one = page_html.xpath(".//*[@id='openone']/div/div[2]/div[1]/div/div[2]/table/tbody/tr[2]/td[1]").text.gsub(/D/,'').to_i
 val_two = page_html.xpath(".//*[@id='opentwo']/div/div[2]/div[2]/div/div[2]/table/tbody/tr[2]/td[1]").text.gsub(/D/,'').to_i
 val_three = page_html.xpath(".//*[@id='avail']/a/span").text.gsub(/D/,'').to_i
 puts val_three
我认为这是 XPath

的问题,但我在这里经历了数十个 XPath 故障排除问题,但没有一个奏效。我用FirePath和"XPath Checker"检查了XPath。我还尝试让 XPath 搜索"SPBalance"类,但结果相同。

当我从末尾删除to.i时,它返回一个空行而不是零。

在站点的其他地方使用 Watir 时,我能够通过调用 .focus 来解决记录值的问题,但对于这段代码,它更像 Nokogiri,使用 .focus 会导致错误消息:

undefined method `focus' for []:Nokogiri::XML::NodeSet (NoMethodError)

我认为.focus对Nokogiri不起作用。

更新:将 HTML 替换为更干净/更完整的版本。

我继续尝试访问该数据单元的不同方法,包括 xpath、css 和搜索方法。有人告诉我 xpath 不适用于此页面,所以我花了更多时间尝试让 css 工作。其他人告诉我这个页面有Javascript,这会阻止Watir工作。所以我尝试重写Selenium的应用程序。硒并没有解决问题,并产生了许多其他问题。

更新:在遵循铁皮人的建议后,我发现使用 curl 下载节点时,该节点实际上在 HTML 中不可见。

我现在尝试使用 Watir 而不是 Nokogiri 访问节点(正如他所建议的那样)。以下是我到目前为止尝试过的一些方法:

avail_funds = browser.span :class => 'SPBalance'
avail_funds.exists?
avail_funds.text
avail_funds = browser.span(:css, 'span[customattribute]').text
avail_funds = browser.div(:id => "avail").a(:href => "/Profile/MyShares").span(:class => "SPBalance").text
avail_funds = browser.span(:xpath, ".//*[@id='avail']/a/span").text
avail_funds = browser.span(:css, 'span[class="SPBalance"]').text
avail_funds = browser.span.text
avail_funds = browser.div.text
browser.span(:class, "SPBalance").focus
avail_funds = browser.span(:class, "SPBalance").text 
avail_funds = @browser.span(:class => 'SPBalance').inner_html
puts @browser.spans(:class => "SPBalance")
puts @browser.span(:class => "SPBalance")
texts = @browser.spans(:class => "SPBalance").map do |span|
  span.text
end

到目前为止,上述所有内容都返回空行或错误消息。

ID 为 "user-info" 的div 类在通过 curl 下载的 HTML 中可见。然而,这下面的一切都是看不见的。

当我尝试时:

avail_funds = browser.div(:id => "user-info").text

我只得到空白行。

当我尝试时:

avail_funds = browser.div(:class => "navbar navbar-default navbar-fixed-top hidden-xs hidden-sm").text

我得到实际的文本!但不幸的是,字符串不包含我想要的值。

我也试过:

puts browser.html

因为我认为如果该版本的 HTML 中可见的值,就像通过我的 Firefox 插件一样,我可以解析为我想要的值。但不幸的是,该值在该版本的 HTML 中不可见。

通过前 2 个命令,您可以直接从文档根目录开始从表格单元格获取数据,在最后一个命令中,您可以从中心开始。

尝试给出span id并再次获取数据,然后增加复杂性,您将在xpath中找到错误

第一个问题是你试图使用一个长而太长的选择器来引用不存在的标签:

require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
<head>
<body class="cbp-spmenu-push">
<div id="FreshWidget" class="freshwidget-container responsive" data-html2canvas-ignore="true" style="display: none;">
<div id="freshwidget-button" class="freshwidget-button fd-btn-right" data-html2canvas-ignore="true" style="display: none; top: 235px;">
<link rel="stylesheet" href="/Content/css/NavPushComponent.css"/>
<script src="/Scripts/classie.js"/>
<script src="/Scripts/modernizr.custom.js"/>
<div class="navbar navbar-default navbar-fixed-top hidden-lg hidden-md" style="z-index: 1002">
<div class="banner-g">
<div class="container">
<div id="user-info">
<div id="acct-value">
<div id="committed">
<div id="avail">
<a href="/Profile/MyBalance">
AVAILABLE 
<span class="SPBalance">$31.59</span>
EOT
doc.at('tbody') # => nil
".//*[@id='openone']/div/div[2]/div[1]/div/div[2]/table/tbody/tr[2]/td[1]"
".//*[@id='opentwo']/div/div[2]/div[2]/div/div[2]/table/tbody/tr[2]/td[1]"

您的示例中没有<tbody>标记,在野外创建的 HTML 中也很少有标记,尤其是在人们手动创建的情况下。我们通常会在HTML中看到有人从浏览器的"查看源代码"显示中抓取<tbody>,这是他们的引擎破坏HTML以使其可读后的结果输出。不要使用该输出。相反,始终直接转到源代码并使用wgetcurl下载页面并使用编辑器检查它,甚至在命令行上使用nokogiri some_url并在那里查看它。

第二个问题是您的 HTML 代码段无效,因为它充满了未终止的标记。Nokogiri 会对错误的 HTML 进行修复,这实际上会移动节点,使得很难找到节点,尤其是在调试时。在这种特殊情况下,Nokogiri 能够终止它们,但遵守标签闭包很重要。

这是我会使用的:

value = doc.at('span.SPBalance').text # => "$31.59"

这是使用CSS,它通常比XPath更具可读性。 at的意思是"找到第一个出现",相当于search('span.SPBalance').first

XPath 等效项为:

doc.at('//span[@class="SPBalance"]')
doc.at('//span[@class="SPBalance"]').text # => "$31.59"

一旦我有了价值,就很容易操纵它。

value[/[d.]+/].to_f # => 31.59

继续前进...

第三个始终返回值"0",即使它应该返回"$31.59"或"31.59"

'$31.58'.to_i # => 0
'$'.to_i # => 0
'31.58'.to_i # => 31
'$31.58'.to_f # => 0.0
'31.58'.to_f # => 31.58

to_fto_i的文档分别说:

返回将 str 中的前导字符解释为浮点数的结果。

返回将 str 中的前导字符解释为整数基数(介于 2 和 36 之间)的结果。

在这两种情况下,"主角"都很重要。


使用 .focus 会导致错误消息:

   undefined method `focus' for []:Nokogiri::XML::NodeSet (NoMethodError)

我认为 .focus 不适用于 Nokogiri。

您可以随时查看 NodeSet 文档,该文档确认focus不是一种方法。

相关内容

  • 没有找到相关文章

最新更新