我正在尝试从此HTML中删除并制作CSV文件:
<ul class="object-props">
<li class="object-props-item price">
<strong>CHF 14'800.-</strong>
</li>
<li class="object-props-item milage">31'000 km</li>
<li class="object-props-item date">08.2012</li>
</ul>
我想用以下方法提取价格和里程:
require 'rubygems'
require 'nokogiri'
require 'CSV'
require 'open-uri'
url= "/tto.htm"
data = Nokogiri::HTML(open(url))
CSV.open('csv.csv', 'wb') do |csv|
csv << %w[ price mileage ]
price=data.css('.price').text
mileage=data.css('.mileage').text
csv << [price, mileage]
end
结果并不是我所期待的。创建了两列,但是如何删除CHF和KM等字符以及为什么里程数不显示result?
答案 0 :(得分:0)
我的猜测是HTML中的文字包含度量单位; CHF
以瑞士法郎为代价,km
以里程为公里。
您可以添加split.first
或split.last
来获取没有度量单位的数字,例如:
2.3.0 :007 > 'CHF 100'.split.last
=> "100"
2.3.0 :008 > '99 km'.split.first
=> "99"
答案 1 :(得分:0)
删除/忽略不需要的文本不是Nokogiri问题,它是字符串处理问题:
require 'nokogiri'
doc = Nokogiri::HTML(<<EOT)
li class="object-props-item price"
<strong>CHF 14'900.-</strong>
<li class="object-props-item milage">61'000 km</li>
EOT
str = doc.at('strong').text # => "CHF 14'900.-"
此时str
包含<strong>
节点的文本。
一个简单的正则表达式将被提取,这是获取数据的直接方式:
str[/[\d']+/] # => "14'900"
sub
可用于删除'CHF '
子字符串:
str.sub('CHF ', '') # => "14'900.-"
delete
可用于删除字符C
,H
,F
和:
str.delete('CHF ') # => "14'900.-"
tr
可用于删除非0
.. 9
,'
,.
或-
的所有内容:
str.tr("^0-9'.-", '') # => "14'900.-"
如果您不想要'
,.
或-
,请修改上述其中一项。
为什么里程数据不显示
因为CSS选择器与实际的class
参数不匹配:
require 'nokogiri'
doc = Nokogiri::HTML('<li class="object-props-item milage">61'000 km</li>')
doc.at('.mileage').text # =>
# ~> NoMethodError
# ~> undefined method `text' for nil:NilClass
# ~>
# ~> /var/folders/yb/whn8dwns6rl92jswry5cz87dsgk2n1/T/seeing_is_believing_temp_dir20160428-96035-1dajnql/program.rb:5:in `<main>'
相反它应该是:
doc.css('.milage').text # => "61'000 km"
但这并非全是错误的。有一个微妙的问题等着你以后咬你。
css
或search
返回一个NodeSet,而at
或at_css
返回一个元素:
doc.css('.milage').class # => Nokogiri::XML::NodeSet
doc.at('.milage').class # => Nokogiri::XML::Element
以下是text
传递包含多个匹配节点的NodeSet时会发生什么:
doc = Nokogiri::HTML('<p>foo</p><p>bar</p>')
doc.search('p').class # => Nokogiri::XML::NodeSet
doc.search('p').text # => "foobar"
doc.at('p').class # => Nokogiri::XML::Element
doc.at('p').text # => "foo"
当text
与NodeSet一起使用时,它会返回连接成单个字符串的所有节点的文本。这可能使文本从一个节点与另一个节点分离变得非常困难。相反,使用at
或其中一个at_*
等效项从单个节点获取文本。如果要单独从每个节点中提取文本并使用数组:
doc.search('p').map(&:text) # => ["foo", "bar"]
参见&#34; How to avoid joining all text from Nodes when scraping&#34;还
最后,请注意您的HTML示例无效:
doc = Nokogiri::HTML(<<EOT)
li class="object-props-item price"
<strong>CHF 14'900.-</strong>
<li class="object-props-item milage">61'000 km</li>')
EOT
puts doc.to_html
# >> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
# >> <html><body>
# >> <p>li class="object-props-item price"
# >> <strong>CHF 14'900.-</strong>
# >> </p>
# >> <li class="object-props-item milage">61'000 km</li>')
# >> </body></html>
以下是发生的事情:
doc = Nokogiri::HTML(<<EOT)
li class="object-props-item price"
<strong>CHF 14'900.-</strong>
<li class="object-props-item milage">61'000 km</li>')
EOT
doc.at('.price') # => nil
Nokogiri必须做一个修复才能理解第一行,所以它将它包含在<p>
中。通过这样做,.price
类不再存在,因此您的代码将再次失败。
修复标记会产生正确的响应:
doc = Nokogiri::HTML(<<EOT)
<li class="object-props-item price">
<strong>CHF 14'900.-</strong>
</li>
<li class="object-props-item milage">61'000 km</li>')
EOT
doc.at('.price').to_html # => "<li class=\"object-props-item price\">\n<strong>CHF 14'900.-</strong>\n</li>"
这就是为什么确保您的输入有效非常重要。如果没有它,试图复制你的问题是很困难的。