我正在研究Ruby中的网络抓取问题。我已经看到了与此相关的多个问题和答案,但是没有一个人看到HTML中包含一些JavaScript框架,而且我不知道该怎么做。我只想选择HTML并返回一个对象数组。以下是我的脚本和HTML代码。诸如名称,货币,余额之类的值的HTML类是相似的,该如何解决呢?
content = document.css("div.acc-list").map do |parameters|
name = parameters.at_css("p.s3.bold.row.acDesc").text.strip, # argument?
currency = parameters.at_css(".row.ccy").text.strip, # argument?
balance = parameters.at_css(".row.acyOpeningBal").text.strip # argument?
Account.new name, currency, balance
end
pp content
这些HTML段落位于其他多个类中,我认为这归因于该框架。但是,它们在<div class = acc-list div>...</div>
内,当我将“ div.acc-list”分配给“ content”变量时,我认为我做得正确。
<!-- HTML for name -->
<td bindonce="" ng-repeat="col in gridOptions.columns" sg-bind-html-compile="col.cellTemplate" bo-class="col.className" bo-style="{width: col.remWidth }"
class="ng-scope icon-two-line-col" style="width: 17.3333rem;">
<div style="width: 17.333333333333332rem" class="first-cell cellText ng-scope">
<i bo-class="{'active':row.selected }" class="i-32 active icon i-circle-account"></i>
<div class="info-wrapper" style="">
<p class="s3 bold" bo-bind="row.acDesc">Name_value</p> # value
<a ui-sref="app.layout.ACCOUNTS.DETAILS.{ID}({id:'091601003439274'})" href="/Bank/accounts/details/BG37FINV91503006938102">
<span bo-bind="row.iban">BG37FINV91503006938102</span>
<i class="i-arrow-right-5x8"></i>
</a>
</div>
</div>
</td>
<!-- HTML for currency -->
<td bindonce="" ng-repeat="col in gridOptions.columns" sg-bind-html-compile="col.cellTemplate" bo-class="col.className" bo-style="{width: col.remWidth }"
class="ng-scope" style="width: 4.4rem;">
<div style="width: 4.4rem" class="text-center cellText ng-scope">
<span bo-bind="row.ccy">EUR</span> # value
</div>
</td>
<!-- HTML for balance -->
<td bindonce="" ng-repeat="col in gridOptions.columns" sg-bind-html-compile="col.cellTemplate" bo-class="col.className" bo-style="{width: col.remWidth }"
class="ng-scope" style="width: 8.73333rem;">
<div style="width: 8.733333333333333rem" class="text-right cellText ng-scope">
<span bo-bind="row.acyAvlBal | sgCurrency">1 523.08</span> # value
</div>
</td>
使用CSS:
require 'nokogiri'
document = Nokogiri::HTML(<<EOT)
<div class="acc-list">
<!-- HTML for name -->
<td>
<div class="first-cell cellText ng-scope">
<div class="info-wrapper">
<!-- # value -->
<p class="s3 bold">Name_value</p>
</div>
</div>
</td>
<!-- HTML for currency -->
<td>
<div class="text-center cellText ng-scope">
<!-- # value -->
<span>EUR</span>
</div>
</td>
<!-- HTML for balance -->
<td>
<div class="text-right cellText ng-scope">
<!-- # value -->
<span>1 523.08</span>
</div>
</td>
</div>
EOT
现在已加载DOM:
content = document.css('div.acc-list').map do |div|
name = div.at("p.s3.bold").text.strip # => "Name_value"
currency = div.at("div.text-center > span").text.strip # => "EUR"
balance = div.at("div.text-right > span").text.strip # => "1 523.08"
[ name, currency, balance ]
end
# => [["Name_value", "EUR", "1 523.08"]]
您的HTML示例中有很多无关的信息,这些信息遮盖了该特定森林中的树木。我删除了它,因为它没有用。 (并且,在提交问题时,您应该在简化非必需信息的过程中自动执行此操作,以便我们都可以专注于实际问题。)
CSS不关心节点名称class
和id
以外的参数。如果需要那种细粒度的访问,class
可以在类的定义中链接参数,但是通常可以使用更通用的类选择器;它仅取决于HTML。
大多数XML和HTML解析基本上是相同的策略:找到一个外部占位符,在其中查找并迭代获取所需的信息。我不能完全证明这一点,因为您的示例仅具有外部div,但是您可能可以想像到处理内部循环的必要代码。
[at_css
几乎等同于at
,并且Nokogiri在99.9%的时间内足够聪明地确定选择器是CSS还是XPath,因此我倾向于使用at
,因为我的手指很懒。