2016-04-28 2 views
1

Я пытаюсь скрести и сделать файл CSV из этого HTML:Как экспортировать данные HTML в файл CSV

<ul class="object-props"> 
       <li class="object-props-item price"> 
        <strong>CHF 14&#39;800.-</strong> 
       </li> 
       <li class="object-props-item milage">31&#39;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

Я не думаю, что это причина вашей проблемы, но вы открываете файл для записи в двоичном режиме ('wb'). CSV - это текстовое представление, поэтому я уверен, что вы должны открывать его в текстовом режиме ('w'). –

+0

Когда вы спрашиваете о проблеме с вашим кодом, мы должны увидеть вашу попытку решить проблему. В вашем коде вы не показываете, где вы пытаетесь удалить данные CHF и KM; Пожалуйста, добавьте это. Без этого похоже, что вы спрашиваете нас, как написать свой код, для чего это не так. Кроме того, ваш «результат» не должен быть ссылкой на изображение. Вместо этого представите эту информацию в самом вопросе. «[mcve]» описывает то, что нам нужно. Чтобы удалить эту информацию, используйте «delete» или «sub» в извлеченном тексте или лучше, используйте 'tr', чтобы удалить то, что вы не хотите, или написать регулярное выражение, чтобы извлечь только то, что вы хотите. –

+0

Кроме того, ваш входной код HTML недопустим. См. Мой ответ для объяснения, почему это важно. –

ответ

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" 
+0

ОК, думаю, вы за tipp, но когда я применяю, я думаю, что я что-то пропустил, потому что: 'price.split.last' возвращает мне только первое значение в моем .csv. Благодарю за быстрый ответ, я не ожидал такого реакционная способность –

+0

Пожалуйста, покажите, что вы сделали и каков был результат. –

+0

'price = data.css ('. Price'). Text' и' price = price.split.last', чтобы применить ваше предложение. –

0

Удаление/игнорируя ненужный текст не является проблемой Nokogiri, это проблема обработки строк:

require 'nokogiri' 

doc = Nokogiri::HTML(<<EOT) 
li class="object-props-item price" 
<strong>CHF 14&#39;900.-</strong> 
<li class="object-props-item milage">61&#39;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&#39;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 возвращает набор узлов, тогда как 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"] 

См «How to avoid joining all text from Nodes when scraping» также.

Наконец, обратите внимание, что ваш HTML образец не является действительным:

doc = Nokogiri::HTML(<<EOT) 
li class="object-props-item price" 
<strong>CHF 14&#39;900.-</strong> 
<li class="object-props-item milage">61&#39;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&#39;900.-</strong> 
<li class="object-props-item milage">61&#39;000 km</li>') 
EOT 

doc.at('.price') # => nil 

Nokogiri должен сделать исправления в систему, чтобы иметь смысл первой строки, поэтому он обертывает его в <p>. Таким образом, класс .price больше не существует, поэтому ваш код снова сработает.

Фиксирование результатов тегов в правильном ответе:

doc = Nokogiri::HTML(<<EOT) 
<li class="object-props-item price"> 
<strong>CHF 14&#39;900.-</strong> 
</li> 
<li class="object-props-item milage">61&#39;000 km</li>') 
EOT 
doc.at('.price').to_html # => "<li class=\"object-props-item price\">\n<strong>CHF 14'900.-</strong>\n</li>" 

Вот почему очень важно, чтобы убедиться, что ваш вход правилен. Без него сложно дублировать свою проблему.

+0

Я изменил свой вход, но я не могу поместить весь код здесь, потому что слишком большой, думает о вашей помощи –

+0

Мы не хотим весь ваш код. «[mcve]» говорит, что вы должны использовать минимальный код и ввод, необходимый для демонстрации проблемы. Ссылки внизу «[ask]» объясняют аргументацию этих требований, которые в основном обучают отладке до того, как задавать здесь. Часто после этого люди находят решение проблемы без необходимости спрашивать. –

Смежные вопросы